mirror of
				https://github.com/s00500/ESPUI.git
				synced 2025-11-04 04:13:23 +00:00 
			
		
		
		
	
							
								
								
									
										323
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										323
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# ESPUI
 | 
			
		||||
# ESPUI (v2.X)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
@@ -14,71 +14,90 @@ So if you either don't know how or just don't want to waste time: this is your
 | 
			
		||||
simple solution user interface without the need of internet connectivity or any
 | 
			
		||||
additional servers.
 | 
			
		||||
 | 
			
		||||
I completely rewrote the EasyUI Library created by ayushsharma82
 | 
			
		||||
[Here](https://github.com/ayushsharma82/) Now it uses ESPAsyncWebserver and is
 | 
			
		||||
mainly to be used with the ESP32 Processor.
 | 
			
		||||
The Library runs fine on any kind of **ESP8266** and **ESP32** (NodeMCU Boards, usw)
 | 
			
		||||
 | 
			
		||||
# Important notes
 | 
			
		||||
## Changelog for 2.0:
 | 
			
		||||
 | 
			
		||||
Currently ESPUI only supports ArduinoJSON 5.x, please keep that in mind! Version
 | 
			
		||||
6 support is work in progress
 | 
			
		||||
- ArduinoJSON 6.10.0 Support
 | 
			
		||||
- split pad into pad and padWithCenter
 | 
			
		||||
- Cleaned Order or parameters on switch
 | 
			
		||||
- cleaned Order of parameters on pad
 | 
			
		||||
- Changes all numbers to actually be numbers (slider value, number value, min and max)
 | 
			
		||||
 | 
			
		||||
### Added features
 | 
			
		||||
 | 
			
		||||
- Tabs by @eringerli #45
 | 
			
		||||
- Generic API by @eringerli
 | 
			
		||||
- Min Max on slider by @eringerli
 | 
			
		||||
- OptionList by @eringerli
 | 
			
		||||
- Public Access to ESPAsyncServer
 | 
			
		||||
- Graph Widget (Persist save graph in local storage #10)
 | 
			
		||||
 | 
			
		||||
## Further Roadmap
 | 
			
		||||
 | 
			
		||||
- Slider css issues
 | 
			
		||||
- implement Gauge
 | 
			
		||||
- File upload ?
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
 | 
			
		||||
This library is dependent on the following libraries to function properly.
 | 
			
		||||
 | 
			
		||||
- [ESPAsyncWebserver](https://github.com/me-no-dev/ESPAsyncWebServer)
 | 
			
		||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson) **(VERSIONS 5.x only
 | 
			
		||||
  currently)**
 | 
			
		||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson) (Last tested with
 | 
			
		||||
  version 6.10.0)
 | 
			
		||||
 | 
			
		||||
**Plus for ESP8266**
 | 
			
		||||
 | 
			
		||||
- [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP)
 | 
			
		||||
 | 
			
		||||
**Additionally necessary for ESP32**
 | 
			
		||||
 | 
			
		||||
- [AsyncTCP](https://github.com/me-no-dev/AsyncTCP)
 | 
			
		||||
- (_For ESP8266_) [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP)
 | 
			
		||||
- (_For ESP32_) [AsyncTCP](https://github.com/me-no-dev/AsyncTCP)
 | 
			
		||||
 | 
			
		||||
## How to Install
 | 
			
		||||
 | 
			
		||||
Make sure all the dependencies are installed, then install like so:
 | 
			
		||||
 | 
			
		||||
#### Using PlattformIO (_recommended_)
 | 
			
		||||
 | 
			
		||||
Just include this library as a dependency on lib_deps like so:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
lib_deps =
 | 
			
		||||
    ESPUI
 | 
			
		||||
    ESPAsyncWebserver
 | 
			
		||||
    ESPAsyncTCP # or AsyncTCP on ESP32
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Directly Through Arduino IDE (_recommended_)
 | 
			
		||||
 | 
			
		||||
You can find this Library in the Arduino IDE library manager Go to Sketch >
 | 
			
		||||
Include Library > Library Manager > Search for "ESPUI" > Install
 | 
			
		||||
 | 
			
		||||
#### Manual Install
 | 
			
		||||
#### Manual Install Arduino IDE
 | 
			
		||||
 | 
			
		||||
For Windows: Download the
 | 
			
		||||
_For Windows:_ Download the
 | 
			
		||||
[Repository](https://github.com/s00500/ESPUI/archive/master.zip) and extract the
 | 
			
		||||
.zip in Documents>Arduino>Libraries>{Place "ESPUI" folder Here}
 | 
			
		||||
 | 
			
		||||
For Linux: Download the
 | 
			
		||||
_For Linux:_ Download the
 | 
			
		||||
[Repository](https://github.com/s00500/ESPUI/archive/master.zip) and extract the
 | 
			
		||||
.zip in Sketchbook/Libraries/{Place "ESPUI" folder Here}
 | 
			
		||||
 | 
			
		||||
For macOs: Download the
 | 
			
		||||
_For macOs:_ Download the
 | 
			
		||||
[Repository](https://github.com/s00500/ESPUI/archive/master.zip) and extract the
 | 
			
		||||
.zip in ~/Documents/Arduino/libraries/{Place "ESPUI" folder Here}
 | 
			
		||||
 | 
			
		||||
#### Manually through IDE
 | 
			
		||||
 | 
			
		||||
Download the [Repository](https://github.com/s00500/ESPUI/archive/master.zip),
 | 
			
		||||
Go to Sketch>Include Library>Add .zip Library> Select the Downloaded .zip File.
 | 
			
		||||
 | 
			
		||||
## Getting started
 | 
			
		||||
 | 
			
		||||
ESPUI serves several files to the browser to build up its webinterface. This can
 | 
			
		||||
be achieved in 2 ways: _PROGMEM_ or _SPIFFS_
 | 
			
		||||
ESPUI serves several files to the browser to build up its web interface. This
 | 
			
		||||
can be achieved in 2 ways: _PROGMEM_ or _SPIFFS_
 | 
			
		||||
 | 
			
		||||
_When `ESPUI.begin()` is called the default is serving files from Memory and
 | 
			
		||||
ESPUI should work out of the box!_
 | 
			
		||||
 | 
			
		||||
But if this causes your program to _use too much memory_ you can burn the files
 | 
			
		||||
into the SPIFFS filesystem on the ESP. There are now two ways to do this: you
 | 
			
		||||
can either use the ESP file upload tool or you use the library function
 | 
			
		||||
`ESPUI.prepareFileSystem()`
 | 
			
		||||
**OPTIONAL:** But if this causes your program to _use too much memory_ you can
 | 
			
		||||
burn the files into the SPIFFS filesystem on the ESP. There are now two ways to
 | 
			
		||||
do this: you can either use the ESP file upload tool or you use the library
 | 
			
		||||
function `ESPUI.prepareFileSystem()`
 | 
			
		||||
 | 
			
		||||
#### Simple filesystem preparation (_recommended_)
 | 
			
		||||
 | 
			
		||||
@@ -88,58 +107,37 @@ will create all needed files. Congratulations, you are done, from now on you
 | 
			
		||||
just need to to this again when there is a library update, or when you want to
 | 
			
		||||
use another chip :-) Now you can upload your normal sketch, when you do not call
 | 
			
		||||
the `ESPUI.prepareFileSystem()` function the compiler will strip out all the
 | 
			
		||||
unnecessary that is already saved in the chip's filesystem and you have more
 | 
			
		||||
programm memory to work with.
 | 
			
		||||
 | 
			
		||||
#### Manual way (mainly for development)
 | 
			
		||||
 | 
			
		||||
To do this download and install me-no-devs wonderful
 | 
			
		||||
[ESP32 sketch data uploader](https://github.com/me-no-dev/arduino-esp32fs-plugin)
 | 
			
		||||
or for ESP8266
 | 
			
		||||
[ESP8266 sketch data uploader](https://github.com/esp8266/arduino-esp8266fs-plugin)
 | 
			
		||||
 | 
			
		||||
Then open the **gui** example sketch and select "Upload Sketch Data" from the
 | 
			
		||||
Tools menu for your processor. Now you are set to go and use any code you want
 | 
			
		||||
to with this library
 | 
			
		||||
unnecessary strings that are already saved in the chip's filesystem and you have
 | 
			
		||||
more program memory to work with.
 | 
			
		||||
 | 
			
		||||
## User interface Elements
 | 
			
		||||
 | 
			
		||||
- Label (updateable)
 | 
			
		||||
- Label
 | 
			
		||||
- Button
 | 
			
		||||
- Switch (updateable)
 | 
			
		||||
- Switch
 | 
			
		||||
- Control pad
 | 
			
		||||
- Control pad with center button
 | 
			
		||||
- Slider
 | 
			
		||||
- Text Input (updateable)
 | 
			
		||||
- Numberinput (updateable)
 | 
			
		||||
- Text Input
 | 
			
		||||
- Numberinput
 | 
			
		||||
- Graph
 | 
			
		||||
- Option select
 | 
			
		||||
 | 
			
		||||
Checkout the example for the usage
 | 
			
		||||
Checkout the example for the usage or see the detailed info below
 | 
			
		||||
 | 
			
		||||
## Available colors:
 | 
			
		||||
 | 
			
		||||
- COLOR_TURQUOISE
 | 
			
		||||
- COLOR_EMERALD
 | 
			
		||||
- COLOR_PETERRIVER
 | 
			
		||||
- COLOR_WETASPHALT
 | 
			
		||||
- COLOR_SUNFLOWER
 | 
			
		||||
- COLOR_CARROT
 | 
			
		||||
- COLOR_ALIZARIN
 | 
			
		||||
- COLOR_NONE
 | 
			
		||||
- Turquoise
 | 
			
		||||
- Emerald
 | 
			
		||||
- Peterriver
 | 
			
		||||
- Wetasphalt
 | 
			
		||||
- Sunflower
 | 
			
		||||
- Carrot
 | 
			
		||||
- Alizarin
 | 
			
		||||
- Dark
 | 
			
		||||
- None
 | 
			
		||||
 | 
			
		||||
## Roadmap :
 | 
			
		||||
 | 
			
		||||
- ~~Setup SPIFFS using values in program memory~~
 | 
			
		||||
- ~~ESP8266 support~~
 | 
			
		||||
- ~~PlattformIO Integration~~
 | 
			
		||||
- ~~Multiline Labels~~
 | 
			
		||||
- ~~GZip Files and serve from memory~~
 | 
			
		||||
- Datagraph output -> _WIP_
 | 
			
		||||
- ~~Number input ~~
 | 
			
		||||
- ~~Text input ~~
 | 
			
		||||
- Dokumentation for Text and number widget
 | 
			
		||||
- Number min and max value
 | 
			
		||||
- proper return value (as int and not as string) for slider
 | 
			
		||||
- Maybe a slider range setting, meanwhile please use _map()_
 | 
			
		||||
(Use like `ControlColor::Sunflower`)
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
@@ -147,17 +145,15 @@ The heart of ESPUI is
 | 
			
		||||
[ESPAsyncWebserver](https://github.com/me-no-dev/ESPAsyncWebServer). ESPUI's
 | 
			
		||||
frontend is based on [Skeleton CSS](http://getskeleton.com/) and jQuery-like
 | 
			
		||||
lightweight [zepto.js](https://zeptojs.com/) for Handling Click Events Etc. The
 | 
			
		||||
communication between the _ESP32_ and the client browser works using web
 | 
			
		||||
communication between the _ESP_ and the client browser works using web
 | 
			
		||||
sockets. ESPUI does not need network access and can be used in standalone access
 | 
			
		||||
point mode. All assets are loaded from the internal SPIFFS filesystem of the
 | 
			
		||||
ESP32.
 | 
			
		||||
point mode, all resources are loaded directly from the ESPs memory.
 | 
			
		||||
 | 
			
		||||
This section will explain in detail how the Library is to be used from the
 | 
			
		||||
Arduino code side. As of now the Facilino blocks are not implemented. In the
 | 
			
		||||
arduino setup() routine the interface can be customised by adding UI Elements.
 | 
			
		||||
Arduino code side. In the arduino `setup()` routine the interface can be customised by adding UI Elements.
 | 
			
		||||
This is done by calling the corresponding library methods on the Library object
 | 
			
		||||
ESPUI. Eg: `ESPUI.button(“button”, &myCallback);` creates a button in the
 | 
			
		||||
interface that calls the “myCallback” function when changed. All buttons and
 | 
			
		||||
`ESPUI`. Eg: `ESPUI.button("button", &myCallback);` creates a button in the
 | 
			
		||||
interface that calls the `myCallback(Control *sender, int value)` function when changed. All buttons and
 | 
			
		||||
items call their callback whenever there is a state change from them. This means
 | 
			
		||||
the button will call the callback when it is pressed and also again when it is
 | 
			
		||||
released. To separate different events an integer number with the event name is
 | 
			
		||||
@@ -169,8 +165,11 @@ of the UI library:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Buttons have a name and a callback value. They have one event for press and one
 | 
			
		||||
for release.
 | 
			
		||||
Buttons have a name and a callback value. They have one event for press (`B_DOWN`) and one
 | 
			
		||||
for release (`B_UP`).
 | 
			
		||||
 | 
			
		||||
- B_DOWN
 | 
			
		||||
- B_UP
 | 
			
		||||
 | 
			
		||||
#### Switch
 | 
			
		||||
 | 
			
		||||
@@ -178,8 +177,11 @@ for release.
 | 
			
		||||
 | 
			
		||||
Switches sync their state on all connected devices. This means when you change
 | 
			
		||||
their value they change visibly on all tablets or computers that currently
 | 
			
		||||
display the interface. They also have two types of events: one for turning on
 | 
			
		||||
and one for turning off.
 | 
			
		||||
display the interface. They also have two types of events: one when turning on (`S_ACTIVE`)
 | 
			
		||||
and one when turning off (`S_INACTIVE`).
 | 
			
		||||
 | 
			
		||||
- S_ACTIVE
 | 
			
		||||
- S_INACTIVE
 | 
			
		||||
 | 
			
		||||
#### Buttonpad
 | 
			
		||||
 | 
			
		||||
@@ -190,13 +192,24 @@ useful for con-trolling all kinds of movements of vehicles or also of course our
 | 
			
		||||
walking robots. They use a single callback per pad and have 8 or 10 different
 | 
			
		||||
event types to differentiate the button actions.
 | 
			
		||||
 | 
			
		||||
- P_LEFT_DOWN
 | 
			
		||||
- P_LEFT_UP
 | 
			
		||||
- P_RIGHT_DOWN
 | 
			
		||||
- P_RIGHT_UP
 | 
			
		||||
- P_FOR_DOWN
 | 
			
		||||
- P_FOR_UP
 | 
			
		||||
- P_BACK_DOWN
 | 
			
		||||
- P_BACK_UP
 | 
			
		||||
- P_CENTER_DOWN
 | 
			
		||||
- P_CENTER_UP
 | 
			
		||||
 | 
			
		||||
#### Labels
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Labels are a nice tool to get information from the robot to the user interface.
 | 
			
		||||
This can be done to show states, values of sensors and configuration parameters.
 | 
			
		||||
To send data from the code use `ESP.print(labelId, “Text”);` . Labels get a name
 | 
			
		||||
To send data from the code use `ESP.print(labelId, "Text");` . Labels get a name
 | 
			
		||||
on creation and a initial value. The name is not changeable once the UI
 | 
			
		||||
initialised.
 | 
			
		||||
 | 
			
		||||
@@ -205,36 +218,140 @@ the normal `<br>` tag in the string you print to the label
 | 
			
		||||
 | 
			
		||||
#### Slider
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The Slider can be used to slide through a value from 1 to 100. Slides provide
 | 
			
		||||
realtime data, are touch compatible and can be used to for example control a
 | 
			
		||||
Servo. The current value is shown while the slider is dragged in a little bubble
 | 
			
		||||
over the handle.
 | 
			
		||||
over the handle. In the Callback the slider does not return an int but a String.
 | 
			
		||||
Use the .toInt function to convert the value, see the **gui** example to check how it works.
 | 
			
		||||
 | 
			
		||||
#### Initialisation of the UI
 | 
			
		||||
A slider usually only sends a new value when it is released to save the esps from being spammed with values. This behaviour can be cahnged globally using a property of the ESPUI object before `begin()`:
 | 
			
		||||
 | 
			
		||||
After all the elements are configured you can use `ESPUI.begin(“Some Title”);`
 | 
			
		||||
to start the UI interface. (Or `ESPUI.beginSPIFFS(“Some Title”);` respectively)
 | 
			
		||||
```
 | 
			
		||||
  ESPUI.sliderContinuous = true;
 | 
			
		||||
  ESPUI.begin("ESPUI Control");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Number Input
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The numberinput can be used to directly input numbers to your program. You can
 | 
			
		||||
enter a Value into it and when you are done with your change it is sent to the
 | 
			
		||||
ESP.
 | 
			
		||||
 | 
			
		||||
A number box needs to have a min and a max value. To set it up just use:
 | 
			
		||||
 | 
			
		||||
`ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10);`
 | 
			
		||||
 | 
			
		||||
#### Text Input
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The textinput works very similar like the number input but with a string. You
 | 
			
		||||
can enter a String into it and when you are done with your change it is sent to
 | 
			
		||||
the ESP.
 | 
			
		||||
 | 
			
		||||
#### Graph
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The graph widget can display graph points with timestamp at wich they arrive
 | 
			
		||||
 | 
			
		||||
Use `ESPUI.addGraphPoint(graphId, random(1, 50));` to add a new value at the current time, use `ESPUI.clearGraph(graphId)` to clear the entire graph.
 | 
			
		||||
Graph points are saved in the browser in **localstorage** to be persistant, clear local storageto remove the points or use clearGraph() from a bbutton callback to provide a clear button.
 | 
			
		||||
 | 
			
		||||
#### Option select
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The option select works by first creating a select widget like so
 | 
			
		||||
 | 
			
		||||
`uint16_t select1 = ESPUI.addControl( ControlType::Select, "Select:", "", ControlColor::Alizarin, tab1, &selectExample );`
 | 
			
		||||
 | 
			
		||||
And then adding Options to it like seperate widgets, specifying the select as the parent:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
ESPUI.addControl( ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1 );
 | 
			
		||||
ESPUI.addControl( ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1 );
 | 
			
		||||
ESPUI.addControl( ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1 );
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Check the **tabbedGui** example for a working demo
 | 
			
		||||
 | 
			
		||||
### Using Tabs
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Tabs can be used to organize your widgets in pages. Check the tabbedGui example.
 | 
			
		||||
Tabs can be created using the generic functions like so:
 | 
			
		||||
`ESPUI.addControl( ControlType::Tab, "Settings 1", "Settings 1" );`
 | 
			
		||||
 | 
			
		||||
Then all widgets for the tab need to be added to it by specifying the tab as the parrent (widgets not added to a tab will be shown above the tab selctor)
 | 
			
		||||
 | 
			
		||||
`ESPUI.addControl( ControlType::Text, "Text Title:", "a Text Field", ControlColor::Alizarin, tab1, &textCall );`
 | 
			
		||||
 | 
			
		||||
### Initialisation of the UI
 | 
			
		||||
 | 
			
		||||
After all the elements are configured you can use `ESPUI.begin("Some Title");`
 | 
			
		||||
to start the UI interface. (Or `ESPUI.beginSPIFFS("Some Title");` respectively)
 | 
			
		||||
Make sure you setup a working network connection or AccesPoint **before** (See
 | 
			
		||||
example). The web interface can then be used from multiple devices at once and
 | 
			
		||||
also shows an connection status in the top bar. The library is designed to be
 | 
			
		||||
easy to use and can still be extended with a lot of more functionality.
 | 
			
		||||
gui.ino example). The web interface can then be used from multiple devices at once and
 | 
			
		||||
also shows an connection status in the top bar.
 | 
			
		||||
 | 
			
		||||
### Advanced: Generic creation and updates of control widgets
 | 
			
		||||
 | 
			
		||||
There are 2 generic functions to create and update controls, to see them in action check the **gui-generic-api** example.
 | 
			
		||||
 | 
			
		||||
To create a generic control use:
 | 
			
		||||
`uint16_t switchOne = ESPUI.addControl(ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, Control::noParent, &switchExample);`
 | 
			
		||||
 | 
			
		||||
Then its value can be updated by doing:
 | 
			
		||||
 | 
			
		||||
`ESPUI.updateControlValue(status, "Start");`
 | 
			
		||||
 | 
			
		||||
You can also update other parameters of the control like its color using:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
    ESPUI.getControl(switchOne)->color = ControlColor::Carrot;
 | 
			
		||||
    ESPUI.updateControl(switchOne);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Log output
 | 
			
		||||
 | 
			
		||||
ESPUI has several different log levels. You can set them using the
 | 
			
		||||
`ESPUI.setVerbosity(Verbosity::VerboseJSON)` function.
 | 
			
		||||
 | 
			
		||||
Loglevels are:
 | 
			
		||||
 | 
			
		||||
- `Verbosity::Quiet` (default)
 | 
			
		||||
- `Verbosity::Verbose`
 | 
			
		||||
- `Verbosity::VerboseJSON`
 | 
			
		||||
 | 
			
		||||
VerboseJSON outputs the most debug information.
 | 
			
		||||
 | 
			
		||||
### Advanced properties
 | 
			
		||||
 | 
			
		||||
If you have many different widgets it might be necessary to adjust the JSON Buffers used internally in ESPUI before .begin() :
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 ESPUI.jsonUpdateDocumentSize = 2000; // This is the default, and this value is not affected by the amount of widgets
 | 
			
		||||
 ESPUI.jsonInitialDocumentSize = 8000; // This is the default, adjust when you have too many widgets or options
 | 
			
		||||
 ESPUI.begin("ESPUI Control");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Notes for Development
 | 
			
		||||
 | 
			
		||||
If you want to work on the HTML/CSS/JS files, do make changes in the
 | 
			
		||||
`examples/gui/data` directory. When you need to transfer that code to the ESP,
 | 
			
		||||
run `tools/prepare_static_ui_sources.py -a` (this script needs python3 with the
 | 
			
		||||
modules htmlmin, jsmin and csscompressor). This will generate a) minified files
 | 
			
		||||
next to the original files to be uploaded with the ESP32 sketch data uploader
 | 
			
		||||
mentioned above and b) the C header files in `src` that contain the minified and
 | 
			
		||||
gzipped HTML/CSS/JS data (which are used by the **prepareFileSystem** example
 | 
			
		||||
sketch or when they are served from PROGMEM; see above in the section "Getting
 | 
			
		||||
started"). Alternatively, you can duplicate the `examples/gui` directory and
 | 
			
		||||
work on the copy. Then specify the `--source` and `--target` arguments to the
 | 
			
		||||
If you want to work on the HTML/CSS/JS files, do make changes in the _data_
 | 
			
		||||
directory. When you need to transfer that code to the ESP, run
 | 
			
		||||
`tools/prepare_static_ui_sources.py -a` (this script needs **python3** with the
 | 
			
		||||
modules **htmlmin**, **jsmin** and **csscompressor**). This will generate a) minified files
 | 
			
		||||
next to the original files and b) the C header files in `src` that contain the minified and
 | 
			
		||||
gzipped HTML/CSS/JS data. Alternatively, you can specify the `--source` and `--target` arguments to the
 | 
			
		||||
`prepare_static_ui_sources.py` script (run the script without arguments for
 | 
			
		||||
help).
 | 
			
		||||
help) if you want to use different locations.
 | 
			
		||||
 | 
			
		||||
If you don't have a python environment, you need to minify and gzip the
 | 
			
		||||
HTML/CSS/JS files manually. I wrote a little useful jsfiddle for this,
 | 
			
		||||
@@ -242,9 +359,11 @@ HTML/CSS/JS files manually. I wrote a little useful jsfiddle for this,
 | 
			
		||||
 | 
			
		||||
If you change something in HTML/CSS/JS and want to create a pull request, please
 | 
			
		||||
do include the minified versions and corresponding C header files in your
 | 
			
		||||
commits.
 | 
			
		||||
commits. (Do **NOT** commit all the minified versions for the non changed files)
 | 
			
		||||
 | 
			
		||||
# Contribute
 | 
			
		||||
 | 
			
		||||
Liked this Library? You can **support** me by sending me a :coffee:
 | 
			
		||||
[Coffee](https://paypal.me/lukasbachschwell/3).
 | 
			
		||||
[Coffee](https://paypal.me/lukasbachschwell/5).
 | 
			
		||||
 | 
			
		||||
Otherwise I really welcome **Pull Requests**.
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,16 @@
 | 
			
		||||
  padding-left: 20px;
 | 
			
		||||
  padding-right: 20px;
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  min-width: 150px;
 | 
			
		||||
  min-width: 500px;
 | 
			
		||||
  color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 630px) {
 | 
			
		||||
  .card {
 | 
			
		||||
    min-width: 98%;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.card-slider {
 | 
			
		||||
  padding-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
@@ -873,6 +879,17 @@ input {
 | 
			
		||||
  background: rgba(255, 255, 255, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
select {
 | 
			
		||||
  margin: 0 auto 1.2rem auto;
 | 
			
		||||
  padding: 2px 5px;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
 | 
			
		||||
  background: rgba(255, 255, 255, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[id^="num"] {
 | 
			
		||||
  max-width: 6em;
 | 
			
		||||
  width: auto;
 | 
			
		||||
@@ -880,3 +897,164 @@ input[id^="num"] {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  font-size: 115%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body div > ul.navigation {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  border-bottom: 3px solid #666;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
ul.navigation li {
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  float: left;
 | 
			
		||||
  margin-right: 4px;
 | 
			
		||||
}
 | 
			
		||||
ul.navigation li.controls {
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
ul.navigation li a {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 6px 12px;
 | 
			
		||||
  color: #888;
 | 
			
		||||
  outline: 0;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  background: #f3f3f3;
 | 
			
		||||
  background: -webkit-gradient(linear, 0 0, 0 bottom, from(#eee), to(#e4e4e4));
 | 
			
		||||
  background: -moz-linear-gradient(#eee, #e4e4e4);
 | 
			
		||||
  background: linear-gradient(#eee, #e4e4e4);
 | 
			
		||||
  -pie-background: linear-gradient(#eee, #e4e4e4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ul.navigation li.active a {
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  color: white;
 | 
			
		||||
  background: #666;
 | 
			
		||||
  background: -webkit-gradient(linear, 0 0, 0 bottom, from(#888), to(#666));
 | 
			
		||||
  background: -moz-linear-gradient(#888, #666);
 | 
			
		||||
  background: linear-gradient(#888, #666);
 | 
			
		||||
  -pie-background: linear-gradient(#888, #666);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.tabscontent > div {
 | 
			
		||||
  padding: 0 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#tabsnav:empty {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.range-slider {
 | 
			
		||||
  margin: 0 0 0 0;
 | 
			
		||||
}
 | 
			
		||||
.range-slider {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range {
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  width: calc(100% - (45px));
 | 
			
		||||
  height: 10px;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  outline: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range::-webkit-slider-thumb {
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  appearance: none;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: background 0.15s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range::-webkit-slider-thumb:hover {
 | 
			
		||||
  background: #1abc9c;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range:active::-webkit-slider-thumb {
 | 
			
		||||
  background: #1abc9c;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range::-moz-range-thumb {
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  border: 0;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: background 0.15s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__range:focus::-webkit-slider-thumb {
 | 
			
		||||
  box-shadow: 0 0 0 3px #fff, 0 0 0 6px #1abc9c;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__value {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 30px;
 | 
			
		||||
  color: #fff;
 | 
			
		||||
  line-height: 20px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  border-radius: 3px;
 | 
			
		||||
  padding: 5px 5px;
 | 
			
		||||
  margin-left: 2px;
 | 
			
		||||
}
 | 
			
		||||
.range-slider__value:after {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 8px;
 | 
			
		||||
  left: -7px;
 | 
			
		||||
  width: 0;
 | 
			
		||||
  height: 0;
 | 
			
		||||
  /*border-top:1px solid transparent;
 | 
			
		||||
 border-right:1px solid #2c3e50;
 | 
			
		||||
 border-bottom:1px solid transparent;*/
 | 
			
		||||
  content: "";
 | 
			
		||||
}
 | 
			
		||||
::-moz-range-track {
 | 
			
		||||
  border: 0;
 | 
			
		||||
}
 | 
			
		||||
input::-moz-focus-inner,
 | 
			
		||||
input::-moz-focus-outer {
 | 
			
		||||
  border: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Styles for Graph widget */
 | 
			
		||||
 | 
			
		||||
svg {
 | 
			
		||||
  display: block;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.y-axis path,
 | 
			
		||||
.x-axis path {
 | 
			
		||||
  stroke: gray;
 | 
			
		||||
  stroke-width: 1;
 | 
			
		||||
  fill: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.series {
 | 
			
		||||
  stroke: steelblue;
 | 
			
		||||
  stroke-width: 3;
 | 
			
		||||
  fill: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.data-points circle {
 | 
			
		||||
  stroke: steelblue;
 | 
			
		||||
  stroke-width: 2;
 | 
			
		||||
  fill: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.data-points text {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.data-points circle:hover {
 | 
			
		||||
  fill: steelblue;
 | 
			
		||||
  stroke-width: 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.data-points circle:hover + text {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
text {
 | 
			
		||||
  text-anchor: end;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								data/css/style.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/css/style.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										35
									
								
								data/index.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								data/index.htm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <title>Control</title>
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="shortcut icon"
 | 
			
		||||
      href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="stylesheet" href="/css/normalize.css" />
 | 
			
		||||
    <link rel="stylesheet" href="/css/style.css" />
 | 
			
		||||
 | 
			
		||||
    <script src="/js/zepto.min.js"></script>
 | 
			
		||||
    <script src="/js/slider.js"></script>
 | 
			
		||||
    <script src="/js/graph.js"></script>
 | 
			
		||||
    <script src="/js/controls.js"></script>
 | 
			
		||||
    <script src="/js/tabbedcontent.js"></script>
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body onload="javascript:start();">
 | 
			
		||||
    <div>
 | 
			
		||||
      <h4>
 | 
			
		||||
        <div id="mainHeader">Control</div>
 | 
			
		||||
        <span id="conStatus" class="label">Offline</span>
 | 
			
		||||
      </h4>
 | 
			
		||||
    </div>
 | 
			
		||||
    <hr />
 | 
			
		||||
    <div class="container">
 | 
			
		||||
      <div id="row" class="row u-full-width"></div>
 | 
			
		||||
      <ul id="tabsnav" class="navigation navigation-tabs u-full-width"></ul>
 | 
			
		||||
      <div id="tabscontent" class="tabscontent u-full-width"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/controls.js></script></head> <body onload=javascript:start();> <div> <h4><div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span></h4></div> <hr> <div class=container> <div id=row class="row u-full-width"> </div> </div> </body> </html> 
 | 
			
		||||
<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html> 
 | 
			
		||||
							
								
								
									
										895
									
								
								data/js/controls.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										895
									
								
								data/js/controls.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,895 @@
 | 
			
		||||
const UI_INITIAL_GUI = 200;
 | 
			
		||||
const UPDATE_OFFSET = 100;
 | 
			
		||||
 | 
			
		||||
const UI_TITEL = 0;
 | 
			
		||||
 | 
			
		||||
const UI_PAD = 1;
 | 
			
		||||
const UPDATE_PAD = 101;
 | 
			
		||||
 | 
			
		||||
const UI_CPAD = 2;
 | 
			
		||||
const UPDATE_CPAD = 102;
 | 
			
		||||
 | 
			
		||||
const UI_BUTTON = 3;
 | 
			
		||||
const UPDATE_BUTTON = 103;
 | 
			
		||||
 | 
			
		||||
const UI_LABEL = 4;
 | 
			
		||||
const UPDATE_LABEL = 104;
 | 
			
		||||
 | 
			
		||||
const UI_SWITCHER = 5;
 | 
			
		||||
const UPDATE_SWITCHER = 105;
 | 
			
		||||
 | 
			
		||||
const UI_SLIDER = 6;
 | 
			
		||||
const UPDATE_SLIDER = 106;
 | 
			
		||||
 | 
			
		||||
const UI_NUMBER = 7;
 | 
			
		||||
const UPDATE_NUMBER = 107;
 | 
			
		||||
 | 
			
		||||
const UI_TEXT_INPUT = 8;
 | 
			
		||||
const UPDATE_TEXT_INPUT = 108;
 | 
			
		||||
 | 
			
		||||
const UI_GRAPH = 9;
 | 
			
		||||
const ADD_GRAPH_POINT = 10;
 | 
			
		||||
const CLEAR_GRAPH = 109;
 | 
			
		||||
 | 
			
		||||
const UI_TAB = 11;
 | 
			
		||||
const UPDATE_TAB = 111;
 | 
			
		||||
 | 
			
		||||
const UI_SELECT = 12;
 | 
			
		||||
const UPDATE_SELECT = 112;
 | 
			
		||||
 | 
			
		||||
const UI_OPTION = 13;
 | 
			
		||||
const UPDATE_OPTION = 113;
 | 
			
		||||
const UI_MIN = 14;
 | 
			
		||||
const UPDATE_MIN = 114;
 | 
			
		||||
const UI_MAX = 15;
 | 
			
		||||
const UPDATE_MAX = 115;
 | 
			
		||||
const UI_STEP = 16;
 | 
			
		||||
const UPDATE_STEP = 116;
 | 
			
		||||
 | 
			
		||||
const UI_GAUGE = 17;
 | 
			
		||||
const UPTDATE_GAUGE = 117;
 | 
			
		||||
const UI_ACCEL = 18;
 | 
			
		||||
const UPTDATE_ACCEL = 117;
 | 
			
		||||
 | 
			
		||||
const UP = 0;
 | 
			
		||||
const DOWN = 1;
 | 
			
		||||
const LEFT = 2;
 | 
			
		||||
const RIGHT = 3;
 | 
			
		||||
const CENTER = 4;
 | 
			
		||||
 | 
			
		||||
// Colors
 | 
			
		||||
const C_TURQUOISE = 0;
 | 
			
		||||
const C_EMERALD = 1;
 | 
			
		||||
const C_PETERRIVER = 2;
 | 
			
		||||
const C_WETASPHALT = 3;
 | 
			
		||||
const C_SUNFLOWER = 4;
 | 
			
		||||
const C_CARROT = 5;
 | 
			
		||||
const C_ALIZARIN = 6;
 | 
			
		||||
const C_DARK = 7;
 | 
			
		||||
const C_NONE = 255;
 | 
			
		||||
 | 
			
		||||
var graphData = new Array();
 | 
			
		||||
var hasAccel = false;
 | 
			
		||||
var sliderContinuous = false;
 | 
			
		||||
 | 
			
		||||
function colorClass(colorId) {
 | 
			
		||||
  colorId = Number(colorId);
 | 
			
		||||
  switch (colorId) {
 | 
			
		||||
    case C_TURQUOISE:
 | 
			
		||||
      return "turquoise";
 | 
			
		||||
 | 
			
		||||
    case C_EMERALD:
 | 
			
		||||
      return "emerald";
 | 
			
		||||
 | 
			
		||||
    case C_PETERRIVER:
 | 
			
		||||
      return "peterriver";
 | 
			
		||||
 | 
			
		||||
    case C_WETASPHALT:
 | 
			
		||||
      return "wetasphalt";
 | 
			
		||||
 | 
			
		||||
    case C_SUNFLOWER:
 | 
			
		||||
      return "sunflower";
 | 
			
		||||
 | 
			
		||||
    case C_CARROT:
 | 
			
		||||
      return "carrot";
 | 
			
		||||
 | 
			
		||||
    case C_ALIZARIN:
 | 
			
		||||
      return "alizarin";
 | 
			
		||||
 | 
			
		||||
    case C_NONE:
 | 
			
		||||
      return "dark";
 | 
			
		||||
    default:
 | 
			
		||||
      return "";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var websock;
 | 
			
		||||
var websockConnected = false;
 | 
			
		||||
 | 
			
		||||
function requestOrientationPermission() {
 | 
			
		||||
  /*
 | 
			
		||||
  // Currently this fails, since it needs secure context on IOS safari
 | 
			
		||||
  if (typeof DeviceMotionEvent.requestPermission === "function") {
 | 
			
		||||
    DeviceOrientationEvent.requestPermission()
 | 
			
		||||
      .then(response => {
 | 
			
		||||
        if (response == "granted") {
 | 
			
		||||
          window.addEventListener("deviceorientation", handleOrientation);
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch(console.error);
 | 
			
		||||
  } else {
 | 
			
		||||
    // Non IOS 13
 | 
			
		||||
    window.addEventListener("deviceorientation", handleOrientation);
 | 
			
		||||
  }
 | 
			
		||||
  */
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
function handleOrientation(event) {
 | 
			
		||||
  var x = event.beta; // In degree in the range [-180,180]
 | 
			
		||||
  var y = event.gamma; // In degree in the range [-90,90]
 | 
			
		||||
 | 
			
		||||
  var output = document.querySelector(".output");
 | 
			
		||||
  output.innerHTML = "beta : " + x + "\n";
 | 
			
		||||
  output.innerHTML += "gamma: " + y + "\n";
 | 
			
		||||
 | 
			
		||||
  // Because we don't want to have the device upside down
 | 
			
		||||
  // We constrain the x value to the range [-90,90]
 | 
			
		||||
  if (x > 90) {
 | 
			
		||||
    x = 90;
 | 
			
		||||
  }
 | 
			
		||||
  if (x < -90) {
 | 
			
		||||
    x = -90;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // To make computation easier we shift the range of
 | 
			
		||||
  // x and y to [0,180]
 | 
			
		||||
  x += 90;
 | 
			
		||||
  y += 90;
 | 
			
		||||
 | 
			
		||||
  // 10 is half the size of the ball
 | 
			
		||||
  // It center the positioning point to the center of the ball
 | 
			
		||||
  var ball = document.querySelector(".ball");
 | 
			
		||||
  var garden = document.querySelector(".garden");
 | 
			
		||||
  var maxX = garden.clientWidth - ball.clientWidth;
 | 
			
		||||
  var maxY = garden.clientHeight - ball.clientHeight;
 | 
			
		||||
  ball.style.top = (maxY * y) / 180 - 10 + "px";
 | 
			
		||||
  ball.style.left = (maxX * x) / 180 - 10 + "px";
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
function saveGraphData() {
 | 
			
		||||
  localStorage.setItem("espuigraphs", JSON.stringify(graphData));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function restoreGraphData(id) {
 | 
			
		||||
  var savedData = localStorage.getItem("espuigraphs", graphData);
 | 
			
		||||
  if (savedData != null) {
 | 
			
		||||
    savedData = JSON.parse(savedData);
 | 
			
		||||
    return savedData[id];
 | 
			
		||||
  }
 | 
			
		||||
  return [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function restart() {
 | 
			
		||||
  $(document)
 | 
			
		||||
    .add("*")
 | 
			
		||||
    .off();
 | 
			
		||||
  $("#row").html("");
 | 
			
		||||
  websock.close();
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function conStatusError() {
 | 
			
		||||
  websockConnected = false;
 | 
			
		||||
  $("#conStatus").removeClass("color-green");
 | 
			
		||||
  $("#conStatus").addClass("color-red");
 | 
			
		||||
  $("#conStatus").html("Error / No Connection ↻");
 | 
			
		||||
  $("#conStatus").off();
 | 
			
		||||
  $("#conStatus").on({
 | 
			
		||||
    click: restart
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleVisibilityChange() {
 | 
			
		||||
  if (!websockConnected && !document.hidden) {
 | 
			
		||||
    restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
  document.addEventListener("visibilitychange", handleVisibilityChange, false);
 | 
			
		||||
  websock = new WebSocket("ws://" + window.location.hostname + "/ws");
 | 
			
		||||
  websock.onopen = function(evt) {
 | 
			
		||||
    console.log("websock open");
 | 
			
		||||
    $("#conStatus").addClass("color-green");
 | 
			
		||||
    $("#conStatus").text("Connected");
 | 
			
		||||
    websockConnected = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onclose = function(evt) {
 | 
			
		||||
    console.log("websock close");
 | 
			
		||||
    conStatusError();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onerror = function(evt) {
 | 
			
		||||
    console.log(evt);
 | 
			
		||||
    conStatusError();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var handleEvent = function(evt) {
 | 
			
		||||
    //console.log(evt);
 | 
			
		||||
    var data = JSON.parse(evt.data);
 | 
			
		||||
    var e = document.body;
 | 
			
		||||
    var center = "";
 | 
			
		||||
    switch (data.type) {
 | 
			
		||||
      case UI_INITIAL_GUI:
 | 
			
		||||
        if (data.sliderContinuous) {
 | 
			
		||||
          sliderContinuous = data.sliderContinuous;
 | 
			
		||||
        }
 | 
			
		||||
        data.controls.forEach(element => {
 | 
			
		||||
          var fauxEvent = {
 | 
			
		||||
            data: JSON.stringify(element)
 | 
			
		||||
          };
 | 
			
		||||
          handleEvent(fauxEvent);
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_TITEL:
 | 
			
		||||
        document.title = data.label;
 | 
			
		||||
        $("#mainHeader").html(data.label);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_LABEL:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<span id='l" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='label label-wrap'>" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "</span>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_BUTTON:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='one columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<button id='btn" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' " +
 | 
			
		||||
            "onmousedown='buttonclick(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' " +
 | 
			
		||||
            "onmouseup='buttonclick(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)'>" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "</button></div>"
 | 
			
		||||
        );
 | 
			
		||||
        $("#btn" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            buttonclick(data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            buttonclick(data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_SWITCHER:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='one columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<label id='sl" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='switch " +
 | 
			
		||||
            (data.value == "1" ? "checked" : "") +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<div class='in'><input type='checkbox' id='s" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' onClick='switcher(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ",null)' " +
 | 
			
		||||
            (data.value == "1" ? "checked" : "") +
 | 
			
		||||
            "/></div>" +
 | 
			
		||||
            "</label>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        switcher(data.id, data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_CPAD:
 | 
			
		||||
      case UI_PAD:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<nav class='control'>" +
 | 
			
		||||
            "<ul>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(UP, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(UP, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' id='pf" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(RIGHT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(RIGHT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' id='pr" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(LEFT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(LEFT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' id='pl" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(DOWN, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(DOWN, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' id='pb" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "</ul>" +
 | 
			
		||||
            (data.type == UI_CPAD
 | 
			
		||||
              ? "<a class='confirm' onmousedown='padclick(CENTER," +
 | 
			
		||||
                data.id +
 | 
			
		||||
                ", true)' onmouseup='padclick(CENTER, " +
 | 
			
		||||
                data.id +
 | 
			
		||||
                ", false)' id='pc" +
 | 
			
		||||
                data.id +
 | 
			
		||||
                "'>OK</a>"
 | 
			
		||||
              : "") +
 | 
			
		||||
            "</nav>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $("#pf" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(UP, data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(UP, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pl" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(LEFT, data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(LEFT, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pr" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(RIGHT, data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(RIGHT, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pb" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(DOWN, data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(DOWN, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pc" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(CENTER, data.id, true);
 | 
			
		||||
          },
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(CENTER, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      //https://codepen.io/seanstopnik/pen/CeLqA
 | 
			
		||||
      case UI_SLIDER:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter card-slider " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<div class='range-slider'>" +
 | 
			
		||||
            "<input id='sl" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' type='range' min='0' max='100' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' class='range-slider__range'>" +
 | 
			
		||||
            "<span class='range-slider__value'>" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "</span>" +
 | 
			
		||||
            "</div>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        rangeSlider(!sliderContinuous);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_NUMBER:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<input style='color:black;' id='num" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' type='number' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' onchange='numberchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_TEXT_INPUT:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<input style='color:black;' id='text" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' onchange='textchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_TAB:
 | 
			
		||||
        $("#tabsnav").append(
 | 
			
		||||
          "<li><a href='#tab" + data.id + "'>" + data.value + "</a></li>"
 | 
			
		||||
        );
 | 
			
		||||
        $("#tabscontent").append("<div id='tab" + data.id + "'></div>");
 | 
			
		||||
 | 
			
		||||
        tabs = $(".tabscontent")
 | 
			
		||||
          .tabbedContent({ loop: true })
 | 
			
		||||
          .data("api");
 | 
			
		||||
        // switch to tab...
 | 
			
		||||
        $("a")
 | 
			
		||||
          .filter(function() {
 | 
			
		||||
            return $(this).attr("href") === "#click-to-switch";
 | 
			
		||||
          })
 | 
			
		||||
          .on("click", function(e) {
 | 
			
		||||
            var tab = prompt("Tab to switch to (number or id)?");
 | 
			
		||||
            if (!tabs.switchTab(tab)) {
 | 
			
		||||
              alert("That tab does not exist :\\");
 | 
			
		||||
            }
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
          });
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_SELECT:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<select style='color:black;' id='select" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' onchange='selectchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_OPTION:
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          var parent = $("#select" + data.parentControl);
 | 
			
		||||
          parent.append(
 | 
			
		||||
            "<option id='option" +
 | 
			
		||||
              data.id +
 | 
			
		||||
              "' value='" +
 | 
			
		||||
              data.value +
 | 
			
		||||
              "' " +
 | 
			
		||||
              data.selected +
 | 
			
		||||
              ">" +
 | 
			
		||||
              data.label +
 | 
			
		||||
              "</option>"
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_MIN:
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          var parent = $("#id" + data.parentControl + " input");
 | 
			
		||||
          if (parent.size()) {
 | 
			
		||||
            parent.attr("min", data.value);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_MAX:
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          var parent = $("#id" + data.parentControl + " input");
 | 
			
		||||
          if (parent.size()) {
 | 
			
		||||
            parent.attr("max", data.value);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_STEP:
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          var parent = $("#id" + data.parentControl + " input");
 | 
			
		||||
          if (parent.size()) {
 | 
			
		||||
            parent.attr("step", data.value);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_GRAPH:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<figure id='graph" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<figcaption>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</figcaption>" +
 | 
			
		||||
            "</figure>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        graphData[data.id] = restoreGraphData(data.id);
 | 
			
		||||
        renderGraphSvg(graphData[data.id], "graph" + data.id);
 | 
			
		||||
        break;
 | 
			
		||||
      case ADD_GRAPH_POINT:
 | 
			
		||||
        var ts = Math.round(new Date().getTime() / 1000);
 | 
			
		||||
        graphData[data.id].push({ x: ts, y: data.value });
 | 
			
		||||
        saveGraphData();
 | 
			
		||||
        renderGraphSvg(graphData[data.id], "graph" + data.id);
 | 
			
		||||
        break;
 | 
			
		||||
      case CLEAR_GRAPH:
 | 
			
		||||
        graphData[data.id] = [];
 | 
			
		||||
        saveGraphData();
 | 
			
		||||
        renderGraphSvg(graphData[data.id], "graph" + data.id);
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_GAUGE:
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "WILL BE A GAUGE <input style='color:black;' id='gauge" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' type='number' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' onchange='numberchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_ACCEL:
 | 
			
		||||
        if (hasAccel) break;
 | 
			
		||||
        var parent;
 | 
			
		||||
        if (data.parentControl) {
 | 
			
		||||
          parent = $("#tab" + data.parentControl);
 | 
			
		||||
        } else {
 | 
			
		||||
          parent = $("#row");
 | 
			
		||||
        }
 | 
			
		||||
        hasAccel = true;
 | 
			
		||||
        parent.append(
 | 
			
		||||
          "<div id='id" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "ACCEL // Not implemented fully!<div class='accelerometer' id='accel" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' ><div class='ball" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'></div><pre class='accelerometeroutput" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'></pre>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        requestOrientationPermission();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_LABEL:
 | 
			
		||||
        $("#l" + data.id).html(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_SWITCHER:
 | 
			
		||||
        switcher(data.id, data.value == "0" ? 0 : 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_SLIDER:
 | 
			
		||||
        slider_move($("#sl" + data.id), data.value, "100", false);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_NUMBER:
 | 
			
		||||
        $("#num" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_TEXT_INPUT:
 | 
			
		||||
        $("#text" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_SELECT:
 | 
			
		||||
        $("#select" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_BUTTON:
 | 
			
		||||
      case UPDATE_PAD:
 | 
			
		||||
      case UPDATE_CPAD:
 | 
			
		||||
        break;
 | 
			
		||||
      case UPDATE_GAUGE:
 | 
			
		||||
        $("#gauge" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
      case UPDATE_ACCEL:
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        console.error("Unknown type or event");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (data.type >= UPDATE_OFFSET && data.type < UI_INITIAL_GUI) {
 | 
			
		||||
      var element = $("#id" + data.id);
 | 
			
		||||
      if (data.type == UPDATE_SLIDER) {
 | 
			
		||||
        element.removeClass(
 | 
			
		||||
          "slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin"
 | 
			
		||||
        );
 | 
			
		||||
        element.addClass("slider-" + colorClass(data.color));
 | 
			
		||||
      } else {
 | 
			
		||||
        element.removeClass(
 | 
			
		||||
          "turquoise emerald peterriver wetasphalt sunflower carrot alizarin"
 | 
			
		||||
        );
 | 
			
		||||
        element.addClass(colorClass(data.color));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onmessage = handleEvent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sliderchange(number) {
 | 
			
		||||
  var val = $("#sl" + number).val();
 | 
			
		||||
  websock.send("slvalue:" + val + ":" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function numberchange(number) {
 | 
			
		||||
  var val = $("#num" + number).val();
 | 
			
		||||
  websock.send("nvalue:" + val + ":" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function textchange(number) {
 | 
			
		||||
  var val = $("#text" + number).val();
 | 
			
		||||
  websock.send("tvalue:" + val + ":" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function selectchange(number) {
 | 
			
		||||
  var val = $("#select" + number).val();
 | 
			
		||||
  websock.send("svalue:" + val + ":" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function buttonclick(number, isdown) {
 | 
			
		||||
  if (isdown) websock.send("bdown:" + number);
 | 
			
		||||
  else websock.send("bup:" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function padclick(type, number, isdown) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case CENTER:
 | 
			
		||||
      if (isdown) websock.send("pcdown:" + number);
 | 
			
		||||
      else websock.send("pcup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case UP:
 | 
			
		||||
      if (isdown) websock.send("pfdown:" + number);
 | 
			
		||||
      else websock.send("pfup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case DOWN:
 | 
			
		||||
      if (isdown) websock.send("pbdown:" + number);
 | 
			
		||||
      else websock.send("pbup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case LEFT:
 | 
			
		||||
      if (isdown) websock.send("pldown:" + number);
 | 
			
		||||
      else websock.send("plup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case RIGHT:
 | 
			
		||||
      if (isdown) websock.send("prdown:" + number);
 | 
			
		||||
      else websock.send("prup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function switcher(number, state) {
 | 
			
		||||
  if (state == null) {
 | 
			
		||||
    if ($("#s" + number).is(":checked")) {
 | 
			
		||||
      websock.send("sactive:" + number);
 | 
			
		||||
      $("#sl" + number).addClass("checked");
 | 
			
		||||
    } else {
 | 
			
		||||
      websock.send("sinactive:" + number);
 | 
			
		||||
      $("#sl" + number).removeClass("checked");
 | 
			
		||||
    }
 | 
			
		||||
  } else if (state == 1) {
 | 
			
		||||
    $("#sl" + number).addClass("checked");
 | 
			
		||||
    $("#sl" + number).prop("checked", true);
 | 
			
		||||
  } else if (state == 0) {
 | 
			
		||||
    $("#sl" + number).removeClass("checked");
 | 
			
		||||
    $("#sl" + number).prop("checked", false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var rangeSlider = function(isDiscrete) {
 | 
			
		||||
  var slider = $(".range-slider"),
 | 
			
		||||
    range = $(".range-slider__range"),
 | 
			
		||||
    value = $(".range-slider__value");
 | 
			
		||||
 | 
			
		||||
  slider.each(function() {
 | 
			
		||||
    value.each(function() {
 | 
			
		||||
      var value = $(this)
 | 
			
		||||
        .prev()
 | 
			
		||||
        .attr("value");
 | 
			
		||||
      $(this).html(value);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!isDiscrete) {
 | 
			
		||||
      range.on({
 | 
			
		||||
        input: function() {
 | 
			
		||||
          sliderchange(
 | 
			
		||||
            $(this)
 | 
			
		||||
              .attr("id")
 | 
			
		||||
              .replace(/^\D+/g, "")
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      range.on({
 | 
			
		||||
        input: function() {
 | 
			
		||||
          $(this)
 | 
			
		||||
            .next()
 | 
			
		||||
            .html(this.value);
 | 
			
		||||
        },
 | 
			
		||||
        change: function() {
 | 
			
		||||
          sliderchange(
 | 
			
		||||
            $(this)
 | 
			
		||||
              .attr("id")
 | 
			
		||||
              .replace(/^\D+/g, "")
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										248
									
								
								data/js/controls.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								data/js/controls.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,248 @@
 | 
			
		||||
const UI_INITIAL_GUI=200;const UPDATE_OFFSET=100;const UI_TITEL=0;const UI_PAD=1;const UPDATE_PAD=101;const UI_CPAD=2;const UPDATE_CPAD=102;const UI_BUTTON=3;const UPDATE_BUTTON=103;const UI_LABEL=4;const UPDATE_LABEL=104;const UI_SWITCHER=5;const UPDATE_SWITCHER=105;const UI_SLIDER=6;const UPDATE_SLIDER=106;const UI_NUMBER=7;const UPDATE_NUMBER=107;const UI_TEXT_INPUT=8;const UPDATE_TEXT_INPUT=108;const UI_GRAPH=9;const ADD_GRAPH_POINT=10;const CLEAR_GRAPH=109;const UI_TAB=11;const UPDATE_TAB=111;const UI_SELECT=12;const UPDATE_SELECT=112;const UI_OPTION=13;const UPDATE_OPTION=113;const UI_MIN=14;const UPDATE_MIN=114;const UI_MAX=15;const UPDATE_MAX=115;const UI_STEP=16;const UPDATE_STEP=116;const UI_GAUGE=17;const UPTDATE_GAUGE=117;const UI_ACCEL=18;const UPTDATE_ACCEL=117;const UP=0;const DOWN=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_DARK=7;const C_NONE=255;var graphData=new Array();var hasAccel=false;var sliderContinuous=false;function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_NONE:return"dark";default:return"";}}
 | 
			
		||||
var websock;var websockConnected=false;function requestOrientationPermission(){}
 | 
			
		||||
function saveGraphData(){localStorage.setItem("espuigraphs",JSON.stringify(graphData));}
 | 
			
		||||
function restoreGraphData(id){var savedData=localStorage.getItem("espuigraphs",graphData);if(savedData!=null){savedData=JSON.parse(savedData);return savedData[id];}
 | 
			
		||||
return[];}
 | 
			
		||||
function restart(){$(document).add("*").off();$("#row").html("");websock.close();start();}
 | 
			
		||||
function conStatusError(){websockConnected=false;$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#conStatus").off();$("#conStatus").on({click:restart});}
 | 
			
		||||
function handleVisibilityChange(){if(!websockConnected&&!document.hidden){restart();}}
 | 
			
		||||
function start(){document.addEventListener("visibilitychange",handleVisibilityChange,false);websock=new WebSocket("ws://"+window.location.hostname+"/ws");websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;};websock.onclose=function(evt){console.log("websock close");conStatusError();};websock.onerror=function(evt){console.log(evt);conStatusError();};var handleEvent=function(evt){var data=JSON.parse(evt.data);var e=document.body;var center="";switch(data.type){case UI_INITIAL_GUI:if(data.sliderContinuous){sliderContinuous=data.sliderContinuous;}
 | 
			
		||||
data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element)};handleEvent(fauxEvent);});break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case UI_LABEL:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<span id='l"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='label label-wrap'>"+
 | 
			
		||||
data.value+
 | 
			
		||||
"</span>"+
 | 
			
		||||
"</div>");break;case UI_BUTTON:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='one columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<button id='btn"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' "+
 | 
			
		||||
"onmousedown='buttonclick("+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' "+
 | 
			
		||||
"onmouseup='buttonclick("+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)'>"+
 | 
			
		||||
data.value+
 | 
			
		||||
"</button></div>");$("#btn"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);},touchend:function(e){e.preventDefault();buttonclick(data.id,false);}});break;case UI_SWITCHER:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='one columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<label id='sl"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='switch "+
 | 
			
		||||
(data.value=="1"?"checked":"")+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<div class='in'><input type='checkbox' id='s"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' onClick='switcher("+
 | 
			
		||||
data.id+
 | 
			
		||||
",null)' "+
 | 
			
		||||
(data.value=="1"?"checked":"")+
 | 
			
		||||
"/></div>"+
 | 
			
		||||
"</label>"+
 | 
			
		||||
"</div>");switcher(data.id,data.value);break;case UI_CPAD:case UI_PAD:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<nav class='control'>"+
 | 
			
		||||
"<ul>"+
 | 
			
		||||
"<li><a onmousedown='padclick(UP, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(UP, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='pf"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(RIGHT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(RIGHT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='pr"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(LEFT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(LEFT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='pl"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(DOWN, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(DOWN, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='pb"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"</ul>"+
 | 
			
		||||
(data.type==UI_CPAD?"<a class='confirm' onmousedown='padclick(CENTER,"+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(CENTER, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='pc"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>OK</a>":"")+
 | 
			
		||||
"</nav>"+
 | 
			
		||||
"</div>");$("#pf"+data.id).on({touchstart:function(e){e.preventDefault();padclick(UP,data.id,true);},touchend:function(e){e.preventDefault();padclick(UP,data.id,false);}});$("#pl"+data.id).on({touchstart:function(e){e.preventDefault();padclick(LEFT,data.id,true);},touchend:function(e){e.preventDefault();padclick(LEFT,data.id,false);}});$("#pr"+data.id).on({touchstart:function(e){e.preventDefault();padclick(RIGHT,data.id,true);},touchend:function(e){e.preventDefault();padclick(RIGHT,data.id,false);}});$("#pb"+data.id).on({touchstart:function(e){e.preventDefault();padclick(DOWN,data.id,true);},touchend:function(e){e.preventDefault();padclick(DOWN,data.id,false);}});$("#pc"+data.id).on({touchstart:function(e){e.preventDefault();padclick(CENTER,data.id,true);},touchend:function(e){e.preventDefault();padclick(CENTER,data.id,false);}});break;case UI_SLIDER:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter card-slider "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<div class='range-slider'>"+
 | 
			
		||||
"<input id='sl"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' type='range' min='0' max='100' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' class='range-slider__range'>"+
 | 
			
		||||
"<span class='range-slider__value'>"+
 | 
			
		||||
data.value+
 | 
			
		||||
"</span>"+
 | 
			
		||||
"</div>"+
 | 
			
		||||
"</div>");rangeSlider(!sliderContinuous);break;case UI_NUMBER:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<input style='color:black;' id='num"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' type='number' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' onchange='numberchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UI_TEXT_INPUT:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<input style='color:black;' id='text"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' onchange='textchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UI_TAB:$("#tabsnav").append("<li><a href='#tab"+data.id+"'>"+data.value+"</a></li>");$("#tabscontent").append("<div id='tab"+data.id+"'></div>");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");}
 | 
			
		||||
e.preventDefault();});break;case UI_SELECT:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<select style='color:black;' id='select"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' onchange='selectchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("<option id='option"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' "+
 | 
			
		||||
data.selected+
 | 
			
		||||
">"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</option>");}
 | 
			
		||||
break;case UI_MIN:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("min",data.value);}}
 | 
			
		||||
break;case UI_MAX:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("max",data.value);}}
 | 
			
		||||
break;case UI_STEP:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("step",data.value);}}
 | 
			
		||||
break;case UI_GRAPH:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<figure id='graph"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<figcaption>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</figcaption>"+
 | 
			
		||||
"</figure>"+
 | 
			
		||||
"</div>");graphData[data.id]=restoreGraphData(data.id);renderGraphSvg(graphData[data.id],"graph"+data.id);break;case ADD_GRAPH_POINT:var ts=Math.round(new Date().getTime()/1000);graphData[data.id].push({x:ts,y:data.value});saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case CLEAR_GRAPH:graphData[data.id]=[];saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case UI_GAUGE:var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"WILL BE A GAUGE <input style='color:black;' id='gauge"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' type='number' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' onchange='numberchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UI_ACCEL:if(hasAccel)break;var parent;if(data.parentControl){parent=$("#tab"+data.parentControl);}else{parent=$("#row");}
 | 
			
		||||
hasAccel=true;parent.append("<div id='id"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"ACCEL // Not implemented fully!<div class='accelerometer' id='accel"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' ><div class='ball"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'></div><pre class='accelerometeroutput"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'></pre>"+
 | 
			
		||||
"</div>");requestOrientationPermission();break;case UPDATE_LABEL:$("#l"+data.id).html(data.value);break;case UPDATE_SWITCHER:switcher(data.id,data.value=="0"?0:1);break;case UPDATE_SLIDER:slider_move($("#sl"+data.id),data.value,"100",false);break;case UPDATE_NUMBER:$("#num"+data.id).val(data.value);break;case UPDATE_TEXT_INPUT:$("#text"+data.id).val(data.value);break;case UPDATE_SELECT:$("#select"+data.id).val(data.value);break;case UPDATE_BUTTON:case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);break;case UPDATE_ACCEL:break;default:console.error("Unknown type or event");break;}
 | 
			
		||||
if(data.type>=UPDATE_OFFSET&&data.type<UI_INITIAL_GUI){var element=$("#id"+data.id);element.removeClass("turquoise emerald peterriver wetasphalt sunflower carrot alizarin");element.addClass(colorClass(data.color));}};websock.onmessage=handleEvent;}
 | 
			
		||||
function sliderchange(number){var val=$("#sl"+number).val();console.log("slvalue:"+val+":"+number);websock.send("slvalue:"+val+":"+number);}
 | 
			
		||||
function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);}
 | 
			
		||||
function textchange(number){var val=$("#text"+number).val();websock.send("tvalue:"+val+":"+number);}
 | 
			
		||||
function selectchange(number){var val=$("#select"+number).val();websock.send("svalue:"+val+":"+number);}
 | 
			
		||||
function buttonclick(number,isdown){if(isdown)websock.send("bdown:"+number);else websock.send("bup:"+number);}
 | 
			
		||||
function padclick(type,number,isdown){switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case UP:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case DOWN:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}}
 | 
			
		||||
function switcher(number,state){if(state==null){if($("#s"+number).is(":checked")){websock.send("sactive:"+number);$("#sl"+number).addClass("checked");}else{websock.send("sinactive:"+number);$("#sl"+number).removeClass("checked");}}else if(state==1){$("#sl"+number).addClass("checked");$("#sl"+number).prop("checked",true);}else if(state==0){$("#sl"+number).removeClass("checked");$("#sl"+number).prop("checked",false);}}
 | 
			
		||||
var rangeSlider=function(isDiscrete){var slider=$(".range-slider"),range=$(".range-slider__range"),value=$(".range-slider__value");slider.each(function(){value.each(function(){var value=$(this).prev().attr("value");$(this).html(value);});if(!isDiscrete){range.on({input:function(){sliderchange($(this).attr("id").replace(/^\D+/g,""));}});}else{range.on({input:function(){$(this).next().html(this.value);},change:function(){sliderchange($(this).attr("id").replace(/^\D+/g,""));}});}});};
 | 
			
		||||
							
								
								
									
										297
									
								
								data/js/graph.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								data/js/graph.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
			
		||||
function lineGraph(parent, xAccessor, yAccessor) {
 | 
			
		||||
  // Constant size definitions TODO: this could well be improved and calculated...
 | 
			
		||||
  const width = 620;
 | 
			
		||||
  const height = 420;
 | 
			
		||||
  const gutter = 40;
 | 
			
		||||
  const pixelsPerTick = 30;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates an object that contatins transform functions that:
 | 
			
		||||
   *   transforms numeric data into coordinate space, linearly
 | 
			
		||||
   *   transforms coordinates into numeric data, linearly
 | 
			
		||||
   */
 | 
			
		||||
  function numericTransformer(dataMin, dataMax, pxMin, pxMax) {
 | 
			
		||||
    var dataDiff = dataMax - dataMin,
 | 
			
		||||
      pxDiff = pxMax - pxMin,
 | 
			
		||||
      dataRatio = pxDiff / dataDiff,
 | 
			
		||||
      coordRatio = dataDiff / pxDiff;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      // transforms a data point to a coordinate point
 | 
			
		||||
      toCoord: function(data) {
 | 
			
		||||
        return (data - dataMin) * dataRatio + pxMin;
 | 
			
		||||
      },
 | 
			
		||||
      // transforms a coord point to a data point
 | 
			
		||||
      toData: function(coord) {
 | 
			
		||||
        return (coord - pxMin) * coordRatio + dataMin;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Renders an axis.
 | 
			
		||||
   *   orientation = 'x' or 'y'
 | 
			
		||||
   *   transform = a function for transforming px into data for labeling/creating tick marks
 | 
			
		||||
   */
 | 
			
		||||
  function axisRenderer(orientation, transform) {
 | 
			
		||||
    var axisGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
 | 
			
		||||
    var axisPath = document.createElementNS(
 | 
			
		||||
      "http://www.w3.org/2000/svg",
 | 
			
		||||
      "path"
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    axisGroup.setAttribute("class", orientation + "-axis");
 | 
			
		||||
 | 
			
		||||
    var xMin = gutter;
 | 
			
		||||
    var xMax = width - gutter;
 | 
			
		||||
    var yMin = height - gutter;
 | 
			
		||||
    var yMax = gutter;
 | 
			
		||||
 | 
			
		||||
    if (orientation === "x") {
 | 
			
		||||
      axisPath.setAttribute(
 | 
			
		||||
        "d",
 | 
			
		||||
        "M " + xMin + " " + yMin + " L " + xMax + " " + yMin
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // generate labels
 | 
			
		||||
      for (var i = xMin; i <= xMax; i++) {
 | 
			
		||||
        if ((i - xMin) % pixelsPerTick === 0 && i !== xMin) {
 | 
			
		||||
          var text = document.createElementNS(
 | 
			
		||||
            "http://www.w3.org/2000/svg",
 | 
			
		||||
            "text"
 | 
			
		||||
          );
 | 
			
		||||
          // primitive formatting
 | 
			
		||||
          text.innerHTML = Math.floor(transform(i));
 | 
			
		||||
          text.setAttribute("x", i);
 | 
			
		||||
          text.setAttribute("y", yMin);
 | 
			
		||||
          // offset the text by 1 em
 | 
			
		||||
          text.setAttribute("dy", "1em");
 | 
			
		||||
          axisGroup.appendChild(text);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      axisPath.setAttribute(
 | 
			
		||||
        "d",
 | 
			
		||||
        "M " + xMin + " " + yMin + " L " + xMin + " " + yMax
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // generate labels
 | 
			
		||||
      for (var i = yMax; i <= yMin; i++) {
 | 
			
		||||
        if ((i - yMin) % pixelsPerTick === 0 && i !== yMin) {
 | 
			
		||||
          var tickGroup = document.createElementNS(
 | 
			
		||||
            "http://www.w3.org/2000/svg",
 | 
			
		||||
            "g"
 | 
			
		||||
          );
 | 
			
		||||
          var gridLine = document.createElementNS(
 | 
			
		||||
            "http://www.w3.org/2000/svg",
 | 
			
		||||
            "path"
 | 
			
		||||
          );
 | 
			
		||||
          text = document.createElementNS("http://www.w3.org/2000/svg", "text");
 | 
			
		||||
          // primitive formatting
 | 
			
		||||
          text.innerHTML = Math.floor(transform(i));
 | 
			
		||||
          text.setAttribute("x", xMin);
 | 
			
		||||
          text.setAttribute("y", i);
 | 
			
		||||
          // offset the text labels to align with grid line and keeping it to the left of the y-axis
 | 
			
		||||
          text.setAttribute("dx", "-.5em");
 | 
			
		||||
          text.setAttribute("dy", ".3em");
 | 
			
		||||
 | 
			
		||||
          gridLine.setAttribute(
 | 
			
		||||
            "d",
 | 
			
		||||
            "M " + xMin + " " + i + " L " + xMax + " " + i
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          tickGroup.appendChild(gridLine);
 | 
			
		||||
          tickGroup.appendChild(text);
 | 
			
		||||
          axisGroup.appendChild(tickGroup);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    axisGroup.appendChild(axisPath);
 | 
			
		||||
    parent.appendChild(axisGroup);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Renders a line
 | 
			
		||||
   */
 | 
			
		||||
  function lineRenderer(xAccessor, yAccessor, xTransform, yTransform) {
 | 
			
		||||
    var line = document.createElementNS("http://www.w3.org/2000/svg", "path");
 | 
			
		||||
 | 
			
		||||
    xAccessor.reset();
 | 
			
		||||
    yAccessor.reset();
 | 
			
		||||
    if (!xAccessor.hasNext() || !yAccessor.hasNext()) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var pathString =
 | 
			
		||||
      "M " + xTransform(xAccessor.next()) + " " + yTransform(yAccessor.next());
 | 
			
		||||
    while (xAccessor.hasNext() && yAccessor.hasNext()) {
 | 
			
		||||
      pathString +=
 | 
			
		||||
        " L " +
 | 
			
		||||
        xTransform(xAccessor.next()) +
 | 
			
		||||
        " " +
 | 
			
		||||
        yTransform(yAccessor.next());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line.setAttribute("class", "series");
 | 
			
		||||
    line.setAttribute("d", pathString);
 | 
			
		||||
 | 
			
		||||
    parent.appendChild(line);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Renders data point circles + text labels
 | 
			
		||||
   */
 | 
			
		||||
  function pointRenderer(xAccessor, yAccessor, xTransform, yTransform) {
 | 
			
		||||
    var pointGroup = document.createElementNS(
 | 
			
		||||
      "http://www.w3.org/2000/svg",
 | 
			
		||||
      "g"
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    pointGroup.setAttribute("class", "data-points");
 | 
			
		||||
 | 
			
		||||
    xAccessor.reset();
 | 
			
		||||
    yAccessor.reset();
 | 
			
		||||
    if (!xAccessor.hasNext() || !yAccessor.hasNext()) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (xAccessor.hasNext() && yAccessor.hasNext()) {
 | 
			
		||||
      var xDataValue = xAccessor.next();
 | 
			
		||||
      var x = xTransform(xDataValue);
 | 
			
		||||
      var yDataValue = yAccessor.next();
 | 
			
		||||
      var y = yTransform(yDataValue);
 | 
			
		||||
 | 
			
		||||
      var circle = document.createElementNS(
 | 
			
		||||
        "http://www.w3.org/2000/svg",
 | 
			
		||||
        "circle"
 | 
			
		||||
      );
 | 
			
		||||
      circle.setAttribute("cx", x);
 | 
			
		||||
      circle.setAttribute("cy", y);
 | 
			
		||||
      circle.setAttribute("r", "4");
 | 
			
		||||
 | 
			
		||||
      var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
 | 
			
		||||
      // primitive formatting
 | 
			
		||||
      text.innerHTML = Math.floor(xDataValue) + " / " + Math.floor(yDataValue);
 | 
			
		||||
      text.setAttribute("x", x);
 | 
			
		||||
      text.setAttribute("y", y);
 | 
			
		||||
 | 
			
		||||
      text.setAttribute("dx", "1em");
 | 
			
		||||
      text.setAttribute("dy", "-.7em");
 | 
			
		||||
 | 
			
		||||
      pointGroup.appendChild(circle);
 | 
			
		||||
      pointGroup.appendChild(text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parent.appendChild(pointGroup);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // perform the rendering
 | 
			
		||||
  xTransform = numericTransformer(
 | 
			
		||||
    xAccessor.min(),
 | 
			
		||||
    xAccessor.max(),
 | 
			
		||||
    0 + gutter,
 | 
			
		||||
    width - gutter
 | 
			
		||||
  );
 | 
			
		||||
  // NOTE: for y... have to reverse coordinate space
 | 
			
		||||
  yTransform = numericTransformer(
 | 
			
		||||
    yAccessor.min(),
 | 
			
		||||
    yAccessor.max(),
 | 
			
		||||
    height - gutter,
 | 
			
		||||
    0 + gutter
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  axisRenderer("x", xTransform.toData);
 | 
			
		||||
  axisRenderer("y", yTransform.toData);
 | 
			
		||||
 | 
			
		||||
  lineRenderer(xAccessor, yAccessor, xTransform.toCoord, yTransform.toCoord);
 | 
			
		||||
  pointRenderer(xAccessor, yAccessor, xTransform.toCoord, yTransform.toCoord);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Final render function
 | 
			
		||||
function renderGraphSvg(dataArray, renderId) {
 | 
			
		||||
  var figure = document.getElementById(renderId);
 | 
			
		||||
  while (figure.hasChildNodes()) {
 | 
			
		||||
    figure.removeChild(figure.lastChild);
 | 
			
		||||
  }
 | 
			
		||||
  //console.log(dataArray);
 | 
			
		||||
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
 | 
			
		||||
  svg.setAttribute("viewBox", "0 0 640 440");
 | 
			
		||||
  svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
 | 
			
		||||
 | 
			
		||||
  lineGraph(
 | 
			
		||||
    svg,
 | 
			
		||||
    // time accessor
 | 
			
		||||
    (function(data, min, max) {
 | 
			
		||||
      var i = 0;
 | 
			
		||||
      return {
 | 
			
		||||
        hasNext: function() {
 | 
			
		||||
          return i < data.length;
 | 
			
		||||
        },
 | 
			
		||||
        next: function() {
 | 
			
		||||
          return data[i++].x;
 | 
			
		||||
        },
 | 
			
		||||
        reset: function() {
 | 
			
		||||
          i = 0;
 | 
			
		||||
        },
 | 
			
		||||
        min: function() {
 | 
			
		||||
          return min;
 | 
			
		||||
        },
 | 
			
		||||
        max: function() {
 | 
			
		||||
          return max;
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    })(
 | 
			
		||||
      dataArray,
 | 
			
		||||
      Math.min.apply(
 | 
			
		||||
        Math,
 | 
			
		||||
        dataArray.map(function(o) {
 | 
			
		||||
          return o.x;
 | 
			
		||||
        })
 | 
			
		||||
      ),
 | 
			
		||||
      Math.max.apply(
 | 
			
		||||
        Math,
 | 
			
		||||
        dataArray.map(function(o) {
 | 
			
		||||
          return o.x;
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    ),
 | 
			
		||||
    // value accessor
 | 
			
		||||
    (function(data, min, max) {
 | 
			
		||||
      var i = 0;
 | 
			
		||||
      return {
 | 
			
		||||
        hasNext: function() {
 | 
			
		||||
          return i < data.length;
 | 
			
		||||
        },
 | 
			
		||||
        next: function() {
 | 
			
		||||
          return data[i++].y;
 | 
			
		||||
        },
 | 
			
		||||
        reset: function() {
 | 
			
		||||
          i = 0;
 | 
			
		||||
        },
 | 
			
		||||
        min: function() {
 | 
			
		||||
          return min;
 | 
			
		||||
        },
 | 
			
		||||
        max: function() {
 | 
			
		||||
          return max;
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    })(
 | 
			
		||||
      dataArray,
 | 
			
		||||
      Math.min.apply(
 | 
			
		||||
        Math,
 | 
			
		||||
        dataArray.map(function(o) {
 | 
			
		||||
          return o.y;
 | 
			
		||||
        })
 | 
			
		||||
      ),
 | 
			
		||||
      Math.max.apply(
 | 
			
		||||
        Math,
 | 
			
		||||
        dataArray.map(function(o) {
 | 
			
		||||
          return o.y;
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  figure.appendChild(svg);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								data/js/graph.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								data/js/graph.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};}
 | 
			
		||||
function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%pixelsPerTick===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}}
 | 
			
		||||
axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);}
 | 
			
		||||
function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
 | 
			
		||||
var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+
 | 
			
		||||
xTransform(xAccessor.next())+
 | 
			
		||||
" "+
 | 
			
		||||
yTransform(yAccessor.next());}
 | 
			
		||||
line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);}
 | 
			
		||||
function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
 | 
			
		||||
while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);}
 | 
			
		||||
parent.appendChild(pointGroup);}
 | 
			
		||||
xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);}
 | 
			
		||||
function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);}
 | 
			
		||||
var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);}
 | 
			
		||||
							
								
								
									
										122
									
								
								data/js/slider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								data/js/slider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
/* -----------------------------------------------------
 | 
			
		||||
  Material Design Sliders
 | 
			
		||||
  CodePen URL: https://codepen.io/rkchauhan/pen/xVGGpR
 | 
			
		||||
  By: Ravikumar Chauhan
 | 
			
		||||
-------------------------------------------------------- */
 | 
			
		||||
function rkmd_rangeSlider(selector) {
 | 
			
		||||
  var self, slider_width, slider_offset, curnt, sliderDiscrete, range, slider;
 | 
			
		||||
  self = $(selector);
 | 
			
		||||
  slider_width = self.width();
 | 
			
		||||
  slider_offset = self.offset().left;
 | 
			
		||||
  sliderDiscrete = self;
 | 
			
		||||
 | 
			
		||||
  sliderDiscrete.each(function(i, v) {
 | 
			
		||||
    curnt = $(this);
 | 
			
		||||
    curnt.append(sliderDiscrete_tmplt());
 | 
			
		||||
    range = curnt.find('input[type="range"]');
 | 
			
		||||
    slider = curnt.find(".slider");
 | 
			
		||||
    slider_fill = slider.find(".slider-fill");
 | 
			
		||||
    slider_handle = slider.find(".slider-handle");
 | 
			
		||||
    slider_label = slider.find(".slider-label");
 | 
			
		||||
 | 
			
		||||
    var range_val = parseInt(range.val());
 | 
			
		||||
    slider_fill.css("width", range_val + "%");
 | 
			
		||||
    slider_handle.css("left", range_val + "%");
 | 
			
		||||
    slider_label.find("span").text(range_val);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  self.on("mousedown touchstart", ".slider-handle", function(e) {
 | 
			
		||||
    if (e.button === 2) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    var parents = $(this).parents(".rkmd-slider");
 | 
			
		||||
    var slider_width = parents.width();
 | 
			
		||||
    var slider_offset = parents.offset().left;
 | 
			
		||||
    var check_range = parents.find('input[type="range"]').is(":disabled");
 | 
			
		||||
    if (check_range === true) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    $(this).addClass("is-active");
 | 
			
		||||
    var moveFu = function(e) {
 | 
			
		||||
      var pageX = e.pageX || e.changedTouches[0].pageX;
 | 
			
		||||
      var slider_new_width = pageX - slider_offset;
 | 
			
		||||
      if (slider_new_width <= slider_width && !(slider_new_width < "0")) {
 | 
			
		||||
        slider_move(parents, slider_new_width, slider_width, true);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    var upFu = function(e) {
 | 
			
		||||
      $(this).off(handlers);
 | 
			
		||||
      parents.find(".is-active").removeClass("is-active");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var handlers = {
 | 
			
		||||
      mousemove: moveFu,
 | 
			
		||||
      touchmove: moveFu,
 | 
			
		||||
      mouseup: upFu,
 | 
			
		||||
      touchend: upFu
 | 
			
		||||
    };
 | 
			
		||||
    $(document).on(handlers);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  self.on("mousedown touchstart", ".slider", function(e) {
 | 
			
		||||
    if (e.button === 2) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var parents = $(this).parents(".rkmd-slider");
 | 
			
		||||
    var slider_width = parents.width();
 | 
			
		||||
    var slider_offset = parents.offset().left;
 | 
			
		||||
    var check_range = parents.find('input[type="range"]').is(":disabled");
 | 
			
		||||
 | 
			
		||||
    if (check_range === true) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var slider_new_width = e.pageX - slider_offset;
 | 
			
		||||
    if (slider_new_width <= slider_width && !(slider_new_width < "0")) {
 | 
			
		||||
      slider_move(parents, slider_new_width, slider_width, true);
 | 
			
		||||
    }
 | 
			
		||||
    var upFu = function(e) {
 | 
			
		||||
      $(this).off(handlers);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var handlers = {
 | 
			
		||||
      mouseup: upFu,
 | 
			
		||||
      touchend: upFu
 | 
			
		||||
    };
 | 
			
		||||
    $(document).on(handlers);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sliderDiscrete_tmplt() {
 | 
			
		||||
  var tmplt =
 | 
			
		||||
    '<div class="slider">' +
 | 
			
		||||
    '<div class="slider-fill"></div>' +
 | 
			
		||||
    '<div class="slider-handle"><div class="slider-label"><span>0</span></div></div>' +
 | 
			
		||||
    "</div>";
 | 
			
		||||
 | 
			
		||||
  return tmplt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function slider_move(parents, newW, sliderW, send) {
 | 
			
		||||
  var slider_new_val = parseInt(Math.round((newW / sliderW) * 100));
 | 
			
		||||
 | 
			
		||||
  var slider_fill = parents.find(".slider-fill");
 | 
			
		||||
  var slider_handle = parents.find(".slider-handle");
 | 
			
		||||
  var range = parents.find('input[type="range"]');
 | 
			
		||||
 | 
			
		||||
  slider_fill.css("width", slider_new_val + "%");
 | 
			
		||||
  slider_handle.css({
 | 
			
		||||
    left: slider_new_val + "%",
 | 
			
		||||
    transition: "none",
 | 
			
		||||
    "-webkit-transition": "none",
 | 
			
		||||
    "-moz-transition": "none"
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  range.val(slider_new_val);
 | 
			
		||||
  if (parents.find(".slider-handle span").text() != slider_new_val) {
 | 
			
		||||
    parents.find(".slider-handle span").text(slider_new_val);
 | 
			
		||||
    var number = parents.attr("id").substring(2);
 | 
			
		||||
    if (send) websock.send("slvalue:" + slider_new_val + ":" + number);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								data/js/slider.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								data/js/slider.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
function rkmd_rangeSlider(selector){var self,slider_width,slider_offset,curnt,sliderDiscrete,range,slider;self=$(selector);slider_width=self.width();slider_offset=self.offset().left;sliderDiscrete=self;sliderDiscrete.each(function(i,v){curnt=$(this);curnt.append(sliderDiscrete_tmplt());range=curnt.find('input[type="range"]');slider=curnt.find(".slider");slider_fill=slider.find(".slider-fill");slider_handle=slider.find(".slider-handle");slider_label=slider.find(".slider-label");var range_val=parseInt(range.val());slider_fill.css("width",range_val+"%");slider_handle.css("left",range_val+"%");slider_label.find("span").text(range_val);});self.on("mousedown touchstart",".slider-handle",function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents(".rkmd-slider");var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(":disabled");if(check_range===true){return false;}
 | 
			
		||||
$(this).addClass("is-active");var moveFu=function(e){var pageX=e.pageX||e.changedTouches[0].pageX;var slider_new_width=pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<"0")){slider_move(parents,slider_new_width,slider_width,true);}};var upFu=function(e){$(this).off(handlers);parents.find(".is-active").removeClass("is-active");};var handlers={mousemove:moveFu,touchmove:moveFu,mouseup:upFu,touchend:upFu};$(document).on(handlers);});self.on("mousedown touchstart",".slider",function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents(".rkmd-slider");var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(":disabled");if(check_range===true){return false;}
 | 
			
		||||
var slider_new_width=e.pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<"0")){slider_move(parents,slider_new_width,slider_width,true);}
 | 
			
		||||
var upFu=function(e){$(this).off(handlers);};var handlers={mouseup:upFu,touchend:upFu};$(document).on(handlers);});}
 | 
			
		||||
function sliderDiscrete_tmplt(){var tmplt='<div class="slider">'+
 | 
			
		||||
'<div class="slider-fill"></div>'+
 | 
			
		||||
'<div class="slider-handle"><div class="slider-label"><span>0</span></div></div>'+
 | 
			
		||||
"</div>";return tmplt;}
 | 
			
		||||
function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round((newW/sliderW)*100));var slider_fill=parents.find(".slider-fill");var slider_handle=parents.find(".slider-handle");var range=parents.find('input[type="range"]');slider_fill.css("width",slider_new_val+"%");slider_handle.css({left:slider_new_val+"%",transition:"none","-webkit-transition":"none","-moz-transition":"none"});range.val(slider_new_val);if(parents.find(".slider-handle span").text()!=slider_new_val){parents.find(".slider-handle span").text(slider_new_val);var number=parents.attr("id").substring(2);if(send)websock.send("slvalue:"+slider_new_val+":"+number);}}
 | 
			
		||||
							
								
								
									
										351
									
								
								data/js/tabbedcontent.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								data/js/tabbedcontent.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Tabs plugin for jQuery created by Òscar Casajuana < elboletaire at underave dot net >
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright 2013-2016 Òscar Casajuana
 | 
			
		||||
 * @license   MIT
 | 
			
		||||
 * @author    Òscar Casajuana Alonso <elboletaire at underave dot net>
 | 
			
		||||
*/
 | 
			
		||||
;(function($, document, window, undefined) {
 | 
			
		||||
  "use strict";
 | 
			
		||||
 | 
			
		||||
  var Tabbedcontent = function(tabcontent, options) {
 | 
			
		||||
    var defaults = {
 | 
			
		||||
        links         : tabcontent.prev().find('a').length ? tabcontent.prev().find('a') : '.tabs a', // the tabs itself. By default it selects the links contained in the previous wrapper or the links inside ".tabs a" if there's no previous item
 | 
			
		||||
        errorSelector : '.error-message', // false to disable
 | 
			
		||||
        speed         : false, // speed of the show effect. Set to null or false to disable
 | 
			
		||||
        onSwitch      : false, // onSwitch callback
 | 
			
		||||
        onInit        : false, // onInit callback
 | 
			
		||||
        currentClass  : 'active', // current selected tab class (is set to the <a> element)
 | 
			
		||||
        tabErrorClass : 'has-errors', // a class to be added to the tab where errorSelector is detected
 | 
			
		||||
        history       : true, // set to false to disable HTML5 history
 | 
			
		||||
        historyOnInit : true, // allows to deactivate the history for the intial autmatically tab switch on load
 | 
			
		||||
        loop          : false // if set to true will loop between tabs when using the next() and prev() api methods
 | 
			
		||||
      },
 | 
			
		||||
      firstTime = false,
 | 
			
		||||
      children  = tabcontent.children(),
 | 
			
		||||
      history   = window.history,
 | 
			
		||||
      loc       = document.location,
 | 
			
		||||
      current   = null
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    options = $.extend(defaults, options);
 | 
			
		||||
 | 
			
		||||
    if (!(options.links instanceof $)) {
 | 
			
		||||
      options.links = $(options.links);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the specified tab id exists.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string tab Tab #id
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    function tabExists(tab) {
 | 
			
		||||
      return Boolean(children.filter(tab).length);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the current tab is the
 | 
			
		||||
     * first one in the tabs set.
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    function isFirst() {
 | 
			
		||||
      return current === 0;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the passed number is an integer.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed  num The value to be checked.
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    function isInt(num) {
 | 
			
		||||
      return num % 1 === 0;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the current tab is the
 | 
			
		||||
     * last one in the tabs set.
 | 
			
		||||
     *
 | 
			
		||||
     * @return {Boolean} [description]
 | 
			
		||||
     */
 | 
			
		||||
    function isLast() {
 | 
			
		||||
      return current === children.length - 1;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Filters a tab based on current links href.
 | 
			
		||||
     *
 | 
			
		||||
     * Method for compatibility with Zepto.js
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string tab Tab #href
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    function filterTab(tab) {
 | 
			
		||||
      return $(this).attr('href').match(new RegExp(tab + '$'));
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an object containing two jQuery instances:
 | 
			
		||||
     * one for the tab content and the other for its link.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed tab A tab id, #id or index.
 | 
			
		||||
     * @return object    With thi
 | 
			
		||||
     */
 | 
			
		||||
    function getTab(tab) {
 | 
			
		||||
      if (tab instanceof $) {
 | 
			
		||||
        return {
 | 
			
		||||
          tab : tab,
 | 
			
		||||
          link : options.links.eq(tab.index())
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      if (isInt(tab)) {
 | 
			
		||||
        return {
 | 
			
		||||
          tab : children.eq(tab),
 | 
			
		||||
          link : options.links.eq(tab)
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      if (children.filter(tab).length) {
 | 
			
		||||
        return {
 | 
			
		||||
          tab : children.filter(tab),
 | 
			
		||||
          link : options.links.filter(function() {
 | 
			
		||||
            return filterTab.apply(this, [tab]);
 | 
			
		||||
          })
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      // assume it's an id without #
 | 
			
		||||
      return {
 | 
			
		||||
        tab : children.filter('#' + tab),
 | 
			
		||||
        link : options.links.filter(function() {
 | 
			
		||||
          return filterTab.apply(this, ['#' + tab]);
 | 
			
		||||
        })
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the index of the current tab.
 | 
			
		||||
     *
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    function getCurrent() {
 | 
			
		||||
      return options.links.parent().filter('.' + options.currentClass).index();
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to the next tab in the tabs set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  bool  loop If defined will overwrite options.loop
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    function next(loop) {
 | 
			
		||||
      ++current;
 | 
			
		||||
 | 
			
		||||
      if (loop === undefined) loop = options.loop;
 | 
			
		||||
 | 
			
		||||
      if (current < children.length) {
 | 
			
		||||
        return switchTab(current, true);
 | 
			
		||||
      } else if (loop && current >= children.length) {
 | 
			
		||||
        return switchTab(0, true);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to the previous tab in the tabs set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  bool  loop If defined will overwrite options.loop
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    function prev(loop) {
 | 
			
		||||
      --current;
 | 
			
		||||
 | 
			
		||||
      if (loop === undefined) loop = options.loop;
 | 
			
		||||
 | 
			
		||||
      if (current >= 0) {
 | 
			
		||||
        return switchTab(current, true);
 | 
			
		||||
      } else if (loop && current < 0) {
 | 
			
		||||
        return switchTab(children.length - 1, true);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * onSwitch callback for switchTab.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string tab The tab #id
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    function onSwitch(tab) {
 | 
			
		||||
      if (options.history && options.historyOnInit && firstTime && history !== undefined && ('pushState' in history)) {
 | 
			
		||||
        firstTime = false;
 | 
			
		||||
        window.setTimeout(function() {
 | 
			
		||||
          history.replaceState(null, '', tab);
 | 
			
		||||
        }, 100);
 | 
			
		||||
      }
 | 
			
		||||
      current = getCurrent();
 | 
			
		||||
      if (options.onSwitch && typeof options.onSwitch === 'function') {
 | 
			
		||||
        options.onSwitch(tab, api());
 | 
			
		||||
      }
 | 
			
		||||
      tabcontent.trigger('tabcontent.switch', [tab, api()]);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Switch to specified tab.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed tab The tab to switch to.
 | 
			
		||||
     * @param  bool  api Set to true to force history writing.
 | 
			
		||||
     * @return bool      Returns false if tab does not exist; true otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    function switchTab(tab, api) {
 | 
			
		||||
      if (!tab.toString().match(/^#/)) {
 | 
			
		||||
        tab = '#' + getTab(tab).tab.attr('id');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!tabExists(tab)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Toggle active class
 | 
			
		||||
      options.links.attr('aria-selected','false').parent().removeClass(options.currentClass);
 | 
			
		||||
      options.links.filter(function() {
 | 
			
		||||
        return filterTab.apply(this, [tab]);
 | 
			
		||||
      }).attr('aria-selected','true').parent().addClass(options.currentClass);
 | 
			
		||||
      // Hide tabs
 | 
			
		||||
      children.hide();
 | 
			
		||||
 | 
			
		||||
      // We need to force the change of the hash if we're using the API
 | 
			
		||||
      if (options.history && api) {
 | 
			
		||||
        if (history !== undefined && ('pushState' in history)) {
 | 
			
		||||
          history.pushState(null, '', tab);
 | 
			
		||||
        } else {
 | 
			
		||||
          // force hash change to add it to the history
 | 
			
		||||
          window.location.hash = tab;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Show tabs
 | 
			
		||||
      children.attr('aria-hidden','true').filter(tab).show(options.speed, function() {
 | 
			
		||||
        if (options.speed) {
 | 
			
		||||
          onSwitch(tab);
 | 
			
		||||
        }
 | 
			
		||||
      }).attr('aria-hidden','false');
 | 
			
		||||
      if (!options.speed) {
 | 
			
		||||
        onSwitch(tab);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Api method to switch tabs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed tab Tab to switch to.
 | 
			
		||||
     * @return bool      Returns false if tab does not exist; true otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    function apiSwitch(tab) {
 | 
			
		||||
      return switchTab(tab, true);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Method used to switch tabs using the
 | 
			
		||||
     * browser query hash.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  object e Event.
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    function hashSwitch(e) {
 | 
			
		||||
      switchTab(loc.hash);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialization method.
 | 
			
		||||
     *
 | 
			
		||||
     * The tab checking preference is:
 | 
			
		||||
     *   - document.location.hash
 | 
			
		||||
     *   - options.errorSelector
 | 
			
		||||
     *   - first tab in the set of tabs
 | 
			
		||||
     *
 | 
			
		||||
     * The onInit method is called at the
 | 
			
		||||
     * end of this method.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    function init() {
 | 
			
		||||
      // Switch to tab using location.hash
 | 
			
		||||
      if (tabExists(loc.hash)) {
 | 
			
		||||
        // Switch to current hash tab
 | 
			
		||||
        switchTab(loc.hash);
 | 
			
		||||
      }
 | 
			
		||||
      // If there's a tab link with the options.currentClass set,
 | 
			
		||||
      // switch to that tab.
 | 
			
		||||
      else if (options.links.parent().filter('.' + options.currentClass).length) {
 | 
			
		||||
        switchTab(options.links.parent().filter('.' + options.currentClass).index());
 | 
			
		||||
      }
 | 
			
		||||
      // Switch to tab containing class options.errorSelector
 | 
			
		||||
      else if (options.errorSelector && children.find(options.errorSelector).length) {
 | 
			
		||||
        // Search for errors and show first tab containing one
 | 
			
		||||
        children.each(function() {
 | 
			
		||||
          if ($(this).find(options.errorSelector).length) {
 | 
			
		||||
            switchTab("#" + $(this).attr("id"));
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      // Open first tab
 | 
			
		||||
      else {
 | 
			
		||||
        switchTab("#" + children.filter(":first-child").attr("id"));
 | 
			
		||||
      }
 | 
			
		||||
      // Add a class to every tab containing errors
 | 
			
		||||
      if (options.errorSelector) {
 | 
			
		||||
        children.find(options.errorSelector).each(function() {
 | 
			
		||||
          var tab = getTab($(this).parent());
 | 
			
		||||
          tab.link.parent().addClass(options.tabErrorClass);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Binding
 | 
			
		||||
      if ('onhashchange' in window) {
 | 
			
		||||
        $(window).bind('hashchange', hashSwitch);
 | 
			
		||||
      } else { // old browsers
 | 
			
		||||
        var current_href = loc.href;
 | 
			
		||||
        window.setInterval(function() {
 | 
			
		||||
          if (current_href !== loc.href) {
 | 
			
		||||
            hashSwitch.call(window.event);
 | 
			
		||||
            current_href = loc.href;
 | 
			
		||||
          }
 | 
			
		||||
        }, 100);
 | 
			
		||||
      }
 | 
			
		||||
      // Bind click event on links, to ensure we don't rewrite the URI in
 | 
			
		||||
      // case history is disabled
 | 
			
		||||
      $(options.links).on('click', function(e) {
 | 
			
		||||
        switchTab($(this).attr('href').replace(/^[^#]+/, ''), options.history);
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // onInit callback
 | 
			
		||||
      if (options.onInit && typeof options.onInit === 'function') {
 | 
			
		||||
        options.onInit(api());
 | 
			
		||||
      }
 | 
			
		||||
      tabcontent.trigger('tabcontent.init', [api()]);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the methods exposed in the api.
 | 
			
		||||
     *
 | 
			
		||||
     * @return object Containing each api method.
 | 
			
		||||
     */
 | 
			
		||||
    function api() {
 | 
			
		||||
      return {
 | 
			
		||||
        'switch'       : apiSwitch,
 | 
			
		||||
        'switchTab'    : apiSwitch, // for old browsers
 | 
			
		||||
        'getCurrent'   : getCurrent,
 | 
			
		||||
        'getTab'       : getTab,
 | 
			
		||||
        'next'         : next,
 | 
			
		||||
        'prev'         : prev,
 | 
			
		||||
        'isFirst'      : isFirst,
 | 
			
		||||
        'isLast'       : isLast
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init();
 | 
			
		||||
 | 
			
		||||
    return api();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $.fn.tabbedContent = function(options) {
 | 
			
		||||
    return this.each(function() {
 | 
			
		||||
      var tabs = new Tabbedcontent($(this), options);
 | 
			
		||||
      $(this).data('api', tabs);
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
})(window.jQuery || window.Zepto || window.$, document, window);
 | 
			
		||||
							
								
								
									
										35
									
								
								data/js/tabbedcontent.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								data/js/tabbedcontent.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
;(function($,document,window,undefined){"use strict";var Tabbedcontent=function(tabcontent,options){var defaults={links:tabcontent.prev().find('a').length?tabcontent.prev().find('a'):'.tabs a',errorSelector:'.error-message',speed:false,onSwitch:false,onInit:false,currentClass:'active',tabErrorClass:'has-errors',history:true,historyOnInit:true,loop:false},firstTime=false,children=tabcontent.children(),history=window.history,loc=document.location,current=null;options=$.extend(defaults,options);if(!(options.links instanceof $)){options.links=$(options.links);}
 | 
			
		||||
function tabExists(tab){return Boolean(children.filter(tab).length);}
 | 
			
		||||
function isFirst(){return current===0;}
 | 
			
		||||
function isInt(num){return num%1===0;}
 | 
			
		||||
function isLast(){return current===children.length-1;}
 | 
			
		||||
function filterTab(tab){return $(this).attr('href').match(new RegExp(tab+'$'));}
 | 
			
		||||
function getTab(tab){if(tab instanceof $){return{tab:tab,link:options.links.eq(tab.index())};}
 | 
			
		||||
if(isInt(tab)){return{tab:children.eq(tab),link:options.links.eq(tab)};}
 | 
			
		||||
if(children.filter(tab).length){return{tab:children.filter(tab),link:options.links.filter(function(){return filterTab.apply(this,[tab]);})};}
 | 
			
		||||
return{tab:children.filter('#'+tab),link:options.links.filter(function(){return filterTab.apply(this,['#'+tab]);})};}
 | 
			
		||||
function getCurrent(){return options.links.parent().filter('.'+options.currentClass).index();}
 | 
			
		||||
function next(loop){++current;if(loop===undefined)loop=options.loop;if(current<children.length){return switchTab(current,true);}else if(loop&¤t>=children.length){return switchTab(0,true);}
 | 
			
		||||
return false;}
 | 
			
		||||
function prev(loop){--current;if(loop===undefined)loop=options.loop;if(current>=0){return switchTab(current,true);}else if(loop&¤t<0){return switchTab(children.length-1,true);}
 | 
			
		||||
return false;}
 | 
			
		||||
function onSwitch(tab){if(options.history&&options.historyOnInit&&firstTime&&history!==undefined&&('pushState'in history)){firstTime=false;window.setTimeout(function(){history.replaceState(null,'',tab);},100);}
 | 
			
		||||
current=getCurrent();if(options.onSwitch&&typeof options.onSwitch==='function'){options.onSwitch(tab,api());}
 | 
			
		||||
tabcontent.trigger('tabcontent.switch',[tab,api()]);}
 | 
			
		||||
function switchTab(tab,api){if(!tab.toString().match(/^#/)){tab='#'+getTab(tab).tab.attr('id');}
 | 
			
		||||
if(!tabExists(tab)){return false;}
 | 
			
		||||
options.links.attr('aria-selected','false').parent().removeClass(options.currentClass);options.links.filter(function(){return filterTab.apply(this,[tab]);}).attr('aria-selected','true').parent().addClass(options.currentClass);children.hide();if(options.history&&api){if(history!==undefined&&('pushState'in history)){history.pushState(null,'',tab);}else{window.location.hash=tab;}}
 | 
			
		||||
children.attr('aria-hidden','true').filter(tab).show(options.speed,function(){if(options.speed){onSwitch(tab);}}).attr('aria-hidden','false');if(!options.speed){onSwitch(tab);}
 | 
			
		||||
return true;}
 | 
			
		||||
function apiSwitch(tab){return switchTab(tab,true);}
 | 
			
		||||
function hashSwitch(e){switchTab(loc.hash);}
 | 
			
		||||
function init(){if(tabExists(loc.hash)){switchTab(loc.hash);}
 | 
			
		||||
else if(options.links.parent().filter('.'+options.currentClass).length){switchTab(options.links.parent().filter('.'+options.currentClass).index());}
 | 
			
		||||
else if(options.errorSelector&&children.find(options.errorSelector).length){children.each(function(){if($(this).find(options.errorSelector).length){switchTab("#"+$(this).attr("id"));return false;}});}
 | 
			
		||||
else{switchTab("#"+children.filter(":first-child").attr("id"));}
 | 
			
		||||
if(options.errorSelector){children.find(options.errorSelector).each(function(){var tab=getTab($(this).parent());tab.link.parent().addClass(options.tabErrorClass);});}
 | 
			
		||||
if('onhashchange'in window){$(window).bind('hashchange',hashSwitch);}else{var current_href=loc.href;window.setInterval(function(){if(current_href!==loc.href){hashSwitch.call(window.event);current_href=loc.href;}},100);}
 | 
			
		||||
$(options.links).on('click',function(e){switchTab($(this).attr('href').replace(/^[^#]+/,''),options.history);e.preventDefault();});if(options.onInit&&typeof options.onInit==='function'){options.onInit(api());}
 | 
			
		||||
tabcontent.trigger('tabcontent.init',[api()]);}
 | 
			
		||||
function api(){return{'switch':apiSwitch,'switchTab':apiSwitch,'getCurrent':getCurrent,'getTab':getTab,'next':next,'prev':prev,'isFirst':isFirst,'isLast':isLast};}
 | 
			
		||||
init();return api();};$.fn.tabbedContent=function(options){return this.each(function(){var tabs=new Tabbedcontent($(this),options);$(this).data('api',tabs);});};})(window.jQuery||window.Zepto||window.$,document,window);
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/ui_graph.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_graph.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 64 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/ui_number.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_number.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 21 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/ui_select1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_select1.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 54 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/ui_select2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_select2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/ui_tabs.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_tabs.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/ui_text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/ui_text.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 21 KiB  | 
							
								
								
									
										262
									
								
								examples/gui-generic-api/gui-generic-api.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								examples/gui-generic-api/gui-generic-api.ino
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <ESPUI.h>
 | 
			
		||||
 | 
			
		||||
const byte DNS_PORT = 53;
 | 
			
		||||
IPAddress apIP(192, 168, 1, 1);
 | 
			
		||||
DNSServer dnsServer;
 | 
			
		||||
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char *ssid = "ESPUI";
 | 
			
		||||
const char *password = "espui";
 | 
			
		||||
const char *hostname = "espui";
 | 
			
		||||
 | 
			
		||||
uint16_t status;
 | 
			
		||||
uint16_t button1;
 | 
			
		||||
uint16_t millisLabelId;
 | 
			
		||||
uint16_t switchOne;
 | 
			
		||||
 | 
			
		||||
void numberCall( Control* sender, int type ) {
 | 
			
		||||
  Serial.println( sender->value );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void textCall(Control *sender, int type) {
 | 
			
		||||
  Serial.print("Text: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println(sender->value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void slider(Control *sender, int type) {
 | 
			
		||||
  Serial.print("Slider: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println(sender->value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonCallback(Control *sender, int type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
  case B_DOWN:
 | 
			
		||||
    Serial.println("Button DOWN");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case B_UP:
 | 
			
		||||
    Serial.println("Button UP");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonExample(Control *sender, int type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
  case B_DOWN:
 | 
			
		||||
    Serial.println("Status: Start");
 | 
			
		||||
    ESPUI.updateControlValue(status, "Start");
 | 
			
		||||
 | 
			
		||||
    ESPUI.getControl(button1)->color = ControlColor::Carrot;
 | 
			
		||||
    ESPUI.updateControl(button1);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case B_UP:
 | 
			
		||||
    Serial.println("Status: Stop");
 | 
			
		||||
    ESPUI.updateControlValue(status, "Stop");
 | 
			
		||||
 | 
			
		||||
    ESPUI.getControl(button1)->color = ControlColor::Peterriver;
 | 
			
		||||
    ESPUI.updateControl(button1);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void padExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
  case P_LEFT_DOWN:
 | 
			
		||||
    Serial.print("left down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_LEFT_UP:
 | 
			
		||||
    Serial.print("left up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_RIGHT_DOWN:
 | 
			
		||||
    Serial.print("right down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_RIGHT_UP:
 | 
			
		||||
    Serial.print("right up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_FOR_DOWN:
 | 
			
		||||
    Serial.print("for down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_FOR_UP:
 | 
			
		||||
    Serial.print("for up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_BACK_DOWN:
 | 
			
		||||
    Serial.print("back down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_BACK_UP:
 | 
			
		||||
    Serial.print("back up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_CENTER_DOWN:
 | 
			
		||||
    Serial.print("center down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_CENTER_UP:
 | 
			
		||||
    Serial.print("center up");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void switchExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
  case S_ACTIVE:
 | 
			
		||||
    Serial.print("Active:");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case S_INACTIVE:
 | 
			
		||||
    Serial.print("Inactive");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void selectExample(Control *sender, int value) {
 | 
			
		||||
  Serial.print("Select: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println(sender->value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void otherSwitchExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
  case S_ACTIVE:
 | 
			
		||||
    Serial.print("Active:");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case S_INACTIVE:
 | 
			
		||||
    Serial.print("Inactive");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setup(void) {
 | 
			
		||||
  ESPUI.setVerbosity(Verbosity::VerboseJSON);
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
  WiFi.setHostname(hostname);
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.hostname(hostname);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // try to connect to existing network
 | 
			
		||||
  WiFi.begin(ssid, password);
 | 
			
		||||
  Serial.print("\n\nTry to connect to existing network");
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    uint8_t timeout = 10;
 | 
			
		||||
 | 
			
		||||
    // Wait for connection, 5s timeout
 | 
			
		||||
    do {
 | 
			
		||||
      delay(500);
 | 
			
		||||
      Serial.print(".");
 | 
			
		||||
      timeout--;
 | 
			
		||||
    } while (timeout && WiFi.status() != WL_CONNECTED);
 | 
			
		||||
 | 
			
		||||
    // not connected -> create hotspot
 | 
			
		||||
    if (WiFi.status() != WL_CONNECTED) {
 | 
			
		||||
      Serial.print("\n\nCreating hotspot");
 | 
			
		||||
 | 
			
		||||
      WiFi.mode(WIFI_AP);
 | 
			
		||||
      WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
 | 
			
		||||
      WiFi.softAP(ssid);
 | 
			
		||||
 | 
			
		||||
      timeout = 5;
 | 
			
		||||
 | 
			
		||||
      do {
 | 
			
		||||
        delay(500);
 | 
			
		||||
        Serial.print(".");
 | 
			
		||||
        timeout--;
 | 
			
		||||
      } while (timeout);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dnsServer.start(DNS_PORT, "*", apIP);
 | 
			
		||||
 | 
			
		||||
  Serial.println("\n\nWiFi parameters:");
 | 
			
		||||
  Serial.print("Mode: ");
 | 
			
		||||
  Serial.println(WiFi.getMode() == WIFI_AP ? "Station" : "Client");
 | 
			
		||||
  Serial.print("IP address: ");
 | 
			
		||||
  Serial.println(WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP());
 | 
			
		||||
 | 
			
		||||
  status = ESPUI.addControl(ControlType::Label, "Status:", "Stop", ControlColor::Turquoise);
 | 
			
		||||
 | 
			
		||||
  uint16_t select1 = ESPUI.addControl(ControlType::Select, "Select:", "", ControlColor::Alizarin, Control::noParent, &selectExample);
 | 
			
		||||
 | 
			
		||||
  ESPUI.addControl(ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1);
 | 
			
		||||
  ESPUI.addControl(ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1);
 | 
			
		||||
  ESPUI.addControl(ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1);
 | 
			
		||||
 | 
			
		||||
  ESPUI.addControl(ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, Control::noParent, &textCall);
 | 
			
		||||
 | 
			
		||||
  millisLabelId = ESPUI.addControl(ControlType::Label, "Millis:", "0", ControlColor::Emerald, Control::noParent);
 | 
			
		||||
  button1 = ESPUI.addControl(ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, Control::noParent, &buttonCallback);
 | 
			
		||||
  ESPUI.addControl(ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, Control::noParent, &buttonExample);
 | 
			
		||||
  ESPUI.addControl(ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, Control::noParent, &padExample);
 | 
			
		||||
  ESPUI.addControl(ControlType::Pad, "Pad without center", "", ControlColor::Carrot, Control::noParent, &padExample);
 | 
			
		||||
  switchOne = ESPUI.addControl(ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, Control::noParent, &switchExample);
 | 
			
		||||
  ESPUI.addControl(ControlType::Switcher, "Switch two", "", ControlColor::None, Control::noParent, &otherSwitchExample);
 | 
			
		||||
  ESPUI.addControl(ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, Control::noParent, &slider);
 | 
			
		||||
  ESPUI.addControl(ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, Control::noParent, &slider);
 | 
			
		||||
  ESPUI.addControl(ControlType::Number, "Number:", "50", ControlColor::Alizarin, Control::noParent, &numberCall);
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * .begin loads and serves all files from PROGMEM directly.
 | 
			
		||||
   * If you want to serve the files from SPIFFS use ESPUI.beginSPIFFS
 | 
			
		||||
   * (.prepareFileSystem has to be run in an empty sketch before)
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  // Enable this option if you want sliders to be continuous (update during move) and not discrete (update on stop)
 | 
			
		||||
  // ESPUI.sliderContinuous = true;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Optionally you can use HTTP BasicAuth. Keep in mind that this is NOT a
 | 
			
		||||
   * SECURE way of limiting access.
 | 
			
		||||
   * Anyone who is able to sniff traffic will be able to intercept your password
 | 
			
		||||
   * since it is transmitted in cleartext. Just add a string as username and
 | 
			
		||||
   * password, for example begin("ESPUI Control", "username", "password")
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ESPUI.begin("ESPUI Control");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void loop(void) {
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
 | 
			
		||||
  static long oldTime = 0;
 | 
			
		||||
  static bool testSwitchState = false;
 | 
			
		||||
 | 
			
		||||
  if (millis() - oldTime > 5000) {
 | 
			
		||||
    ESPUI.updateControlValue(millisLabelId, String(millis()));
 | 
			
		||||
    testSwitchState = !testSwitchState;
 | 
			
		||||
    ESPUI.updateControlValue(switchOne, testSwitchState ? "1" : "0");
 | 
			
		||||
 | 
			
		||||
    oldTime = millis();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/gui/data/css/style.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								examples/gui/data/css/style.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,28 +0,0 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="utf-8">
 | 
			
		||||
  <title>Control</title>
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
  <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC"
 | 
			
		||||
  />
 | 
			
		||||
  <link rel="stylesheet" href="/css/normalize.css">
 | 
			
		||||
  <link rel="stylesheet" href="/css/style.css">
 | 
			
		||||
 | 
			
		||||
  <script src="/js/zepto.min.js"></script>
 | 
			
		||||
  <script src="/js/slider.js"></script>
 | 
			
		||||
  <script src="/js/controls.js"></script>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body onload="javascript:start();">
 | 
			
		||||
  <div>
 | 
			
		||||
    <h4><div id="mainHeader">Control</div> <span id="conStatus" class="label">Offline</span></h4></div>
 | 
			
		||||
  <hr />
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div id="row" class="row u-full-width">
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										487
									
								
								examples/gui/data/js/controls.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										487
									
								
								examples/gui/data/js/controls.js
									
									
									
									
										vendored
									
									
								
							@@ -1,487 +0,0 @@
 | 
			
		||||
const UI_INITIAL_GUI = 100;
 | 
			
		||||
const UI_TITEL = 0;
 | 
			
		||||
 | 
			
		||||
const UI_LABEL = 1;
 | 
			
		||||
const UPDATE_LABEL = 6;
 | 
			
		||||
 | 
			
		||||
const UI_BUTTON = 2;
 | 
			
		||||
 | 
			
		||||
const UI_SWITCHER = 3;
 | 
			
		||||
const UPDATE_SWITCHER = 7;
 | 
			
		||||
 | 
			
		||||
const UI_PAD = 4;
 | 
			
		||||
const UI_CPAD = 5;
 | 
			
		||||
 | 
			
		||||
const UI_SLIDER = 8;
 | 
			
		||||
const UPDATE_SLIDER = 9;
 | 
			
		||||
 | 
			
		||||
const UI_NUMBER = 10;
 | 
			
		||||
const UPDATE_NUMBER = 11;
 | 
			
		||||
 | 
			
		||||
const UI_TEXT_INPUT = 12;
 | 
			
		||||
const UPDATE_TEXT_INPUT = 13;
 | 
			
		||||
 | 
			
		||||
const UI_GRAPH = 14;
 | 
			
		||||
const CLEAR_GRAPH = 15;
 | 
			
		||||
const ADD_GRAPH_POINT = 16;
 | 
			
		||||
 | 
			
		||||
const FOR = 0;
 | 
			
		||||
const BACK = 1;
 | 
			
		||||
const LEFT = 2;
 | 
			
		||||
const RIGHT = 3;
 | 
			
		||||
const CENTER = 4;
 | 
			
		||||
 | 
			
		||||
// Colors
 | 
			
		||||
const C_TURQUOISE = 0;
 | 
			
		||||
const C_EMERALD = 1;
 | 
			
		||||
const C_PETERRIVER = 2;
 | 
			
		||||
const C_WETASPHALT = 3;
 | 
			
		||||
const C_SUNFLOWER = 4;
 | 
			
		||||
const C_CARROT = 5;
 | 
			
		||||
const C_ALIZARIN = 6;
 | 
			
		||||
const C_NONE = 7;
 | 
			
		||||
const C_DARK = 8;
 | 
			
		||||
 | 
			
		||||
function colorClass(colorId) {
 | 
			
		||||
  colorId = Number(colorId);
 | 
			
		||||
  switch (colorId) {
 | 
			
		||||
    case C_TURQUOISE:
 | 
			
		||||
      return "turquoise";
 | 
			
		||||
 | 
			
		||||
    case C_EMERALD:
 | 
			
		||||
      return "emerald";
 | 
			
		||||
 | 
			
		||||
    case C_PETERRIVER:
 | 
			
		||||
      return "peterriver";
 | 
			
		||||
 | 
			
		||||
    case C_WETASPHALT:
 | 
			
		||||
      return "wetasphalt";
 | 
			
		||||
 | 
			
		||||
    case C_SUNFLOWER:
 | 
			
		||||
      return "sunflower";
 | 
			
		||||
 | 
			
		||||
    case C_CARROT:
 | 
			
		||||
      return "carrot";
 | 
			
		||||
 | 
			
		||||
    case C_ALIZARIN:
 | 
			
		||||
      return "alizarin";
 | 
			
		||||
 | 
			
		||||
    case C_NONE:
 | 
			
		||||
      return "dark";
 | 
			
		||||
    default:
 | 
			
		||||
      return "";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var websock;
 | 
			
		||||
var websockConnected = false;
 | 
			
		||||
 | 
			
		||||
function restart() {
 | 
			
		||||
  $(document)
 | 
			
		||||
    .add("*")
 | 
			
		||||
    .off();
 | 
			
		||||
  $("#row").html("");
 | 
			
		||||
  websock.close();
 | 
			
		||||
  start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function conStatusError() {
 | 
			
		||||
  websockConnected = false;
 | 
			
		||||
  $("#conStatus").removeClass("color-green");
 | 
			
		||||
  $("#conStatus").addClass("color-red");
 | 
			
		||||
  $("#conStatus").html("Error / No Connection ↻");
 | 
			
		||||
  $("#conStatus").off();
 | 
			
		||||
  $("#conStatus").on({
 | 
			
		||||
    click: restart
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleVisibilityChange() {
 | 
			
		||||
  if (!websockConnected && !document.hidden) {
 | 
			
		||||
    restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
  document.addEventListener("visibilitychange", handleVisibilityChange, false);
 | 
			
		||||
  websock = new WebSocket("ws://" + window.location.hostname + "/ws");
 | 
			
		||||
  websock.onopen = function(evt) {
 | 
			
		||||
    console.log("websock open");
 | 
			
		||||
    $("#conStatus").addClass("color-green");
 | 
			
		||||
    $("#conStatus").text("Connected");
 | 
			
		||||
    websockConnected = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onclose = function(evt) {
 | 
			
		||||
    console.log("websock close");
 | 
			
		||||
    conStatusError();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onerror = function(evt) {
 | 
			
		||||
    console.log(evt);
 | 
			
		||||
    conStatusError();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var handleEvent = function(evt) {
 | 
			
		||||
    //console.log(evt);
 | 
			
		||||
    var data = JSON.parse(evt.data);
 | 
			
		||||
    var e = document.body;
 | 
			
		||||
    var center = "";
 | 
			
		||||
    switch (data.type) {
 | 
			
		||||
      case UI_INITIAL_GUI:
 | 
			
		||||
        data.controls.forEach(element => {
 | 
			
		||||
          var fauxEvent = {
 | 
			
		||||
            data: JSON.stringify(element)
 | 
			
		||||
          };
 | 
			
		||||
          handleEvent(fauxEvent);
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_TITEL:
 | 
			
		||||
        document.title = data.label;
 | 
			
		||||
        $("#mainHeader").html(data.label);
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_LABEL:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'><h5 id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr /><span id='l" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='label label-wrap'>" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "</span></div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_BUTTON:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='one columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'><h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/><button onmousedown='buttonclick(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='buttonclick(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "</button></div>"
 | 
			
		||||
        );
 | 
			
		||||
        $("#" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            buttonclick(data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            buttonclick(data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_SWITCHER:
 | 
			
		||||
        var label = "<label id='sl" + data.id + "' class='switch checked'>";
 | 
			
		||||
        var input =
 | 
			
		||||
          "<div class='in'><input type='checkbox' id='s" +
 | 
			
		||||
          data.id +
 | 
			
		||||
          "' onClick='switcher(" +
 | 
			
		||||
          data.id +
 | 
			
		||||
          ",null)' checked></div>";
 | 
			
		||||
        if (data.value == "0") {
 | 
			
		||||
          label = "<label id='sl" + data.id + "' class='switch'>";
 | 
			
		||||
          input =
 | 
			
		||||
            "<div class='in'><input type='checkbox' id='s" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' onClick='switcher(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ",null)' ></div>";
 | 
			
		||||
        }
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='one columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'><h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            label +
 | 
			
		||||
            input +
 | 
			
		||||
            "</label>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_CPAD:
 | 
			
		||||
        center =
 | 
			
		||||
          "<a class='confirm' onmousedown='padclick(CENTER, " +
 | 
			
		||||
          data.id +
 | 
			
		||||
          ", true)' onmouseup='padclick(CENTER, " +
 | 
			
		||||
          data.id +
 | 
			
		||||
          ", false)' href='#' id='pc" +
 | 
			
		||||
          data.id +
 | 
			
		||||
          "'>OK</a>";
 | 
			
		||||
      //NO BREAK
 | 
			
		||||
      case UI_PAD:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'><h5>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr/>" +
 | 
			
		||||
            "<nav class='control'>" +
 | 
			
		||||
            "<ul>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(FOR, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(FOR, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' href='#' id='pf" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(RIGHT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(RIGHT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' href='#' id='pr" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(LEFT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(LEFT, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' href='#' id='pl" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "<li><a onmousedown='padclick(BACK, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", true)' onmouseup='padclick(BACK, " +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ", false)' href='#' id='pb" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>▲</a></li>" +
 | 
			
		||||
            "</ul>" +
 | 
			
		||||
            center +
 | 
			
		||||
            "</nav>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $("#pf" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(FOR, data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pf" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(FOR, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pl" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(LEFT, data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pl" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(LEFT, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pr" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(RIGHT, data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pr" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(RIGHT, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pb" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(BACK, data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pb" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(BACK, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pc" + data.id).on({
 | 
			
		||||
          touchstart: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(CENTER, data.id, true);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        $("#pc" + data.id).on({
 | 
			
		||||
          touchend: function(e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            padclick(CENTER, data.id, false);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
      case UPDATE_LABEL:
 | 
			
		||||
        $("#l" + data.id).html(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
      case UPDATE_SWITCHER:
 | 
			
		||||
        if (data.value == "0") switcher(data.id, 0);
 | 
			
		||||
        else switcher(data.id, 1);
 | 
			
		||||
        break;
 | 
			
		||||
      case UI_SLIDER:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='two columns card tcenter card-slider " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5 id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr />" +
 | 
			
		||||
            "<div id='sl" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' class='rkmd-slider slider-discrete slider-" +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<input type='range' min='0' max='100' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "</div>" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<script>" + "rkmd_rangeSlider('#sl" + data.id + "');" + "</script>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_SLIDER:
 | 
			
		||||
        slider_move($("#sl" + data.id), data.value, "100", false);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_NUMBER:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5 id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr />" +
 | 
			
		||||
            "<input style='color:black;' id='num" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' type='number' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' onchange='numberchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_NUMBER:
 | 
			
		||||
        $("#num" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UI_TEXT_INPUT:
 | 
			
		||||
        $("#row").append(
 | 
			
		||||
          "<div class='two columns card tcenter " +
 | 
			
		||||
            colorClass(data.color) +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            "<h5 id='" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "'>" +
 | 
			
		||||
            data.label +
 | 
			
		||||
            "</h5><hr />" +
 | 
			
		||||
            "<input style='color:black;' id='text" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            "' value='" +
 | 
			
		||||
            data.value +
 | 
			
		||||
            "' onchange='textchange(" +
 | 
			
		||||
            data.id +
 | 
			
		||||
            ")' />" +
 | 
			
		||||
            "</div>"
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case UPDATE_TEXT_INPUT:
 | 
			
		||||
        $("#text" + data.id).val(data.value);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        console.error("Unknown type or event");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  websock.onmessage = handleEvent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function numberchange(number) {
 | 
			
		||||
  var val = $("#num" + number).val();
 | 
			
		||||
  websock.send("nvalue:" + val + ":" + number);
 | 
			
		||||
  console.log(val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function textchange(number) {
 | 
			
		||||
  var val = $("#text" + number).val();
 | 
			
		||||
  websock.send("tvalue:" + val + ":" + number);
 | 
			
		||||
  console.log(val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function buttonclick(number, isdown) {
 | 
			
		||||
  if (isdown) websock.send("bdown:" + number);
 | 
			
		||||
  else websock.send("bup:" + number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function padclick(type, number, isdown) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case CENTER:
 | 
			
		||||
      if (isdown) websock.send("pcdown:" + number);
 | 
			
		||||
      else websock.send("pcup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case FOR:
 | 
			
		||||
      if (isdown) websock.send("pfdown:" + number);
 | 
			
		||||
      else websock.send("pfup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case BACK:
 | 
			
		||||
      if (isdown) websock.send("pbdown:" + number);
 | 
			
		||||
      else websock.send("pbup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case LEFT:
 | 
			
		||||
      if (isdown) websock.send("pldown:" + number);
 | 
			
		||||
      else websock.send("plup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
    case RIGHT:
 | 
			
		||||
      if (isdown) websock.send("prdown:" + number);
 | 
			
		||||
      else websock.send("prup:" + number);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function switcher(number, state) {
 | 
			
		||||
  if (state == null) {
 | 
			
		||||
    if ($("#s" + number).is(":checked")) {
 | 
			
		||||
      websock.send("sactive:" + number);
 | 
			
		||||
      $("#sl" + number).addClass("checked");
 | 
			
		||||
    } else {
 | 
			
		||||
      websock.send("sinactive:" + number);
 | 
			
		||||
      $("#sl" + number).removeClass("checked");
 | 
			
		||||
    }
 | 
			
		||||
  } else if (state == 1) {
 | 
			
		||||
    $("#sl" + number).addClass("checked");
 | 
			
		||||
    $("#sl" + number).prop("checked", true);
 | 
			
		||||
  } else if (state == 0) {
 | 
			
		||||
    $("#sl" + number).removeClass("checked");
 | 
			
		||||
    $("#sl" + number).prop("checked", false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										142
									
								
								examples/gui/data/js/controls.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										142
									
								
								examples/gui/data/js/controls.min.js
									
									
									
									
										vendored
									
									
								
							@@ -1,142 +0,0 @@
 | 
			
		||||
const UI_INITIAL_GUI=100;const UI_TITEL=0;const UI_LABEL=1;const UPDATE_LABEL=6;const UI_BUTTON=2;const UI_SWITCHER=3;const UPDATE_SWITCHER=7;const UI_PAD=4;const UI_CPAD=5;const UI_SLIDER=8;const UPDATE_SLIDER=9;const UI_NUMBER=10;const UPDATE_NUMBER=11;const UI_TEXT_INPUT=12;const UPDATE_TEXT_INPUT=13;const UI_GRAPH=14;const CLEAR_GRAPH=15;const ADD_GRAPH_POINT=16;const FOR=0;const BACK=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_NONE=7;const C_DARK=8;function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_NONE:return"dark";default:return"";}}
 | 
			
		||||
var websock;var websockConnected=false;function restart(){$(document).add("*").off();$("#row").html("");websock.close();start();}
 | 
			
		||||
function conStatusError(){websockConnected=false;$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#conStatus").off();$("#conStatus").on({click:restart});}
 | 
			
		||||
function handleVisibilityChange(){if(!websockConnected&&!document.hidden){restart();}}
 | 
			
		||||
function start(){document.addEventListener("visibilitychange",handleVisibilityChange,false);websock=new WebSocket("ws://"+window.location.hostname+"/ws");websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;};websock.onclose=function(evt){console.log("websock close");conStatusError();};websock.onerror=function(evt){console.log(evt);conStatusError();};var handleEvent=function(evt){var data=JSON.parse(evt.data);var e=document.body;var center="";switch(data.type){case UI_INITIAL_GUI:data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element)};handleEvent(fauxEvent);});break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case UI_LABEL:$("#row").append("<div class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'><h5 id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr /><span id='l"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='label label-wrap'>"+
 | 
			
		||||
data.value+
 | 
			
		||||
"</span></div>");break;case UI_BUTTON:$("#row").append("<div class='one columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'><h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/><button onmousedown='buttonclick("+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='buttonclick("+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
data.value+
 | 
			
		||||
"</button></div>");$("#"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);}});$("#"+data.id).on({touchend:function(e){e.preventDefault();buttonclick(data.id,false);}});break;case UI_SWITCHER:var label="<label id='sl"+data.id+"' class='switch checked'>";var input="<div class='in'><input type='checkbox' id='s"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' onClick='switcher("+
 | 
			
		||||
data.id+
 | 
			
		||||
",null)' checked></div>";if(data.value=="0"){label="<label id='sl"+data.id+"' class='switch'>";input="<div class='in'><input type='checkbox' id='s"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' onClick='switcher("+
 | 
			
		||||
data.id+
 | 
			
		||||
",null)' ></div>";}
 | 
			
		||||
$("#row").append("<div id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='one columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'><h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
label+
 | 
			
		||||
input+
 | 
			
		||||
"</label>"+
 | 
			
		||||
"</div>");break;case UI_CPAD:center="<a class='confirm' onmousedown='padclick(CENTER, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(CENTER, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' href='#' id='pc"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>OK</a>";case UI_PAD:$("#row").append("<div class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'><h5>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr/>"+
 | 
			
		||||
"<nav class='control'>"+
 | 
			
		||||
"<ul>"+
 | 
			
		||||
"<li><a onmousedown='padclick(FOR, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(FOR, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' href='#' id='pf"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(RIGHT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(RIGHT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' href='#' id='pr"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(LEFT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(LEFT, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' href='#' id='pl"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"<li><a onmousedown='padclick(BACK, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", true)' onmouseup='padclick(BACK, "+
 | 
			
		||||
data.id+
 | 
			
		||||
", false)' href='#' id='pb"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>▲</a></li>"+
 | 
			
		||||
"</ul>"+
 | 
			
		||||
center+
 | 
			
		||||
"</nav>"+
 | 
			
		||||
"</div>");$("#pf"+data.id).on({touchstart:function(e){e.preventDefault();padclick(FOR,data.id,true);}});$("#pf"+data.id).on({touchend:function(e){e.preventDefault();padclick(FOR,data.id,false);}});$("#pl"+data.id).on({touchstart:function(e){e.preventDefault();padclick(LEFT,data.id,true);}});$("#pl"+data.id).on({touchend:function(e){e.preventDefault();padclick(LEFT,data.id,false);}});$("#pr"+data.id).on({touchstart:function(e){e.preventDefault();padclick(RIGHT,data.id,true);}});$("#pr"+data.id).on({touchend:function(e){e.preventDefault();padclick(RIGHT,data.id,false);}});$("#pb"+data.id).on({touchstart:function(e){e.preventDefault();padclick(BACK,data.id,true);}});$("#pb"+data.id).on({touchend:function(e){e.preventDefault();padclick(BACK,data.id,false);}});$("#pc"+data.id).on({touchstart:function(e){e.preventDefault();padclick(CENTER,data.id,true);}});$("#pc"+data.id).on({touchend:function(e){e.preventDefault();padclick(CENTER,data.id,false);}});break;case UPDATE_LABEL:$("#l"+data.id).html(data.value);break;case UPDATE_SWITCHER:if(data.value=="0")switcher(data.id,0);else switcher(data.id,1);break;case UI_SLIDER:$("#row").append("<div class='two columns card tcenter card-slider "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5 id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr />"+
 | 
			
		||||
"<div id='sl"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' class='rkmd-slider slider-discrete slider-"+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<input type='range' min='0' max='100' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"'>"+
 | 
			
		||||
"</div>"+
 | 
			
		||||
"</div>");$("#row").append("<script>"+"rkmd_rangeSlider('#sl"+data.id+"');"+"</script>");break;case UPDATE_SLIDER:slider_move($("#sl"+data.id),data.value,"100",false);break;case UI_NUMBER:$("#row").append("<div class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5 id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr />"+
 | 
			
		||||
"<input style='color:black;' id='num"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' type='number' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' onchange='numberchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UPDATE_NUMBER:$("#num"+data.id).val(data.value);break;case UI_TEXT_INPUT:$("#row").append("<div class='two columns card tcenter "+
 | 
			
		||||
colorClass(data.color)+
 | 
			
		||||
"'>"+
 | 
			
		||||
"<h5 id='"+
 | 
			
		||||
data.id+
 | 
			
		||||
"'>"+
 | 
			
		||||
data.label+
 | 
			
		||||
"</h5><hr />"+
 | 
			
		||||
"<input style='color:black;' id='text"+
 | 
			
		||||
data.id+
 | 
			
		||||
"' value='"+
 | 
			
		||||
data.value+
 | 
			
		||||
"' onchange='textchange("+
 | 
			
		||||
data.id+
 | 
			
		||||
")' />"+
 | 
			
		||||
"</div>");break;case UPDATE_TEXT_INPUT:$("#text"+data.id).val(data.value);break;default:console.error("Unknown type or event");break;}};websock.onmessage=handleEvent;}
 | 
			
		||||
function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);console.log(val);}
 | 
			
		||||
function textchange(number){var val=$("#text"+number).val();websock.send("tvalue:"+val+":"+number);console.log(val);}
 | 
			
		||||
function buttonclick(number,isdown){if(isdown)websock.send("bdown:"+number);else websock.send("bup:"+number);}
 | 
			
		||||
function padclick(type,number,isdown){switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case FOR:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case BACK:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}}
 | 
			
		||||
function switcher(number,state){if(state==null){if($("#s"+number).is(":checked")){websock.send("sactive:"+number);$("#sl"+number).addClass("checked");}else{websock.send("sinactive:"+number);$("#sl"+number).removeClass("checked");}}else if(state==1){$("#sl"+number).addClass("checked");$("#sl"+number).prop("checked",true);}else if(state==0){$("#sl"+number).removeClass("checked");$("#sl"+number).prop("checked",false);}}
 | 
			
		||||
@@ -1,125 +0,0 @@
 | 
			
		||||
/* -----------------------------------------------------
 | 
			
		||||
  Material Design Sliders
 | 
			
		||||
  CodePen URL: https://codepen.io/rkchauhan/pen/xVGGpR
 | 
			
		||||
  By: Ravikumar Chauhan
 | 
			
		||||
-------------------------------------------------------- */
 | 
			
		||||
function rkmd_rangeSlider(selector) {
 | 
			
		||||
    var self, slider_width, slider_offset, curnt, sliderDiscrete, range, slider;
 | 
			
		||||
    self = $(selector);
 | 
			
		||||
    slider_width = self.width();
 | 
			
		||||
    slider_offset = self.offset().left;
 | 
			
		||||
    sliderDiscrete = self;
 | 
			
		||||
 | 
			
		||||
    sliderDiscrete.each(function(i, v) {
 | 
			
		||||
        curnt = $(this);
 | 
			
		||||
        curnt.append(sliderDiscrete_tmplt());
 | 
			
		||||
        range = curnt.find('input[type="range"]');
 | 
			
		||||
        slider = curnt.find('.slider');
 | 
			
		||||
        slider_fill = slider.find('.slider-fill');
 | 
			
		||||
        slider_handle = slider.find('.slider-handle');
 | 
			
		||||
        slider_label = slider.find('.slider-label');
 | 
			
		||||
 | 
			
		||||
        var range_val = parseInt(range.val());
 | 
			
		||||
        slider_fill.css('width', range_val + '%');
 | 
			
		||||
        slider_handle.css('left', range_val + '%');
 | 
			
		||||
        slider_label.find('span').text(range_val);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    self.on('mousedown touchstart', '.slider-handle', function(e) {
 | 
			
		||||
        if (e.button === 2) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        var parents = $(this).parents('.rkmd-slider');
 | 
			
		||||
        var slider_width = parents.width();
 | 
			
		||||
        var slider_offset = parents.offset().left;
 | 
			
		||||
        var check_range = parents.find('input[type="range"]').is(':disabled');
 | 
			
		||||
        if (check_range === true) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $(this).addClass('is-active');
 | 
			
		||||
        var moveFu =
 | 
			
		||||
            function(e) {
 | 
			
		||||
                var pageX = e.pageX || e.changedTouches[0].pageX;
 | 
			
		||||
                var slider_new_width = pageX - slider_offset;
 | 
			
		||||
                if (slider_new_width <= slider_width && !(slider_new_width < '0')) {
 | 
			
		||||
                    slider_move(parents, slider_new_width, slider_width, true);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        var upFu =
 | 
			
		||||
            function(e) {
 | 
			
		||||
                $(this).off(handlers);
 | 
			
		||||
                parents.find('.is-active').removeClass('is-active');
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        var handlers = {
 | 
			
		||||
            mousemove: moveFu,
 | 
			
		||||
            touchmove: moveFu,
 | 
			
		||||
            mouseup: upFu,
 | 
			
		||||
            touchend: upFu
 | 
			
		||||
        };
 | 
			
		||||
        $(document).on(handlers);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    self.on('mousedown touchstart', '.slider', function(e) {
 | 
			
		||||
        if (e.button === 2) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var parents = $(this).parents('.rkmd-slider');
 | 
			
		||||
        var slider_width = parents.width();
 | 
			
		||||
        var slider_offset = parents.offset().left;
 | 
			
		||||
        var check_range = parents.find('input[type="range"]').is(':disabled');
 | 
			
		||||
 | 
			
		||||
        if (check_range === true) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var slider_new_width = e.pageX - slider_offset;
 | 
			
		||||
        if (slider_new_width <= slider_width && !(slider_new_width < '0')) {
 | 
			
		||||
            slider_move(parents, slider_new_width, slider_width, true);
 | 
			
		||||
        }
 | 
			
		||||
        var upFu =
 | 
			
		||||
        function(e) {
 | 
			
		||||
            $(this).off(handlers);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var handlers = {
 | 
			
		||||
            mouseup: upFu,
 | 
			
		||||
            touchend: upFu
 | 
			
		||||
        };
 | 
			
		||||
        $(document).on(handlers);
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function sliderDiscrete_tmplt() {
 | 
			
		||||
    var tmplt = '<div class="slider">' +
 | 
			
		||||
        '<div class="slider-fill"></div>' +
 | 
			
		||||
        '<div class="slider-handle"><div class="slider-label"><span>0</span></div></div>' +
 | 
			
		||||
        '</div>';
 | 
			
		||||
 | 
			
		||||
    return tmplt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function slider_move(parents, newW, sliderW, send) {
 | 
			
		||||
    var slider_new_val = parseInt(Math.round(newW / sliderW * 100));
 | 
			
		||||
 | 
			
		||||
    var slider_fill = parents.find('.slider-fill');
 | 
			
		||||
    var slider_handle = parents.find('.slider-handle');
 | 
			
		||||
    var range = parents.find('input[type="range"]');
 | 
			
		||||
 | 
			
		||||
    slider_fill.css('width', slider_new_val + '%');
 | 
			
		||||
    slider_handle.css({
 | 
			
		||||
        'left': slider_new_val + '%',
 | 
			
		||||
        'transition': 'none',
 | 
			
		||||
        '-webkit-transition': 'none',
 | 
			
		||||
        '-moz-transition': 'none'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    range.val(slider_new_val);
 | 
			
		||||
    if (parents.find('.slider-handle span').text() != slider_new_val) {
 | 
			
		||||
        parents.find('.slider-handle span').text(slider_new_val);
 | 
			
		||||
        var number = parents.attr('id').substring(2);
 | 
			
		||||
        if (send) websock.send('slvalue:' + slider_new_val + ':' + number);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								examples/gui/data/js/slider.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								examples/gui/data/js/slider.min.js
									
									
									
									
										vendored
									
									
								
							@@ -1,10 +0,0 @@
 | 
			
		||||
function rkmd_rangeSlider(selector){var self,slider_width,slider_offset,curnt,sliderDiscrete,range,slider;self=$(selector);slider_width=self.width();slider_offset=self.offset().left;sliderDiscrete=self;sliderDiscrete.each(function(i,v){curnt=$(this);curnt.append(sliderDiscrete_tmplt());range=curnt.find('input[type="range"]');slider=curnt.find('.slider');slider_fill=slider.find('.slider-fill');slider_handle=slider.find('.slider-handle');slider_label=slider.find('.slider-label');var range_val=parseInt(range.val());slider_fill.css('width',range_val+'%');slider_handle.css('left',range_val+'%');slider_label.find('span').text(range_val);});self.on('mousedown touchstart','.slider-handle',function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents('.rkmd-slider');var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(':disabled');if(check_range===true){return false;}
 | 
			
		||||
$(this).addClass('is-active');var moveFu=function(e){var pageX=e.pageX||e.changedTouches[0].pageX;var slider_new_width=pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<'0')){slider_move(parents,slider_new_width,slider_width,true);}};var upFu=function(e){$(this).off(handlers);parents.find('.is-active').removeClass('is-active');};var handlers={mousemove:moveFu,touchmove:moveFu,mouseup:upFu,touchend:upFu};$(document).on(handlers);});self.on('mousedown touchstart','.slider',function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents('.rkmd-slider');var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(':disabled');if(check_range===true){return false;}
 | 
			
		||||
var slider_new_width=e.pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<'0')){slider_move(parents,slider_new_width,slider_width,true);}
 | 
			
		||||
var upFu=function(e){$(this).off(handlers);};var handlers={mouseup:upFu,touchend:upFu};$(document).on(handlers);});};function sliderDiscrete_tmplt(){var tmplt='<div class="slider">'+
 | 
			
		||||
'<div class="slider-fill"></div>'+
 | 
			
		||||
'<div class="slider-handle"><div class="slider-label"><span>0</span></div></div>'+
 | 
			
		||||
'</div>';return tmplt;}
 | 
			
		||||
function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round(newW/sliderW*100));var slider_fill=parents.find('.slider-fill');var slider_handle=parents.find('.slider-handle');var range=parents.find('input[type="range"]');slider_fill.css('width',slider_new_val+'%');slider_handle.css({'left':slider_new_val+'%','transition':'none','-webkit-transition':'none','-moz-transition':'none'});range.val(slider_new_val);if(parents.find('.slider-handle span').text()!=slider_new_val){parents.find('.slider-handle span').text(slider_new_val);var number=parents.attr('id').substring(2);if(send)websock.send('slvalue:'+slider_new_val+':'+number);}}
 | 
			
		||||
@@ -12,174 +12,237 @@ DNSServer dnsServer;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char *ssid = "ESPUI";
 | 
			
		||||
const char *password = "";
 | 
			
		||||
const char *password = "espui";
 | 
			
		||||
 | 
			
		||||
long oldTime = 0;
 | 
			
		||||
bool switchi = false;
 | 
			
		||||
const char *hostname = "espui";
 | 
			
		||||
 | 
			
		||||
void numberCall(Control sender, int type) { Serial.println(sender.value); }
 | 
			
		||||
int statusLabelId;
 | 
			
		||||
int graphId;
 | 
			
		||||
int millisLabelId;
 | 
			
		||||
int testSwitchId;
 | 
			
		||||
 | 
			
		||||
void textCall(Control sender, int type) { Serial.println(sender.value); }
 | 
			
		||||
void numberCall(Control *sender, int type) { Serial.println(sender->value); }
 | 
			
		||||
 | 
			
		||||
void slider(Control sender, int type) { Serial.println(sender.value); }
 | 
			
		||||
void textCall(Control *sender, int type) {
 | 
			
		||||
  Serial.print("Text: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println(sender->value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonCallback(Control sender, int type) {
 | 
			
		||||
void slider(Control *sender, int type) {
 | 
			
		||||
  Serial.print("Slider: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println(sender->value);
 | 
			
		||||
  // Like all Control Values in ESPUI slider values are Strings. To use them as int simply do this:
 | 
			
		||||
  int sliderValueWithOffset = sender->value.toInt() + 100;
 | 
			
		||||
  Serial.print("SliderValue with offset");
 | 
			
		||||
  Serial.println(sliderValueWithOffset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonCallback(Control *sender, int type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case B_DOWN:
 | 
			
		||||
      Serial.println("Button DOWN");
 | 
			
		||||
      break;
 | 
			
		||||
    case B_UP:
 | 
			
		||||
      Serial.println("Button UP");
 | 
			
		||||
      break;
 | 
			
		||||
  case B_DOWN:
 | 
			
		||||
    Serial.println("Button DOWN");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case B_UP:
 | 
			
		||||
    Serial.println("Button UP");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonExample(Control sender, int type) {
 | 
			
		||||
void buttonExample(Control *sender, int type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case B_DOWN:
 | 
			
		||||
      Serial.println("Status: Start");
 | 
			
		||||
      ESPUI.print(0, "Status: Start");
 | 
			
		||||
      break;
 | 
			
		||||
    case B_UP:
 | 
			
		||||
      Serial.println("Status: Stop");
 | 
			
		||||
      ESPUI.print(0, "Status: Stop");
 | 
			
		||||
      break;
 | 
			
		||||
  case B_DOWN:
 | 
			
		||||
    Serial.println("Status: Start");
 | 
			
		||||
    ESPUI.print(statusLabelId, "Start");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case B_UP:
 | 
			
		||||
    Serial.println("Status: Stop");
 | 
			
		||||
    ESPUI.print(statusLabelId, "Stop");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void padExample(Control sender, int value) {
 | 
			
		||||
void padExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case P_LEFT_DOWN:
 | 
			
		||||
      Serial.print("left down");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_LEFT_UP:
 | 
			
		||||
      Serial.print("left up");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_RIGHT_DOWN:
 | 
			
		||||
      Serial.print("right down");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_RIGHT_UP:
 | 
			
		||||
      Serial.print("right up");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_FOR_DOWN:
 | 
			
		||||
      Serial.print("for down");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_FOR_UP:
 | 
			
		||||
      Serial.print("for up");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_BACK_DOWN:
 | 
			
		||||
      Serial.print("back down");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_BACK_UP:
 | 
			
		||||
      Serial.print("back up");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_CENTER_DOWN:
 | 
			
		||||
      Serial.print("center down");
 | 
			
		||||
      break;
 | 
			
		||||
    case P_CENTER_UP:
 | 
			
		||||
      Serial.print("center up");
 | 
			
		||||
      break;
 | 
			
		||||
  case P_LEFT_DOWN:
 | 
			
		||||
    Serial.print("left down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_LEFT_UP:
 | 
			
		||||
    Serial.print("left up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_RIGHT_DOWN:
 | 
			
		||||
    Serial.print("right down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_RIGHT_UP:
 | 
			
		||||
    Serial.print("right up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_FOR_DOWN:
 | 
			
		||||
    Serial.print("for down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_FOR_UP:
 | 
			
		||||
    Serial.print("for up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_BACK_DOWN:
 | 
			
		||||
    Serial.print("back down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_BACK_UP:
 | 
			
		||||
    Serial.print("back up");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_CENTER_DOWN:
 | 
			
		||||
    Serial.print("center down");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case P_CENTER_UP:
 | 
			
		||||
    Serial.print("center up");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender.id);
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void switchExample(Control sender, int value) {
 | 
			
		||||
void switchExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case S_ACTIVE:
 | 
			
		||||
      Serial.print("Active:");
 | 
			
		||||
      break;
 | 
			
		||||
    case S_INACTIVE:
 | 
			
		||||
      Serial.print("Inactive");
 | 
			
		||||
      break;
 | 
			
		||||
  case S_ACTIVE:
 | 
			
		||||
    Serial.print("Active:");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case S_INACTIVE:
 | 
			
		||||
    Serial.print("Inactive");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender.id);
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void otherSwitchExample(Control sender, int value) {
 | 
			
		||||
void otherSwitchExample(Control *sender, int value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case S_ACTIVE:
 | 
			
		||||
      Serial.print("Active:");
 | 
			
		||||
      break;
 | 
			
		||||
    case S_INACTIVE:
 | 
			
		||||
      Serial.print("Inactive");
 | 
			
		||||
      break;
 | 
			
		||||
  case S_ACTIVE:
 | 
			
		||||
    Serial.print("Active:");
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case S_INACTIVE:
 | 
			
		||||
    Serial.print("Inactive");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print(" ");
 | 
			
		||||
  Serial.println(sender.id);
 | 
			
		||||
  Serial.println(sender->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setup(void) {
 | 
			
		||||
  ESPUI.setVerbosity(Verbosity::VerboseJSON);
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
  WiFi.mode(WIFI_AP);
 | 
			
		||||
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
 | 
			
		||||
  /*
 | 
			
		||||
  #if defined(ESP32)
 | 
			
		||||
    WiFi.setHostname(ssid);
 | 
			
		||||
  #else
 | 
			
		||||
    WiFi.hostname(ssid);
 | 
			
		||||
  #endif
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  WiFi.softAP(ssid);
 | 
			
		||||
  // WiFi.softAP(ssid, password);
 | 
			
		||||
  Serial.println("");
 | 
			
		||||
  Serial.print("IP address: ");
 | 
			
		||||
  Serial.println(WiFi.softAPIP());
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
  WiFi.setHostname(hostname);
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.hostname(hostname);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // change the beginning to this if you want to join an existing network
 | 
			
		||||
  /*
 | 
			
		||||
     Serial.begin(115200);
 | 
			
		||||
     WiFi.begin(ssid, password);
 | 
			
		||||
     Serial.println("");
 | 
			
		||||
     // Wait for connection
 | 
			
		||||
     while (WiFi.status() != WL_CONNECTED) {
 | 
			
		||||
     delay(500);
 | 
			
		||||
     Serial.print(".");
 | 
			
		||||
     }
 | 
			
		||||
     Serial.println("");
 | 
			
		||||
     Serial.print("IP address: ");
 | 
			
		||||
     Serial.println(WiFi.localIP());
 | 
			
		||||
   */
 | 
			
		||||
  // try to connect to existing network
 | 
			
		||||
  WiFi.begin(ssid, password);
 | 
			
		||||
  Serial.print("\n\nTry to connect to existing network");
 | 
			
		||||
 | 
			
		||||
  ESPUI.label("Status:", COLOR_TURQUOISE, "Stop");
 | 
			
		||||
  ESPUI.label("Millis:", COLOR_EMERALD, "0");
 | 
			
		||||
  ESPUI.button("Push Button", &buttonCallback, COLOR_PETERRIVER);
 | 
			
		||||
  ESPUI.button("Other Button", &buttonExample, COLOR_WETASPHALT, "Press");
 | 
			
		||||
  ESPUI.pad("Pad with center", true, &padExample, COLOR_SUNFLOWER);
 | 
			
		||||
  ESPUI.pad("Pad without center", false, &padExample, COLOR_CARROT);
 | 
			
		||||
  ESPUI.switcher("Switch one", false, &switchExample, COLOR_ALIZARIN);
 | 
			
		||||
  ESPUI.switcher("Switch two", true, &otherSwitchExample, COLOR_NONE);
 | 
			
		||||
  ESPUI.slider("Slider one", &slider, COLOR_ALIZARIN, "30");
 | 
			
		||||
  ESPUI.slider("Slider two", &slider, COLOR_NONE, "100");
 | 
			
		||||
  ESPUI.text("Text Test:", &textCall, COLOR_ALIZARIN, "a Text Field");
 | 
			
		||||
  ESPUI.number("Numbertest", &numberCall, COLOR_ALIZARIN, 5, 0, 10);
 | 
			
		||||
  {
 | 
			
		||||
    uint8_t timeout = 10;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
     .begin loads and serves all files from PROGMEM directly.
 | 
			
		||||
     If you want to serve the files from SPIFFS use ESPUI.beginSPIFFS
 | 
			
		||||
     (.prepareFileSystem has to be run in an empty sketch before)
 | 
			
		||||
   */
 | 
			
		||||
    // Wait for connection, 5s timeout
 | 
			
		||||
    do {
 | 
			
		||||
      delay(500);
 | 
			
		||||
      Serial.print(".");
 | 
			
		||||
      timeout--;
 | 
			
		||||
    } while (timeout && WiFi.status() != WL_CONNECTED);
 | 
			
		||||
 | 
			
		||||
    // not connected -> create hotspot
 | 
			
		||||
    if (WiFi.status() != WL_CONNECTED) {
 | 
			
		||||
      Serial.print("\n\nCreating hotspot");
 | 
			
		||||
 | 
			
		||||
      WiFi.mode(WIFI_AP);
 | 
			
		||||
      WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
 | 
			
		||||
      WiFi.softAP(ssid);
 | 
			
		||||
 | 
			
		||||
      timeout = 5;
 | 
			
		||||
 | 
			
		||||
      do {
 | 
			
		||||
        delay(500);
 | 
			
		||||
        Serial.print(".");
 | 
			
		||||
        timeout--;
 | 
			
		||||
      } while (timeout);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dnsServer.start(DNS_PORT, "*", apIP);
 | 
			
		||||
 | 
			
		||||
  Serial.println("\n\nWiFi parameters:");
 | 
			
		||||
  Serial.print("Mode: ");
 | 
			
		||||
  Serial.println(WiFi.getMode() == WIFI_AP ? "Station" : "Client");
 | 
			
		||||
  Serial.print("IP address: ");
 | 
			
		||||
  Serial.println(WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP());
 | 
			
		||||
 | 
			
		||||
  statusLabelId = ESPUI.label("Status:", ControlColor::Turquoise, "Stop");
 | 
			
		||||
  millisLabelId = ESPUI.label("Millis:", ControlColor::Emerald, "0");
 | 
			
		||||
  ESPUI.button("Push Button", &buttonCallback, ControlColor::Peterriver, "Press");
 | 
			
		||||
  ESPUI.button("Other Button", &buttonExample, ControlColor::Wetasphalt, "Press");
 | 
			
		||||
  ESPUI.padWithCenter("Pad with center", &padExample, ControlColor::Sunflower);
 | 
			
		||||
  ESPUI.pad("Pad without center", &padExample, ControlColor::Carrot);
 | 
			
		||||
  testSwitchId = ESPUI.switcher("Switch one", &switchExample, ControlColor::Alizarin, false);
 | 
			
		||||
  ESPUI.switcher("Switch two", &otherSwitchExample, ControlColor::None, true);
 | 
			
		||||
  ESPUI.slider("Slider one", &slider, ControlColor::Alizarin, 30);
 | 
			
		||||
  ESPUI.slider("Slider two", &slider, ControlColor::None, 100);
 | 
			
		||||
  ESPUI.text("Text Test:", &textCall, ControlColor::Alizarin, "a Text Field");
 | 
			
		||||
  ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10);
 | 
			
		||||
 | 
			
		||||
  graphId = ESPUI.graph("Graph Test", ControlColor::Wetasphalt);
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * .begin loads and serves all files from PROGMEM directly.
 | 
			
		||||
   * If you want to serve the files from SPIFFS use ESPUI.beginSPIFFS
 | 
			
		||||
   * (.prepareFileSystem has to be run in an empty sketch before)
 | 
			
		||||
   */
 | 
			
		||||
 
 | 
			
		||||
  // Enable this option if you want sliders to be continuous (update during move) and not discrete (update on stop)
 | 
			
		||||
  // ESPUI.sliderContinuous = true;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Optionally you can use HTTP BasicAuth. Keep in mind that this is NOT a
 | 
			
		||||
   SECURE way of limiting access.
 | 
			
		||||
   * SECURE way of limiting access.
 | 
			
		||||
   * Anyone who is able to sniff traffic will be able to intercept your password
 | 
			
		||||
   since it is transmitted in cleartext ESPUI.begin("ESPUI Control", "myuser",
 | 
			
		||||
   "mypassword");
 | 
			
		||||
  */
 | 
			
		||||
   * since it is transmitted in cleartext. Just add a string as username and
 | 
			
		||||
   * password, for example begin("ESPUI Control", "username", "password")
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ESPUI.begin("ESPUI Control");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void loop(void) {
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
 | 
			
		||||
  static long oldTime = 0;
 | 
			
		||||
  static bool testSwitchState = false;
 | 
			
		||||
 | 
			
		||||
  if (millis() - oldTime > 5000) {
 | 
			
		||||
    ESPUI.print("Millis:", String(millis()));
 | 
			
		||||
    switchi = !switchi;
 | 
			
		||||
    ESPUI.updateSwitcher("Switch one", switchi);
 | 
			
		||||
    ESPUI.print(millisLabelId, String(millis()));
 | 
			
		||||
 | 
			
		||||
    ESPUI.addGraphPoint(graphId, random(1, 50));
 | 
			
		||||
 | 
			
		||||
    testSwitchState = !testSwitchState;
 | 
			
		||||
    ESPUI.updateSwitcher(testSwitchId, testSwitchState);
 | 
			
		||||
    
 | 
			
		||||
    oldTime = millis();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#include <ESPUI.h>
 | 
			
		||||
 | 
			
		||||
ESPUIClass ESPUI( Verbosity::VerboseJSON );
 | 
			
		||||
 | 
			
		||||
void setup(void) {
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
  ESPUI.prepareFileSystem();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										262
									
								
								examples/tabbedGui/tabbedGui.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								examples/tabbedGui/tabbedGui.ino
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <ESPUI.h>
 | 
			
		||||
 | 
			
		||||
const byte DNS_PORT = 53;
 | 
			
		||||
IPAddress apIP( 192, 168, 1, 1 );
 | 
			
		||||
DNSServer dnsServer;
 | 
			
		||||
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const char* ssid = "ESPUI";
 | 
			
		||||
const char* password = "espui";
 | 
			
		||||
const char* hostname = "espui";
 | 
			
		||||
 | 
			
		||||
uint16_t button1;
 | 
			
		||||
uint16_t switchOne;
 | 
			
		||||
uint16_t status;
 | 
			
		||||
 | 
			
		||||
void numberCall( Control* sender, int type ) {
 | 
			
		||||
  Serial.println( sender->value );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void textCall( Control* sender, int type ) {
 | 
			
		||||
  Serial.print("Text: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println( sender->value );}
 | 
			
		||||
 | 
			
		||||
void slider( Control* sender, int type ) {
 | 
			
		||||
  Serial.print("Slider: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println( sender->value );}
 | 
			
		||||
 | 
			
		||||
void buttonCallback( Control* sender, int type ) {
 | 
			
		||||
  switch ( type ) {
 | 
			
		||||
    case B_DOWN:
 | 
			
		||||
      Serial.println( "Button DOWN" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case B_UP:
 | 
			
		||||
      Serial.println( "Button UP" );
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buttonExample( Control* sender, int type ) {
 | 
			
		||||
  switch ( type ) {
 | 
			
		||||
    case B_DOWN:
 | 
			
		||||
      Serial.println( "Status: Start" );
 | 
			
		||||
      ESPUI.updateControlValue( status, "Start" );
 | 
			
		||||
    
 | 
			
		||||
      ESPUI.getControl( button1 )->color = ControlColor::Carrot;
 | 
			
		||||
      ESPUI.updateControl( button1 );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case B_UP:
 | 
			
		||||
      Serial.println( "Status: Stop" );
 | 
			
		||||
      ESPUI.updateControlValue( status, "Stop" );
 | 
			
		||||
   
 | 
			
		||||
      ESPUI.getControl( button1 )->color = ControlColor::Peterriver;
 | 
			
		||||
      ESPUI.updateControl( button1 );
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void padExample( Control* sender, int value ) {
 | 
			
		||||
  switch ( value ) {
 | 
			
		||||
    case P_LEFT_DOWN:
 | 
			
		||||
      Serial.print( "left down" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_LEFT_UP:
 | 
			
		||||
      Serial.print( "left up" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_RIGHT_DOWN:
 | 
			
		||||
      Serial.print( "right down" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_RIGHT_UP:
 | 
			
		||||
      Serial.print( "right up" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_FOR_DOWN:
 | 
			
		||||
      Serial.print( "for down" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_FOR_UP:
 | 
			
		||||
      Serial.print( "for up" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_BACK_DOWN:
 | 
			
		||||
      Serial.print( "back down" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_BACK_UP:
 | 
			
		||||
      Serial.print( "back up" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_CENTER_DOWN:
 | 
			
		||||
      Serial.print( "center down" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case P_CENTER_UP:
 | 
			
		||||
      Serial.print( "center up" );
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print( " " );
 | 
			
		||||
  Serial.println( sender->id );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void switchExample( Control* sender, int value ) {
 | 
			
		||||
  switch ( value ) {
 | 
			
		||||
    case S_ACTIVE:
 | 
			
		||||
      Serial.print( "Active:" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case S_INACTIVE:
 | 
			
		||||
      Serial.print( "Inactive" );
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print( " " );
 | 
			
		||||
  Serial.println( sender->id );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void selectExample( Control* sender, int value ) {
 | 
			
		||||
  Serial.print("Select: ID: ");
 | 
			
		||||
  Serial.print(sender->id);
 | 
			
		||||
  Serial.print(", Value: ");
 | 
			
		||||
  Serial.println( sender->value );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void otherSwitchExample( Control* sender, int value ) {
 | 
			
		||||
  switch ( value ) {
 | 
			
		||||
    case S_ACTIVE:
 | 
			
		||||
      Serial.print( "Active:" );
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case S_INACTIVE:
 | 
			
		||||
      Serial.print( "Inactive" );
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Serial.print( " " );
 | 
			
		||||
  Serial.println( sender->id );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setup( void ) {
 | 
			
		||||
  Serial.begin( 115200 );
 | 
			
		||||
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
  WiFi.setHostname( hostname );
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.hostname( hostname );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // try to connect to existing network
 | 
			
		||||
  WiFi.begin( ssid, password );
 | 
			
		||||
  Serial.print( "\n\nTry to connect to existing network" );
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    uint8_t timeout = 10;
 | 
			
		||||
 | 
			
		||||
    // Wait for connection, 5s timeout
 | 
			
		||||
    do {
 | 
			
		||||
      delay( 500 );
 | 
			
		||||
      Serial.print( "." );
 | 
			
		||||
      timeout--;
 | 
			
		||||
    } while ( timeout && WiFi.status() != WL_CONNECTED );
 | 
			
		||||
 | 
			
		||||
    // not connected -> create hotspot
 | 
			
		||||
    if ( WiFi.status() != WL_CONNECTED ) {
 | 
			
		||||
      Serial.print( "\n\nCreating hotspot" );
 | 
			
		||||
 | 
			
		||||
      WiFi.mode( WIFI_AP );
 | 
			
		||||
      WiFi.softAPConfig( apIP, apIP, IPAddress( 255, 255, 255, 0 ) );
 | 
			
		||||
      WiFi.softAP( ssid );
 | 
			
		||||
 | 
			
		||||
      timeout = 5;
 | 
			
		||||
 | 
			
		||||
      do {
 | 
			
		||||
        delay( 500 );
 | 
			
		||||
        Serial.print( "." );
 | 
			
		||||
        timeout--;
 | 
			
		||||
      } while ( timeout );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dnsServer.start( DNS_PORT, "*", apIP );
 | 
			
		||||
 | 
			
		||||
  Serial.println( "\n\nWiFi parameters:" );
 | 
			
		||||
  Serial.print( "Mode: " );
 | 
			
		||||
  Serial.println( WiFi.getMode() == WIFI_AP ? "Station" : "Client" );
 | 
			
		||||
  Serial.print( "IP address: " );
 | 
			
		||||
  Serial.println( WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP() );
 | 
			
		||||
 | 
			
		||||
  uint16_t tab1 = ESPUI.addControl( ControlType::Tab, "Settings 1", "Settings 1" );
 | 
			
		||||
  uint16_t tab2 = ESPUI.addControl( ControlType::Tab, "Settings 2", "Settings 2" );
 | 
			
		||||
  uint16_t tab3 = ESPUI.addControl( ControlType::Tab, "Settings 3", "Settings 3" );
 | 
			
		||||
 | 
			
		||||
  // shown above all tabs
 | 
			
		||||
  status = ESPUI.addControl( ControlType::Label, "Status:", "Stop", ControlColor::Turquoise );
 | 
			
		||||
 | 
			
		||||
  uint16_t select1 = ESPUI.addControl( ControlType::Select, "Select:", "", ControlColor::Alizarin, tab1, &selectExample );
 | 
			
		||||
  ESPUI.addControl( ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1 );
 | 
			
		||||
  ESPUI.addControl( ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1 );
 | 
			
		||||
  ESPUI.addControl( ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1 );
 | 
			
		||||
  
 | 
			
		||||
  ESPUI.addControl( ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, tab1, &textCall );
 | 
			
		||||
 | 
			
		||||
  // tabbed controls
 | 
			
		||||
  ESPUI.addControl( ControlType::Label, "Millis:", "0", ControlColor::Emerald, tab1 );
 | 
			
		||||
  button1 = ESPUI.addControl( ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, tab1, &buttonCallback );
 | 
			
		||||
  ESPUI.addControl( ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, tab1, &buttonExample );
 | 
			
		||||
  ESPUI.addControl( ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, tab2, &padExample );
 | 
			
		||||
  ESPUI.addControl( ControlType::Pad, "Pad without center", "", ControlColor::Carrot, tab3, &padExample );
 | 
			
		||||
  switchOne = ESPUI.addControl( ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, tab3, &switchExample );
 | 
			
		||||
  ESPUI.addControl( ControlType::Switcher, "Switch two", "", ControlColor::None, tab3, &otherSwitchExample );
 | 
			
		||||
  ESPUI.addControl( ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, tab1, &slider );
 | 
			
		||||
  ESPUI.addControl( ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, tab3, &slider );
 | 
			
		||||
  ESPUI.addControl( ControlType::Number, "Number:", "50", ControlColor::Alizarin, tab3, &numberCall );
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * .begin loads and serves all files from PROGMEM directly.
 | 
			
		||||
   * If you want to serve the files from SPIFFS use ESPUI.beginSPIFFS
 | 
			
		||||
   * (.prepareFileSystem has to be run in an empty sketch before)
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  // Enable this option if you want sliders to be continuous (update during move) and not discrete (update on stop)
 | 
			
		||||
  // ESPUI.sliderContinuous = true;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Optionally you can use HTTP BasicAuth. Keep in mind that this is NOT a
 | 
			
		||||
   * SECURE way of limiting access.
 | 
			
		||||
   * Anyone who is able to sniff traffic will be able to intercept your password
 | 
			
		||||
   * since it is transmitted in cleartext. Just add a string as username and
 | 
			
		||||
   * password, for example begin("ESPUI Control", "username", "password")
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ESPUI.begin("ESPUI Control");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void loop( void ) {
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
 | 
			
		||||
  static long oldTime = 0;
 | 
			
		||||
  static bool switchi = false;
 | 
			
		||||
 | 
			
		||||
  if ( millis() - oldTime > 5000 ) {
 | 
			
		||||
    switchi = !switchi;
 | 
			
		||||
    ESPUI.updateControlValue( switchOne, switchi ? "1" : "0" );
 | 
			
		||||
 | 
			
		||||
    oldTime = millis();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
      "frameworks": "arduino"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "version": "1.6.3",
 | 
			
		||||
  "version": "2.0.0",
 | 
			
		||||
  "frameworks": "arduino",
 | 
			
		||||
  "platforms": "*"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
name=ESPUI
 | 
			
		||||
version=1.6.3
 | 
			
		||||
version=2.0.0
 | 
			
		||||
author=Lukas Bachschwell
 | 
			
		||||
maintainer=Lukas Bachschwell <lukas@lbsfilm.at>
 | 
			
		||||
sentence=ESP32 and ESP8266 Web Interface Library
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1093
									
								
								src/ESPUI.cpp
									
									
									
									
									
								
							
							
						
						
									
										1093
									
								
								src/ESPUI.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										254
									
								
								src/ESPUI.h
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								src/ESPUI.h
									
									
									
									
									
								
							@@ -11,10 +11,10 @@
 | 
			
		||||
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include "SPIFFS.h"
 | 
			
		||||
#include "WiFi.h"
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
@@ -31,42 +31,113 @@
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct Control {
 | 
			
		||||
  unsigned int type;
 | 
			
		||||
  unsigned int id;  // just mirroring the id here for practical reasons
 | 
			
		||||
  const char *label;
 | 
			
		||||
  void (*callback)(Control, int);
 | 
			
		||||
  String value;
 | 
			
		||||
  unsigned int color;
 | 
			
		||||
} Control;
 | 
			
		||||
 | 
			
		||||
// Message Types (and control types)
 | 
			
		||||
#define UI_INITIAL_GUI 100
 | 
			
		||||
#define UI_TITEL 0
 | 
			
		||||
 | 
			
		||||
#define UI_LABEL 1
 | 
			
		||||
#define UPDATE_LABEL 6
 | 
			
		||||
enum ControlType : uint8_t {
 | 
			
		||||
  // fixed controls
 | 
			
		||||
  Title = 0,
 | 
			
		||||
 | 
			
		||||
#define UI_BUTTON 2
 | 
			
		||||
  // updatable controls
 | 
			
		||||
  Pad,
 | 
			
		||||
  PadWithCenter,
 | 
			
		||||
  Button,
 | 
			
		||||
  Label,
 | 
			
		||||
  Switcher,
 | 
			
		||||
  Slider,
 | 
			
		||||
  Number,
 | 
			
		||||
  Text,
 | 
			
		||||
  Graph,
 | 
			
		||||
  GraphPoint,
 | 
			
		||||
  Tab,
 | 
			
		||||
  Select,
 | 
			
		||||
  Option,
 | 
			
		||||
  Min,
 | 
			
		||||
  Max,
 | 
			
		||||
  Step,
 | 
			
		||||
  Gauge,
 | 
			
		||||
  Accel,
 | 
			
		||||
 | 
			
		||||
#define UI_SWITCHER 3
 | 
			
		||||
#define UPDATE_SWITCHER 7
 | 
			
		||||
  UpdateOffset = 100,
 | 
			
		||||
  UpdatePad = 101,
 | 
			
		||||
  UpdatePadWithCenter,
 | 
			
		||||
  ButtonButton,
 | 
			
		||||
  UpdateLabel,
 | 
			
		||||
  UpdateSwitcher,
 | 
			
		||||
  UpdateSlider,
 | 
			
		||||
  UpdateNumber,
 | 
			
		||||
  UpdateText,
 | 
			
		||||
  ClearGraph,
 | 
			
		||||
  UpdateTab,
 | 
			
		||||
  UpdateSelection,
 | 
			
		||||
  UpdateOption,
 | 
			
		||||
  UpdateMin,
 | 
			
		||||
  UpdateMax,
 | 
			
		||||
  UpdateStep,
 | 
			
		||||
  UpdateGauge,
 | 
			
		||||
  UpdateAccel,
 | 
			
		||||
 | 
			
		||||
#define UI_PAD 4
 | 
			
		||||
#define UI_CPAD 5
 | 
			
		||||
  InitialGui = 200
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define UI_SLIDER 8
 | 
			
		||||
#define UPDATE_SLIDER 9
 | 
			
		||||
#define UI_INITIAL_GUI ControlType::InitialGui
 | 
			
		||||
 | 
			
		||||
#define UI_NUMBER 10
 | 
			
		||||
#define UPDATE_NUMBER 11
 | 
			
		||||
#define UI_TITLE ControlType::Title
 | 
			
		||||
#define UI_LABEL ControlType::Label
 | 
			
		||||
#define UI_BUTTON ControlType::Button
 | 
			
		||||
#define UI_SWITCHER ControlType::Switcher
 | 
			
		||||
#define UI_PAD ControlType::Pad
 | 
			
		||||
#define UI_CPAD ControlType::Cpad
 | 
			
		||||
#define UI_SLIDER ControlType::Slider
 | 
			
		||||
#define UI_NUMBER ControlType::Number
 | 
			
		||||
#define UI_TEXT_INPUT ControlType::Text
 | 
			
		||||
#define UI_GRAPH ControlType::Graph
 | 
			
		||||
#define UI_ADD_GRAPH_POINT ControlType::GraphPoint
 | 
			
		||||
 | 
			
		||||
#define UI_TEXT_INPUT 12
 | 
			
		||||
#define UPDATE_TEXT_INPUT 13
 | 
			
		||||
#define UPDATE_LABEL ControlType::UpdateLabel
 | 
			
		||||
#define UPDATE_SWITCHER ControlType::UpdateSwitcher
 | 
			
		||||
#define UPDATE_SLIDER ControlType::UpdateSlider
 | 
			
		||||
#define UPDATE_NUMBER ControlType::UpdateNumber
 | 
			
		||||
#define UPDATE_TEXT_INPUT ControlType::UpdateText
 | 
			
		||||
#define CLEAR_GRAPH ControlType::ClearGraph
 | 
			
		||||
 | 
			
		||||
#define UI_GRAPH 14
 | 
			
		||||
#define CLEAR_GRAPH 15
 | 
			
		||||
#define ADD_GRAPH_POINT 16
 | 
			
		||||
// Colors
 | 
			
		||||
enum ControlColor : uint8_t { Turquoise, Emerald, Peterriver, Wetasphalt, Sunflower, Carrot, Alizarin, Dark, None = 0xFF };
 | 
			
		||||
#define COLOR_TURQUOISE ControlColor::Turquoise
 | 
			
		||||
#define COLOR_EMERALD ControlColor::Emerald
 | 
			
		||||
#define COLOR_PETERRIVER ControlColor::Peterriver
 | 
			
		||||
#define COLOR_WETASPHALT ControlColor::Wetasphalt
 | 
			
		||||
#define COLOR_SUNFLOWER ControlColor::Sunflower
 | 
			
		||||
#define COLOR_CARROT ControlColor::Carrot
 | 
			
		||||
#define COLOR_ALIZARIN ControlColor::Alizarin
 | 
			
		||||
#define COLOR_DARK ControlColor::Dark
 | 
			
		||||
#define COLOR_NONE ControlColor::None
 | 
			
		||||
 | 
			
		||||
class Control {
 | 
			
		||||
public:
 | 
			
		||||
  ControlType type;
 | 
			
		||||
  uint16_t id; // just mirroring the id here for practical reasons
 | 
			
		||||
  const char *label;
 | 
			
		||||
  void (*callback)(Control *, int);
 | 
			
		||||
  String value;
 | 
			
		||||
  ControlColor color;
 | 
			
		||||
  uint16_t parentControl;
 | 
			
		||||
  Control *next;
 | 
			
		||||
 | 
			
		||||
  static constexpr uint16_t noParent = 0xffff;
 | 
			
		||||
 | 
			
		||||
  Control(ControlType type, const char *label, void (*callback)(Control *, int), String value, ControlColor color,
 | 
			
		||||
          uint16_t parentControl = Control::noParent)
 | 
			
		||||
      : type(type), label(label), callback(callback), value(value), color(color), parentControl(parentControl), next(nullptr) {
 | 
			
		||||
    id = idCounter++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Control(const Control &control)
 | 
			
		||||
      : type(control.type), id(control.id), label(control.label), callback(control.callback), value(control.value), color(control.color),
 | 
			
		||||
        parentControl(control.parentControl), next(control.next) {}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  static uint16_t idCounter;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Values
 | 
			
		||||
#define B_DOWN -1
 | 
			
		||||
@@ -89,87 +160,90 @@ typedef struct Control {
 | 
			
		||||
#define SL_VALUE 8
 | 
			
		||||
#define N_VALUE 9
 | 
			
		||||
#define T_VALUE 10
 | 
			
		||||
#define S_VALUE 11
 | 
			
		||||
 | 
			
		||||
// Colors
 | 
			
		||||
#define COLOR_TURQUOISE 0
 | 
			
		||||
#define COLOR_EMERALD 1
 | 
			
		||||
#define COLOR_PETERRIVER 2
 | 
			
		||||
#define COLOR_WETASPHALT 3
 | 
			
		||||
#define COLOR_SUNFLOWER 4
 | 
			
		||||
#define COLOR_CARROT 5
 | 
			
		||||
#define COLOR_ALIZARIN 6
 | 
			
		||||
#define COLOR_NONE 7
 | 
			
		||||
enum Verbosity : uint8_t { Quiet = 0, Verbose, VerboseJSON };
 | 
			
		||||
 | 
			
		||||
class ESPUIClass {
 | 
			
		||||
 public:
 | 
			
		||||
  void begin(const char *_title);  // Setup servers and page in Memorymode
 | 
			
		||||
  void begin(const char *_title, const char *username, const char *password);
 | 
			
		||||
public:
 | 
			
		||||
  ESPUIClass() {
 | 
			
		||||
    verbosity = Verbosity::Quiet;
 | 
			
		||||
    jsonUpdateDocumentSize = 2000;
 | 
			
		||||
    jsonInitialDocumentSize = 8000;
 | 
			
		||||
    sliderContinuous = false;
 | 
			
		||||
  }
 | 
			
		||||
  unsigned int jsonUpdateDocumentSize;
 | 
			
		||||
  unsigned int jsonInitialDocumentSize;
 | 
			
		||||
  bool sliderContinuous;
 | 
			
		||||
 | 
			
		||||
  void beginSPIFFS(const char *_title);  // Setup servers and page in SPIFFSmode
 | 
			
		||||
  void beginSPIFFS(const char *_title, const char *username,
 | 
			
		||||
                   const char *password);
 | 
			
		||||
  void setVerbosity(Verbosity verbosity);
 | 
			
		||||
  void begin(const char *_title, const char *username = nullptr, const char *password = nullptr);       // Setup server and page in Memorymode
 | 
			
		||||
  void beginSPIFFS(const char *_title, const char *username = nullptr, const char *password = nullptr); // Setup server and page in SPIFFSmode
 | 
			
		||||
 | 
			
		||||
  void prepareFileSystem();  // Initially preps the filesystem and loads a lot
 | 
			
		||||
                             // of stuff into SPIFFS
 | 
			
		||||
  void list();
 | 
			
		||||
  // Creating Elements
 | 
			
		||||
  void prepareFileSystem(); // Initially preps the filesystem and loads a lot of stuff into SPIFFS
 | 
			
		||||
  void list();              // Lists SPIFFS directory
 | 
			
		||||
 | 
			
		||||
  int button(const char *label, void (*callBack)(Control, int), int color,
 | 
			
		||||
             String value = "");  // Create Event Button
 | 
			
		||||
  int switcher(const char *label, bool startState,
 | 
			
		||||
               void (*callBack)(Control, int),
 | 
			
		||||
               int color);  // Create Toggle Button
 | 
			
		||||
  int pad(const char *label, bool centerButton, void (*callBack)(Control, int),
 | 
			
		||||
          int color);  // Create Pad Control
 | 
			
		||||
  int slider(const char *label, void (*callBack)(Control, int), int color,
 | 
			
		||||
             String value);  // Create Slider Control
 | 
			
		||||
  int number(const char *label, void (*callBack)(Control, int), int color,
 | 
			
		||||
             int number, int min, int max);  // Create a Number Input Control
 | 
			
		||||
  int text(const char *label, void (*callBack)(Control, int), int color,
 | 
			
		||||
           String value = "");  // Create a Text Input Control
 | 
			
		||||
  uint16_t addControl(ControlType type, const char *label, String value = String(""), ControlColor color = ControlColor::Turquoise,
 | 
			
		||||
                      uint16_t parentControl = Control::noParent, void (*callback)(Control *, int) = nullptr);
 | 
			
		||||
 | 
			
		||||
  // create Elements
 | 
			
		||||
  uint16_t button(const char *label, void (*callback)(Control *, int), ControlColor color, String value = "");         // Create Event Button
 | 
			
		||||
  uint16_t switcher(const char *label, void (*callback)(Control *, int), ControlColor color, bool startState = false); // Create Toggle Button
 | 
			
		||||
  uint16_t pad(const char *label, void (*callback)(Control *, int), ControlColor color);                               // Create Pad Control
 | 
			
		||||
  uint16_t padWithCenter(const char *label, void (*callback)(Control *, int), ControlColor color); // Create Pad Control with Centerbutton
 | 
			
		||||
 | 
			
		||||
  uint16_t slider(const char *label, void (*callback)(Control *, int), ControlColor color, int value, int min = 0,
 | 
			
		||||
                  int max = 100); // Create Slider Control
 | 
			
		||||
  uint16_t number(const char *label, void (*callback)(Control *, int), ControlColor color, int value, int min = 0,
 | 
			
		||||
                  int max = 100);                                                                            // Create a Number Input Control
 | 
			
		||||
  uint16_t text(const char *label, void (*callback)(Control *, int), ControlColor color, String value = ""); // Create a Text Input Control
 | 
			
		||||
 | 
			
		||||
  // Output only
 | 
			
		||||
  int label(const char *label, int color, String value = "");  // Create Label
 | 
			
		||||
  int graph(const char *label, int color);  // Create Graph display
 | 
			
		||||
  uint16_t label(const char *label, ControlColor color, String value = ""); // Create Label
 | 
			
		||||
  uint16_t graph(const char *label, ControlColor color);                    // Create Graph display
 | 
			
		||||
  uint16_t gauge(const char *label, ControlColor color, int value, int min = 0,
 | 
			
		||||
                 int max = 100); // Create Gauge display
 | 
			
		||||
 | 
			
		||||
  // Input only
 | 
			
		||||
  uint16_t accelerometer(const char *label, void (*callback)(Control *, int), ControlColor color);
 | 
			
		||||
 | 
			
		||||
  // Update Elements
 | 
			
		||||
  void print(int id, String value);
 | 
			
		||||
  void print(String label, String value);
 | 
			
		||||
 | 
			
		||||
  void updateSwitcher(int id, bool nValue, int clientId = -1);
 | 
			
		||||
  void updateSwitcher(String label, bool nValue, int clientId = -1);
 | 
			
		||||
  Control *getControl(uint16_t id);
 | 
			
		||||
 | 
			
		||||
  void updateSlider(int id, int nValue, int clientId = -1);
 | 
			
		||||
  void updateSlider(String label, int nValue, int clientId = -1);
 | 
			
		||||
  // Update Elements
 | 
			
		||||
  void updateControlValue(uint16_t id, String value, int clientId = -1);
 | 
			
		||||
  void updateControlValue(Control *control, String value, int clientId = -1);
 | 
			
		||||
 | 
			
		||||
  void updateNumber(int id, int nValue, int clientId = -1);
 | 
			
		||||
  void updateNumber(String label, int nValue, int clientId = -1);
 | 
			
		||||
  void updateControl(uint16_t id, int clientId = -1);
 | 
			
		||||
  void updateControl(Control *control, int clientId = -1);
 | 
			
		||||
 | 
			
		||||
  void updateText(int id, String nValue, int clientId = -1);
 | 
			
		||||
  void updateText(String label, String nValue, int clientId = -1);
 | 
			
		||||
  void print(uint16_t id, String value);
 | 
			
		||||
  void updateLabel(uint16_t id, String value);
 | 
			
		||||
  void updateSwitcher(uint16_t id, bool nValue, int clientId = -1);
 | 
			
		||||
  void updateSlider(uint16_t id, int nValue, int clientId = -1);
 | 
			
		||||
  void updateNumber(uint16_t id, int nValue, int clientId = -1);
 | 
			
		||||
  void updateText(uint16_t id, String nValue, int clientId = -1);
 | 
			
		||||
  void updateSelect(uint16_t id, String nValue, int clientId = -1);
 | 
			
		||||
  void updateGauge(uint16_t id, int number, int clientId);
 | 
			
		||||
 | 
			
		||||
  void clearGraph(int id, int clientId = -1);
 | 
			
		||||
  void clearGraph(String label, int clientId = -1);
 | 
			
		||||
  void clearGraph(uint16_t id, int clientId = -1);
 | 
			
		||||
  void addGraphPoint(uint16_t id, int nValue, int clientId = -1);
 | 
			
		||||
 | 
			
		||||
  void addGraphPoint(int id, int nValue, int clientId = -1);
 | 
			
		||||
  void addGraphPoint(String label, int nValue, int clientId = -1);
 | 
			
		||||
 | 
			
		||||
  void textThem(String text, int clientId);
 | 
			
		||||
 | 
			
		||||
  // Variables ---
 | 
			
		||||
  const char *ui_title = "ESPUI";  // Store UI Title and Header Name
 | 
			
		||||
  int cIndex = 0;                  // Control index
 | 
			
		||||
  Control *controls[25];
 | 
			
		||||
  // Variables
 | 
			
		||||
  const char *ui_title = "ESPUI"; // Store UI Title and Header Name
 | 
			
		||||
  Control *controls = nullptr;
 | 
			
		||||
  void jsonDom(AsyncWebSocketClient *client);
 | 
			
		||||
  int getIdByLabel(String label);
 | 
			
		||||
  bool labelExists(String label);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  const char *basicAuthUsername;
 | 
			
		||||
  const char *basicAuthPassword;
 | 
			
		||||
  bool basicAuth = true;
 | 
			
		||||
  Verbosity verbosity;
 | 
			
		||||
 | 
			
		||||
  AsyncWebServer *server;
 | 
			
		||||
  AsyncWebSocket *ws;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  const char *basicAuthUsername = nullptr;
 | 
			
		||||
  const char *basicAuthPassword = nullptr;
 | 
			
		||||
  bool basicAuth = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern ESPUIClass ESPUI;
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										19
									
								
								src/dataGraphJS.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/dataGraphJS.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
const char JS_GRAPH[] PROGMEM = R"=====(
 | 
			
		||||
function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};}
 | 
			
		||||
function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%pixelsPerTick===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}}
 | 
			
		||||
axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);}
 | 
			
		||||
function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
 | 
			
		||||
var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+
 | 
			
		||||
xTransform(xAccessor.next())+
 | 
			
		||||
" "+
 | 
			
		||||
yTransform(yAccessor.next());}
 | 
			
		||||
line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);}
 | 
			
		||||
function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
 | 
			
		||||
while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);}
 | 
			
		||||
parent.appendChild(pointGroup);}
 | 
			
		||||
xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);}
 | 
			
		||||
function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);}
 | 
			
		||||
var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);}
 | 
			
		||||
)=====";
 | 
			
		||||
 | 
			
		||||
const uint8_t JS_GRAPH_GZIP[1245] PROGMEM = { 31,139,8,0,243,90,6,94,2,255,205,87,95,111,219,54,16,127,247,167,112,4,44,16,107,89,86,27,175,3,170,240,33,109,135,174,64,18,20,77,48,96,24,246,192,73,180,76,76,150,4,138,182,69,184,254,238,59,146,162,36,219,82,134,58,45,186,135,56,226,253,231,241,119,119,228,98,157,69,130,229,217,56,101,25,253,192,73,177,116,11,194,105,38,188,234,38,138,104,89,230,220,147,246,11,237,162,60,43,197,120,203,98,177,196,175,95,5,161,89,47,41,75,150,2,207,27,66,178,22,130,114,60,183,235,130,85,52,45,63,81,254,200,162,127,240,85,16,46,172,219,108,189,162,156,69,143,156,100,229,34,231,176,112,99,34,200,29,203,60,253,159,84,94,81,169,21,252,146,10,237,54,132,143,21,227,61,91,44,112,45,49,181,26,69,165,201,90,116,106,212,20,235,51,1,87,216,48,103,86,217,139,242,156,199,134,101,105,51,35,19,114,42,214,60,219,137,252,157,146,121,99,131,213,145,161,157,225,234,133,245,140,94,52,126,38,218,111,184,247,68,254,30,104,173,178,246,215,104,235,149,137,17,189,104,67,153,212,6,195,253,62,220,143,154,44,145,138,149,159,105,22,83,14,249,201,57,131,243,81,210,153,39,108,222,76,102,148,220,7,158,175,11,28,231,17,100,54,19,126,196,41,17,244,215,148,170,213,253,131,235,44,133,40,222,204,102,219,237,214,223,94,249,57,79,102,175,130,32,152,149,155,196,241,156,196,65,161,53,244,137,192,33,159,105,167,0,93,48,213,196,227,151,84,220,8,193,217,223,107,65,93,39,74,73,89,58,94,103,39,19,103,170,132,107,247,42,45,216,128,168,94,147,10,107,212,77,59,84,169,164,12,246,14,201,32,92,175,217,162,155,46,140,177,83,57,104,103,119,119,20,85,12,129,223,141,157,137,242,62,113,224,67,154,143,91,77,35,85,67,67,33,164,220,85,190,24,214,167,205,174,177,18,8,217,100,130,118,224,211,101,83,125,178,63,29,2,31,227,224,242,146,93,96,173,100,78,76,208,74,156,155,100,165,11,9,83,255,124,150,101,148,255,246,120,119,139,239,212,198,22,41,64,202,109,208,225,50,84,203,29,238,184,114,60,214,203,144,142,103,54,218,195,139,129,233,188,164,171,131,243,37,69,1,248,124,183,100,105,236,42,29,4,8,222,195,214,233,121,201,110,104,80,243,157,100,75,157,228,107,44,117,210,219,100,203,167,146,45,219,100,3,253,155,148,71,194,89,124,11,13,243,153,229,241,195,15,191,26,58,99,57,4,140,24,180,156,169,255,179,62,254,33,112,248,87,154,109,179,244,95,39,207,142,107,76,185,182,71,117,128,44,107,113,136,111,144,55,128,74,171,161,161,57,234,23,178,96,69,161,153,130,39,76,107,161,237,205,106,112,54,189,185,103,104,122,85,51,220,60,249,120,216,175,211,231,99,168,241,232,115,10,121,118,81,40,79,40,80,36,23,173,220,146,148,247,144,39,23,125,249,114,33,79,169,118,68,193,30,85,136,202,205,3,156,93,150,96,115,106,205,22,218,205,250,153,209,52,53,219,10,200,99,129,112,11,137,164,110,79,44,151,151,189,161,180,222,39,216,128,100,244,100,0,35,21,193,232,201,16,246,163,244,20,147,245,64,114,74,184,140,80,53,133,210,94,220,182,225,244,2,36,213,208,236,96,163,200,89,38,206,5,135,86,126,118,187,106,173,12,108,89,223,99,180,84,249,157,225,244,181,135,175,39,191,186,68,253,78,210,53,197,199,167,109,110,6,184,139,135,70,216,48,101,171,44,251,148,37,238,34,229,72,57,98,60,74,207,174,78,163,13,9,53,31,199,185,87,189,119,136,167,102,239,0,143,131,229,121,61,131,190,235,240,232,100,18,138,122,6,69,213,97,118,51,53,48,87,6,47,21,195,67,229,229,147,35,101,234,255,162,249,29,56,119,43,207,100,107,144,93,223,72,70,61,53,219,106,40,129,22,76,184,231,101,210,34,112,197,50,23,121,157,53,169,96,29,76,204,181,211,235,94,85,161,130,158,52,42,143,140,202,35,163,7,23,220,198,133,153,114,77,107,209,73,111,172,250,230,237,113,44,163,242,127,42,243,21,243,203,175,95,68,135,102,52,169,78,253,183,48,212,233,159,92,155,211,207,210,135,77,162,159,92,55,156,19,233,25,198,199,216,52,137,5,75,214,188,83,170,9,21,117,37,188,149,31,99,183,17,174,199,143,17,87,157,70,67,224,62,143,105,169,250,77,77,231,116,149,111,168,65,71,77,130,86,41,52,1,213,83,17,202,232,220,218,83,191,40,132,223,35,152,111,24,221,190,205,85,37,4,227,96,252,122,30,140,231,243,160,87,178,80,141,152,111,232,77,89,208,72,232,119,35,104,193,117,42,254,3,254,198,43,74,69,61,193,204,131,30,44,120,238,193,35,214,3,168,121,43,251,158,102,56,176,79,222,186,251,182,175,86,219,190,199,236,90,41,250,41,205,18,177,132,231,109,214,47,166,132,254,132,155,249,95,126,5,66,122,98,116,165,148,171,189,242,222,163,186,210,207,102,136,170,143,7,247,126,245,36,70,29,12,232,142,4,74,170,156,83,233,170,165,215,112,161,120,138,118,207,121,99,40,87,113,33,84,43,147,234,28,101,244,3,178,41,255,167,217,148,207,201,166,86,134,43,89,93,101,221,182,12,152,133,90,251,23,198,24,146,161,158,18,0,0 };
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
const char HTML_INDEX[] PROGMEM = R"=====(
 | 
			
		||||
<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/controls.js></script></head> <body onload=javascript:start();> <div> <h4><div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span></h4></div> <hr> <div class=container> <div id=row class="row u-full-width"> </div> </div> </body> </html> 
 | 
			
		||||
<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html> 
 | 
			
		||||
)=====";
 | 
			
		||||
 | 
			
		||||
const uint8_t HTML_INDEX_GZIP[863] PROGMEM = { 31,139,8,0,56,43,252,91,2,255,133,84,109,115,170,56,20,254,43,172,159,118,231,222,22,17,107,219,123,197,153,160,96,171,34,2,130,226,183,0,169,4,195,75,73,16,245,215,111,34,189,179,187,179,51,187,204,36,231,237,57,207,57,132,112,198,191,205,236,233,54,220,24,82,202,114,50,25,223,119,105,156,34,152,76,198,57,98,80,138,83,88,83,196,180,134,125,60,188,76,198,12,51,130,38,211,178,96,117,73,198,114,103,118,200,2,230,72,59,99,212,86,101,205,164,152,67,80,193,180,94,139,19,150,106,9,58,227,24,61,220,141,239,18,46,48,195,144,60,208,24,18,164,41,189,201,152,224,226,36,213,136,104,61,154,242,244,184,97,18,230,20,61,41,173,209,135,150,64,6,127,224,28,30,145,92,21,199,159,17,164,104,52,252,142,3,221,118,219,254,114,126,44,1,127,214,158,159,26,254,145,107,186,48,129,51,5,150,144,229,202,121,221,10,69,159,39,250,214,55,0,88,205,55,83,249,146,234,14,119,78,245,204,51,23,107,30,29,45,120,238,241,157,59,55,130,111,10,70,124,79,68,166,93,9,214,81,206,55,83,157,198,13,49,94,4,223,198,212,173,192,240,239,88,37,216,185,3,229,211,25,16,236,170,230,167,88,158,26,52,174,26,112,189,147,246,64,201,189,129,210,120,247,24,95,92,95,12,230,75,135,231,113,60,199,152,162,174,46,171,243,230,89,240,27,133,235,109,137,5,244,18,168,167,141,222,222,94,240,211,242,134,203,203,113,184,14,98,95,109,140,197,219,253,77,119,139,149,219,119,0,6,126,226,221,29,164,245,76,114,19,103,162,180,0,204,146,88,119,48,142,208,61,150,244,61,95,49,245,111,134,105,164,177,97,125,123,171,194,86,28,132,30,122,190,171,7,122,182,77,67,121,49,163,224,56,53,60,54,36,213,94,62,191,102,43,231,176,24,125,154,175,76,61,165,224,154,41,151,189,191,140,156,161,30,148,237,172,253,180,195,163,113,57,157,118,67,28,142,200,6,154,253,145,251,105,189,132,239,204,110,220,200,130,183,209,37,139,104,67,103,96,39,39,117,69,244,227,240,57,219,140,94,71,241,222,120,137,54,64,65,254,12,45,7,162,187,197,222,53,119,111,238,41,220,187,196,206,215,215,195,206,236,31,28,112,181,102,134,186,218,2,101,181,53,134,193,236,253,102,103,160,111,103,254,101,121,3,87,174,183,23,227,233,21,181,226,83,4,125,55,72,251,135,57,207,219,86,44,26,184,213,161,56,1,43,3,151,245,181,223,174,189,254,197,54,157,171,117,43,219,245,172,84,44,143,182,86,86,182,214,202,223,94,226,88,180,112,72,140,32,76,204,245,249,80,184,106,184,95,16,240,150,168,201,245,169,138,114,118,11,7,102,123,240,158,206,113,142,162,231,172,133,247,35,53,136,185,61,121,141,147,79,167,127,187,201,148,93,9,162,41,66,172,187,196,114,76,169,92,148,117,14,9,190,161,71,110,253,31,248,238,236,128,52,174,113,197,36,90,199,154,156,81,249,134,42,86,62,230,184,120,204,120,80,238,162,255,66,81,130,19,84,255,39,36,238,254,100,250,79,144,124,31,0,210,56,42,147,171,84,22,164,132,137,150,193,51,236,226,63,40,131,53,251,253,143,159,28,145,224,179,152,23,195,137,208,36,156,104,57,196,197,27,207,70,245,95,83,162,3,209,10,22,2,193,75,122,12,178,134,74,49,129,148,106,4,70,136,76,236,143,15,126,24,136,247,192,97,162,3,78,249,149,152,214,93,161,47,188,104,153,23,65,191,188,156,178,46,219,175,96,79,168,205,195,71,67,72,55,108,122,28,245,197,243,75,136,183,18,178,27,118,127,2,173,245,31,70,3,5,0,0 };
 | 
			
		||||
const uint8_t HTML_INDEX_GZIP[916] PROGMEM = { 31,139,8,0,193,22,6,94,2,255,133,148,235,115,162,58,20,192,255,21,174,159,238,157,221,22,95,181,237,174,56,19,20,108,85,68,64,240,241,45,64,42,193,240,40,9,162,254,245,155,128,157,238,157,189,211,235,12,201,201,57,191,243,200,17,206,240,175,137,57,94,239,86,154,20,177,132,140,134,245,42,13,35,4,195,209,48,65,12,74,65,4,11,138,152,82,178,183,187,167,209,144,97,70,208,104,156,165,172,200,200,80,110,142,13,153,194,4,41,39,140,170,60,43,152,20,112,4,165,76,105,85,56,100,145,18,162,19,14,208,93,125,248,46,225,20,51,12,201,29,13,32,65,74,167,53,26,18,156,30,165,2,17,165,69,35,238,30,148,76,194,60,68,75,138,10,244,166,132,144,193,31,56,129,7,36,231,233,225,167,15,41,26,244,191,99,79,53,237,170,61,159,30,50,192,127,75,199,141,52,247,192,37,85,28,129,53,6,134,216,179,133,245,188,22,130,58,13,213,181,171,1,176,152,174,198,242,57,82,45,174,28,171,177,163,207,150,220,58,152,113,223,195,43,87,174,68,188,49,24,240,53,20,158,102,46,162,14,18,190,232,189,113,80,18,237,73,196,91,233,170,225,105,110,205,118,188,141,221,237,188,91,93,130,237,158,254,46,30,167,231,149,118,207,227,114,179,155,221,78,226,116,59,165,83,219,248,195,229,89,119,58,183,184,31,231,57,163,139,188,170,220,155,150,143,34,190,150,218,206,154,24,64,205,64,239,184,82,171,235,19,126,152,95,113,118,62,244,151,94,224,246,74,109,246,82,223,116,51,91,216,109,11,96,224,134,78,173,32,149,163,147,171,232,73,167,2,96,18,6,170,133,177,143,106,91,216,118,220,142,174,126,211,116,45,10,52,227,219,75,190,171,68,35,212,157,227,218,170,167,198,235,104,39,207,38,20,28,198,154,195,250,36,223,202,167,231,120,97,237,103,131,119,253,153,245,142,17,184,196,157,243,214,157,251,86,95,245,178,106,82,189,155,187,131,118,62,30,55,125,188,27,144,21,212,219,3,251,221,120,218,189,50,179,180,125,3,94,7,231,216,167,37,157,128,141,28,22,57,81,15,253,199,120,53,120,30,4,91,237,201,95,129,14,114,39,104,222,21,213,205,182,182,190,121,177,143,187,173,77,204,100,121,217,111,244,246,222,2,23,99,162,245,22,107,208,89,172,181,190,55,121,189,154,49,104,155,177,123,158,95,193,133,203,213,89,123,120,70,149,248,43,188,182,237,69,237,253,148,251,173,115,230,119,237,124,159,30,129,17,131,243,242,210,174,150,78,251,108,234,214,197,184,102,213,114,146,117,12,135,86,70,156,85,198,194,93,159,131,64,148,176,15,53,111,23,234,203,211,62,181,123,187,237,140,128,151,176,23,94,30,114,63,97,215,93,87,175,246,206,195,41,72,144,255,24,87,176,110,169,70,244,245,209,41,173,100,60,254,237,77,166,236,66,16,141,16,98,205,75,44,7,148,202,105,86,36,144,224,43,186,231,167,255,131,107,101,3,210,160,192,57,147,104,17,40,114,76,229,43,202,89,118,159,224,244,62,230,70,185,177,254,65,81,130,67,84,124,137,28,10,152,71,95,18,65,243,173,211,47,33,6,125,31,133,183,111,254,223,164,92,15,19,105,232,103,225,69,202,82,146,193,80,137,225,9,54,246,31,148,193,130,253,253,207,79,78,132,248,36,102,79,191,17,37,28,42,9,196,233,11,119,71,197,231,200,105,40,154,195,84,16,60,165,195,32,43,169,20,16,72,169,66,160,143,200,200,124,123,227,157,69,188,8,142,113,90,174,131,222,92,163,226,150,160,241,16,69,243,52,168,248,76,91,100,213,205,216,18,98,121,247,86,18,210,204,46,62,170,110,97,74,34,80,126,113,154,194,143,88,45,46,226,3,100,56,75,165,79,241,78,64,127,68,41,201,103,66,1,220,154,247,17,233,119,213,127,23,240,177,137,198,214,119,172,103,247,47,103,53,186,226,210,5,0,0 };
 | 
			
		||||
 
 | 
			
		||||
@@ -2,4 +2,4 @@ const char CSS_NORMALIZE[] PROGMEM = R"=====(
 | 
			
		||||
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
 | 
			
		||||
)=====";
 | 
			
		||||
 | 
			
		||||
const uint8_t CSS_NORMALIZE_GZIP[861] PROGMEM = { 31,139,8,0,56,43,252,91,2,255,149,84,237,142,155,58,16,125,149,104,171,74,183,146,137,216,237,199,94,25,221,39,137,242,99,176,7,112,227,47,217,38,155,20,241,238,119,12,132,36,219,108,165,254,2,6,123,230,204,57,103,166,75,70,15,141,179,169,104,192,40,125,230,17,108,44,34,6,213,84,133,137,69,194,83,42,162,250,133,5,200,159,125,76,252,185,44,63,87,197,27,214,7,149,30,255,29,107,39,207,131,129,208,42,203,203,17,66,82,66,35,131,168,36,50,137,9,148,142,172,81,173,0,159,148,179,249,181,15,200,26,231,18,6,214,33,200,252,104,131,235,61,51,160,44,51,104,123,102,225,200,34,138,233,70,236,13,165,63,15,82,69,175,225,204,107,237,196,97,132,94,42,199,4,216,35,68,230,131,107,3,198,200,142,84,213,173,39,149,213,202,98,49,93,168,142,152,161,129,46,64,171,214,242,26,34,230,191,115,34,110,93,250,103,39,136,153,224,116,220,127,89,83,88,103,177,234,80,181,93,162,238,118,157,146,18,237,158,37,52,244,59,225,221,185,17,134,26,196,33,247,98,101,33,156,118,129,167,64,12,123,8,104,211,8,28,168,163,35,145,195,59,71,112,6,215,167,12,33,211,86,215,97,151,84,210,184,31,106,23,136,147,162,118,41,57,195,159,253,105,35,233,21,229,88,179,72,240,108,59,43,248,54,131,170,157,150,163,108,236,28,140,233,172,145,171,68,61,138,177,123,94,130,36,25,127,65,83,45,42,109,127,188,162,217,148,35,125,30,110,16,243,79,77,83,86,51,236,79,101,89,142,209,128,214,55,41,254,37,181,99,79,40,122,127,19,125,253,254,185,154,104,190,176,84,121,23,85,86,142,7,36,142,168,225,15,185,207,153,146,243,188,216,126,71,147,115,15,75,215,197,246,37,71,148,105,23,58,136,163,120,108,39,153,120,32,239,124,25,50,131,141,118,111,124,214,100,156,141,117,113,226,51,117,248,173,244,167,177,11,67,97,220,47,162,243,148,241,42,219,242,44,51,233,145,67,213,7,225,85,113,79,41,215,74,208,39,55,10,71,198,62,212,146,76,135,44,130,241,119,3,101,156,117,164,183,64,182,190,85,87,174,8,213,88,247,212,161,101,202,250,62,49,231,211,108,125,34,132,236,206,242,136,145,89,96,152,101,80,182,163,217,76,83,134,245,99,157,181,57,211,21,222,81,69,85,107,188,84,152,83,14,211,212,78,54,108,92,48,179,81,151,19,29,173,131,205,4,100,151,206,30,255,123,154,227,79,123,118,27,164,193,194,244,46,70,82,25,69,193,225,178,27,192,123,4,42,34,144,207,73,42,209,135,72,45,120,167,136,214,176,148,220,209,188,0,97,148,251,219,226,107,112,88,46,73,108,160,215,105,185,196,249,164,96,227,68,31,11,101,45,45,140,233,222,239,241,213,44,149,7,41,179,168,229,56,29,29,110,29,106,137,7,208,227,109,63,162,67,113,32,225,223,183,14,180,27,158,242,72,174,46,89,167,243,244,190,198,114,199,246,166,198,240,180,39,116,11,55,19,180,34,122,101,139,91,241,63,60,79,107,225,254,252,176,0,159,252,119,39,3,113,46,186,199,50,100,221,27,133,90,86,127,242,255,229,226,95,141,199,67,12,87,252,115,164,16,25,134,126,212,242,135,87,36,10,23,32,239,142,71,29,77,214,157,90,34,67,94,164,206,251,49,58,173,228,38,42,77,147,176,142,199,230,197,95,37,218,126,165,117,178,217,254,120,153,30,175,121,183,104,108,209,202,71,142,89,135,240,126,240,47,179,250,251,250,77,217,189,151,189,77,147,171,193,71,228,151,151,106,249,145,151,193,82,64,178,212,13,215,130,255,3,4,241,118,208,151,7,0,0 };
 | 
			
		||||
const uint8_t CSS_NORMALIZE_GZIP[861] PROGMEM = { 31,139,8,0,193,22,6,94,2,255,149,84,237,142,155,58,16,125,149,104,171,74,183,146,137,216,237,199,94,25,221,39,137,242,99,176,7,112,227,47,217,38,155,20,241,238,119,12,132,36,219,108,165,254,2,6,123,230,204,57,103,166,75,70,15,141,179,169,104,192,40,125,230,17,108,44,34,6,213,84,133,137,69,194,83,42,162,250,133,5,200,159,125,76,252,185,44,63,87,197,27,214,7,149,30,255,29,107,39,207,131,129,208,42,203,203,17,66,82,66,35,131,168,36,50,137,9,148,142,172,81,173,0,159,148,179,249,181,15,200,26,231,18,6,214,33,200,252,104,131,235,61,51,160,44,51,104,123,102,225,200,34,138,233,70,236,13,165,63,15,82,69,175,225,204,107,237,196,97,132,94,42,199,4,216,35,68,230,131,107,3,198,200,142,84,213,173,39,149,213,202,98,49,93,168,142,152,161,129,46,64,171,214,242,26,34,230,191,115,34,110,93,250,103,39,136,153,224,116,220,127,89,83,88,103,177,234,80,181,93,162,238,118,157,146,18,237,158,37,52,244,59,225,221,185,17,134,26,196,33,247,98,101,33,156,118,129,167,64,12,123,8,104,211,8,28,168,163,35,145,195,59,71,112,6,215,167,12,33,211,86,215,97,151,84,210,184,31,106,23,136,147,162,118,41,57,195,159,253,105,35,233,21,229,88,179,72,240,108,59,43,248,54,131,170,157,150,163,108,236,28,140,233,172,145,171,68,61,138,177,123,94,130,36,25,127,65,83,45,42,109,127,188,162,217,148,35,125,30,110,16,243,79,77,83,86,51,236,79,101,89,142,209,128,214,55,41,254,37,181,99,79,40,122,127,19,125,253,254,185,154,104,190,176,84,121,23,85,86,142,7,36,142,168,225,15,185,207,153,146,243,188,216,126,71,147,115,15,75,215,197,246,37,71,148,105,23,58,136,163,120,108,39,153,120,32,239,124,25,50,131,141,118,111,124,214,100,156,141,117,113,226,51,117,248,173,244,167,177,11,67,97,220,47,162,243,148,241,42,219,242,44,51,233,145,67,213,7,225,85,113,79,41,215,74,208,39,55,10,71,198,62,212,146,76,135,44,130,241,119,3,101,156,117,164,183,64,182,190,85,87,174,8,213,88,247,212,161,101,202,250,62,49,231,211,108,125,34,132,236,206,242,136,145,89,96,152,101,80,182,163,217,76,83,134,245,99,157,181,57,211,21,222,81,69,85,107,188,84,152,83,14,211,212,78,54,108,92,48,179,81,151,19,29,173,131,205,4,100,151,206,30,255,123,154,227,79,123,118,27,164,193,194,244,46,70,82,25,69,193,225,178,27,192,123,4,42,34,144,207,73,42,209,135,72,45,120,167,136,214,176,148,220,209,188,0,97,148,251,219,226,107,112,88,46,73,108,160,215,105,185,196,249,164,96,227,68,31,11,101,45,45,140,233,222,239,241,213,44,149,7,41,179,168,229,56,29,29,110,29,106,137,7,208,227,109,63,162,67,113,32,225,223,183,14,180,27,158,242,72,174,46,89,167,243,244,190,198,114,199,246,166,198,240,180,39,116,11,55,19,180,34,122,101,139,91,241,63,60,79,107,225,254,252,176,0,159,252,119,39,3,113,46,186,199,50,100,221,27,133,90,86,127,242,255,229,226,95,141,199,67,12,87,252,115,164,16,25,134,126,212,242,135,87,36,10,23,32,239,142,71,29,77,214,157,90,34,67,94,164,206,251,49,58,173,228,38,42,77,147,176,142,199,230,197,95,37,218,126,165,117,178,217,254,120,153,30,175,121,183,104,108,209,202,71,142,89,135,240,126,240,47,179,250,251,250,77,217,189,151,189,77,147,171,193,71,228,151,151,106,249,145,151,193,82,64,178,212,13,215,130,255,3,4,241,118,208,151,7,0,0 };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,15 @@
 | 
			
		||||
const char JS_SLIDER[] PROGMEM = R"=====(
 | 
			
		||||
function rkmd_rangeSlider(selector){var self,slider_width,slider_offset,curnt,sliderDiscrete,range,slider;self=$(selector);slider_width=self.width();slider_offset=self.offset().left;sliderDiscrete=self;sliderDiscrete.each(function(i,v){curnt=$(this);curnt.append(sliderDiscrete_tmplt());range=curnt.find('input[type="range"]');slider=curnt.find('.slider');slider_fill=slider.find('.slider-fill');slider_handle=slider.find('.slider-handle');slider_label=slider.find('.slider-label');var range_val=parseInt(range.val());slider_fill.css('width',range_val+'%');slider_handle.css('left',range_val+'%');slider_label.find('span').text(range_val);});self.on('mousedown touchstart','.slider-handle',function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents('.rkmd-slider');var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(':disabled');if(check_range===true){return false;}
 | 
			
		||||
$(this).addClass('is-active');var moveFu=function(e){var pageX=e.pageX||e.changedTouches[0].pageX;var slider_new_width=pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<'0')){slider_move(parents,slider_new_width,slider_width,true);}};var upFu=function(e){$(this).off(handlers);parents.find('.is-active').removeClass('is-active');};var handlers={mousemove:moveFu,touchmove:moveFu,mouseup:upFu,touchend:upFu};$(document).on(handlers);});self.on('mousedown touchstart','.slider',function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents('.rkmd-slider');var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(':disabled');if(check_range===true){return false;}
 | 
			
		||||
var slider_new_width=e.pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<'0')){slider_move(parents,slider_new_width,slider_width,true);}
 | 
			
		||||
var upFu=function(e){$(this).off(handlers);};var handlers={mouseup:upFu,touchend:upFu};$(document).on(handlers);});};function sliderDiscrete_tmplt(){var tmplt='<div class="slider">'+
 | 
			
		||||
function rkmd_rangeSlider(selector){var self,slider_width,slider_offset,curnt,sliderDiscrete,range,slider;self=$(selector);slider_width=self.width();slider_offset=self.offset().left;sliderDiscrete=self;sliderDiscrete.each(function(i,v){curnt=$(this);curnt.append(sliderDiscrete_tmplt());range=curnt.find('input[type="range"]');slider=curnt.find(".slider");slider_fill=slider.find(".slider-fill");slider_handle=slider.find(".slider-handle");slider_label=slider.find(".slider-label");var range_val=parseInt(range.val());slider_fill.css("width",range_val+"%");slider_handle.css("left",range_val+"%");slider_label.find("span").text(range_val);});self.on("mousedown touchstart",".slider-handle",function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents(".rkmd-slider");var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(":disabled");if(check_range===true){return false;}
 | 
			
		||||
$(this).addClass("is-active");var moveFu=function(e){var pageX=e.pageX||e.changedTouches[0].pageX;var slider_new_width=pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<"0")){slider_move(parents,slider_new_width,slider_width,true);}};var upFu=function(e){$(this).off(handlers);parents.find(".is-active").removeClass("is-active");};var handlers={mousemove:moveFu,touchmove:moveFu,mouseup:upFu,touchend:upFu};$(document).on(handlers);});self.on("mousedown touchstart",".slider",function(e){if(e.button===2){return false;}
 | 
			
		||||
var parents=$(this).parents(".rkmd-slider");var slider_width=parents.width();var slider_offset=parents.offset().left;var check_range=parents.find('input[type="range"]').is(":disabled");if(check_range===true){return false;}
 | 
			
		||||
var slider_new_width=e.pageX-slider_offset;if(slider_new_width<=slider_width&&!(slider_new_width<"0")){slider_move(parents,slider_new_width,slider_width,true);}
 | 
			
		||||
var upFu=function(e){$(this).off(handlers);};var handlers={mouseup:upFu,touchend:upFu};$(document).on(handlers);});}
 | 
			
		||||
function sliderDiscrete_tmplt(){var tmplt='<div class="slider">'+
 | 
			
		||||
'<div class="slider-fill"></div>'+
 | 
			
		||||
'<div class="slider-handle"><div class="slider-label"><span>0</span></div></div>'+
 | 
			
		||||
'</div>';return tmplt;}
 | 
			
		||||
function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round(newW/sliderW*100));var slider_fill=parents.find('.slider-fill');var slider_handle=parents.find('.slider-handle');var range=parents.find('input[type="range"]');slider_fill.css('width',slider_new_val+'%');slider_handle.css({'left':slider_new_val+'%','transition':'none','-webkit-transition':'none','-moz-transition':'none'});range.val(slider_new_val);if(parents.find('.slider-handle span').text()!=slider_new_val){parents.find('.slider-handle span').text(slider_new_val);var number=parents.attr('id').substring(2);if(send)websock.send('slvalue:'+slider_new_val+':'+number);}}
 | 
			
		||||
"</div>";return tmplt;}
 | 
			
		||||
function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round((newW/sliderW)*100));var slider_fill=parents.find(".slider-fill");var slider_handle=parents.find(".slider-handle");var range=parents.find('input[type="range"]');slider_fill.css("width",slider_new_val+"%");slider_handle.css({left:slider_new_val+"%",transition:"none","-webkit-transition":"none","-moz-transition":"none"});range.val(slider_new_val);if(parents.find(".slider-handle span").text()!=slider_new_val){parents.find(".slider-handle span").text(slider_new_val);var number=parents.attr("id").substring(2);if(send)websock.send("slvalue:"+slider_new_val+":"+number);}}
 | 
			
		||||
)=====";
 | 
			
		||||
 | 
			
		||||
const uint8_t JS_SLIDER_GZIP[865] PROGMEM = { 31,139,8,0,56,43,252,91,2,255,237,86,207,111,155,48,20,190,247,175,72,163,174,134,149,184,89,143,33,238,101,211,164,29,118,218,164,77,170,170,200,1,83,172,16,131,176,73,182,209,252,239,123,254,1,1,66,170,110,167,29,118,194,246,251,252,252,222,247,62,243,156,84,34,82,60,23,147,114,179,141,87,37,21,79,236,75,198,99,86,122,146,101,44,82,121,233,215,59,90,78,96,150,4,210,88,86,123,30,171,180,153,228,73,34,153,10,162,170,20,202,173,125,224,50,42,153,98,129,113,231,22,67,237,129,92,29,221,134,93,111,68,91,177,25,122,173,197,186,182,38,59,246,124,156,177,68,133,253,115,12,98,176,134,25,141,82,47,113,217,121,60,216,249,181,137,17,66,80,41,151,126,104,102,152,22,5,19,177,215,223,188,82,219,34,131,195,252,208,100,64,44,52,225,0,68,92,20,149,122,80,63,11,70,166,198,58,125,68,77,196,61,32,182,107,173,113,149,240,44,35,118,220,135,204,180,229,136,75,169,136,51,54,142,180,182,35,54,163,107,118,198,169,49,1,82,87,207,4,186,218,209,140,20,180,148,236,147,80,158,89,194,176,164,211,236,68,136,35,41,61,100,42,129,130,118,223,13,122,51,12,208,2,117,57,206,225,76,4,46,42,89,80,129,124,172,216,15,119,180,70,251,225,193,15,109,121,133,135,182,121,37,89,156,239,197,68,229,85,148,74,69,75,112,61,76,61,104,107,202,252,154,39,30,195,235,74,169,92,16,66,238,252,26,170,7,37,152,36,52,147,44,60,92,232,220,33,99,38,148,108,234,142,221,28,136,210,146,159,181,85,50,42,239,74,210,1,91,85,118,0,78,153,13,162,47,78,141,139,82,22,109,236,117,106,81,47,232,7,115,136,103,17,115,73,215,25,139,33,24,72,172,235,130,16,85,86,236,36,189,38,37,26,199,239,51,170,203,193,229,140,2,59,59,230,18,218,230,59,246,177,34,93,206,44,39,79,236,59,97,216,124,159,159,161,150,169,62,39,254,170,137,103,242,97,254,104,77,221,156,5,219,183,196,128,105,214,163,66,7,60,196,45,73,151,206,235,235,203,83,4,154,35,223,175,221,178,142,213,115,100,5,67,104,255,223,99,216,8,15,7,19,95,85,12,50,108,104,129,208,60,171,154,18,238,123,191,12,184,195,20,46,153,62,123,132,66,123,64,227,131,212,70,162,26,187,176,196,6,70,169,221,5,131,168,138,133,14,202,90,225,247,98,102,135,240,202,139,243,168,218,66,20,190,22,252,49,180,215,223,130,255,242,31,164,119,34,78,39,234,127,65,158,23,127,160,206,81,165,253,133,142,14,97,115,214,100,188,167,153,251,111,198,4,45,99,190,155,68,90,246,100,106,209,211,123,116,115,49,178,110,90,212,244,126,121,11,150,115,16,27,7,128,78,77,166,19,128,69,119,129,251,249,242,214,124,173,179,142,75,59,10,93,149,77,140,64,226,32,159,126,29,160,0,223,28,247,240,5,138,220,107,229,88,158,94,211,251,76,85,138,203,188,2,37,234,157,183,110,231,219,119,243,185,223,83,184,233,213,131,63,70,191,89,119,192,174,97,143,195,219,142,221,246,225,215,220,136,179,29,185,159,217,185,182,92,219,190,188,56,69,7,72,193,33,146,107,70,209,2,137,92,64,75,69,179,61,91,111,184,154,141,218,182,249,175,17,195,193,61,142,204,19,162,127,142,185,191,47,177,49,233,190,6,252,75,50,216,94,191,122,239,240,92,205,177,168,182,107,120,138,53,62,168,82,37,144,12,127,21,44,171,181,84,37,23,79,222,157,9,209,232,5,50,151,121,180,193,122,2,207,148,12,252,84,108,129,110,134,212,193,146,245,172,251,206,111,101,146,232,206,54,11,0,0 };
 | 
			
		||||
const uint8_t JS_SLIDER_GZIP[869] PROGMEM = { 31,139,8,0,226,85,7,94,2,255,237,86,77,143,155,48,16,189,231,87,100,173,237,6,186,196,155,238,49,196,185,180,170,212,67,79,173,212,74,171,85,228,128,89,172,16,131,176,73,218,102,243,223,59,254,128,0,33,171,109,79,61,244,4,246,60,143,103,222,60,123,156,84,34,82,60,23,227,114,179,141,87,37,21,79,236,75,198,99,86,122,146,101,44,82,121,233,31,118,180,28,195,40,9,164,177,172,246,60,86,105,61,200,147,68,50,21,68,85,41,148,155,251,192,101,84,50,197,2,227,206,77,134,218,3,185,62,185,13,219,222,136,182,98,243,235,53,22,235,218,154,236,191,231,227,140,37,42,236,238,99,16,189,57,204,104,148,122,137,203,206,227,193,206,63,152,24,33,4,149,114,233,135,102,132,105,81,48,17,123,221,197,43,181,45,50,216,204,15,77,6,196,66,19,14,192,9,23,69,165,30,212,207,130,17,100,172,232,113,82,71,220,6,34,108,231,80,147,78,194,179,140,216,255,46,100,170,45,39,92,74,69,156,177,97,164,181,157,176,25,93,179,11,78,141,9,144,186,122,38,208,213,142,102,164,160,165,100,159,132,242,204,20,134,41,157,102,43,66,28,73,233,33,83,9,20,52,235,110,209,155,126,128,22,168,203,113,9,103,34,112,81,201,130,10,228,99,197,126,184,173,53,218,15,143,126,104,203,43,60,180,205,43,201,226,124,47,198,42,175,162,84,42,90,130,235,126,234,65,83,83,230,31,120,226,49,188,174,148,202,5,33,228,222,63,64,245,160,4,227,132,102,146,133,199,145,206,29,50,102,66,201,186,238,216,141,129,40,45,249,105,83,37,163,242,182,36,29,176,81,101,11,224,148,89,35,186,226,212,184,40,101,209,198,30,167,6,245,130,126,48,135,120,230,49,151,116,157,177,24,130,129,196,218,46,8,81,101,197,206,210,171,83,162,113,252,62,163,186,28,92,78,41,176,179,99,46,161,109,190,99,31,43,210,230,204,114,242,196,190,19,134,205,247,249,25,106,153,234,125,226,175,154,120,38,31,102,143,214,212,206,89,176,125,67,12,152,166,29,42,116,192,125,220,130,180,233,188,185,185,58,71,160,25,242,253,131,155,214,177,122,142,172,160,15,237,222,61,134,141,240,120,52,241,85,69,47,195,154,22,8,205,179,170,41,225,188,119,202,128,112,139,41,92,50,189,247,0,133,118,131,218,7,57,24,137,106,236,220,18,27,24,165,182,39,12,162,42,230,58,40,107,133,235,197,140,142,225,181,23,231,81,181,133,40,124,45,248,83,104,175,63,5,255,229,223,75,239,76,156,78,212,255,130,60,71,127,160,206,65,165,253,133,142,142,163,122,175,241,112,79,51,231,223,252,147,201,34,230,187,113,164,101,79,144,19,194,114,114,59,26,152,183,45,106,185,184,3,203,37,136,187,160,151,3,38,219,139,150,11,221,5,150,179,197,157,249,90,103,141,75,100,255,80,232,170,108,98,60,207,167,91,7,40,192,55,199,61,124,129,34,247,90,57,149,167,211,244,62,83,149,226,50,175,64,137,158,94,122,231,150,250,111,223,205,102,126,71,227,166,91,247,238,140,110,187,110,129,93,203,30,134,55,61,187,233,196,175,57,19,23,123,114,55,183,75,141,249,160,207,226,252,28,11,218,164,66,114,77,232,28,137,92,64,67,69,211,61,91,111,184,154,158,76,232,100,219,230,191,6,12,71,247,52,50,15,136,238,46,230,244,190,196,196,184,253,22,240,175,72,111,249,225,213,107,251,251,106,126,69,181,93,195,67,172,246,65,149,42,225,74,135,59,5,203,106,45,85,201,197,147,119,111,66,52,106,129,204,101,30,109,176,30,192,35,37,3,63,21,155,163,219,62,113,48,101,61,235,174,243,27,27,117,74,231,52,11,0,0 };
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										39
									
								
								src/dataTabbedcontentJS.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/dataTabbedcontentJS.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -21,7 +21,7 @@ def parse_arguments(args=None):
 | 
			
		||||
    parser = argparse.ArgumentParser(
 | 
			
		||||
        description="Prepares ESPUI header files by minifying and gzipping HTML, JS and CSS source files.")
 | 
			
		||||
    parser.add_argument("--auto", "--all", "-a", dest="auto", action="store_true",
 | 
			
		||||
                        help="Automatically find all source files in examples/gui/data/ and write C header files to src/")
 | 
			
		||||
                        help="Automatically find all source files in data/ and write C header files to src/")
 | 
			
		||||
    parser.add_argument("--source", "--sources", "-s", dest="sources", default=None,
 | 
			
		||||
                        help="Sources directory containing CSS or JS files OR one specific file to minify")
 | 
			
		||||
    parser.add_argument("--target", "-t", dest="target", default=None,
 | 
			
		||||
@@ -120,7 +120,7 @@ def check_args(args):
 | 
			
		||||
        sys.exit(abort)
 | 
			
		||||
 | 
			
		||||
def main(args):
 | 
			
		||||
    args.sources = os.path.realpath(args.sources or os.sep.join((os.path.dirname(os.path.realpath(__file__)), "..", "examples", "gui", "data")))
 | 
			
		||||
    args.sources = os.path.realpath(args.sources or os.sep.join((os.path.dirname(os.path.realpath(__file__)), "..", "data")))
 | 
			
		||||
    args.target  = os.path.realpath(args.target  or os.sep.join((os.path.dirname(os.path.realpath(__file__)), "..", "src")))
 | 
			
		||||
    check_args(args)
 | 
			
		||||
    if os.path.isfile(args.sources):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user