Compare commits
2 Commits
master
...
feature/vo
Author | SHA1 | Date | |
---|---|---|---|
bdb9bd3625 | |||
165db0f093 |
@ -1,129 +0,0 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
https://sidweb.nl/cms3/en/contact.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
165
LICENSE
@ -1,165 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
1
_config.yml
Normal file
@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
3
component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := src
|
||||
COMPONENT_SRCDIRS := src
|
||||
CXXFLAGS += -fno-rtti
|
@ -1,8 +0,0 @@
|
||||
# bundle exec jekyll serve --host=0.0.0.0
|
||||
|
||||
title: ESPAsyncWebServer
|
||||
description: "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040"
|
||||
remote_theme: pages-themes/cayman@v0.2.0
|
||||
plugins:
|
||||
- jekyll-remote-theme
|
||||
|
1979
docs/index.md
Before Width: | Height: | Size: 310 KiB |
Before Width: | Height: | Size: 295 KiB |
@ -1,13 +1,10 @@
|
||||
#include <DNSServer.h>
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
@ -15,44 +12,36 @@ DNSServer dnsServer;
|
||||
AsyncWebServer server(80);
|
||||
|
||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
||||
public:
|
||||
bool canHandle(__unused AsyncWebServerRequest* request) const override {
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
CaptiveRequestHandler() {}
|
||||
virtual ~CaptiveRequestHandler() {}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest* request) {
|
||||
AsyncResponseStream* response = request->beginResponseStream("text/html");
|
||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||
response->print("<p>This is out captive portal front page.</p>");
|
||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||
#endif
|
||||
response->print("</body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
if (!WiFi.softAP("esp-captive")) {
|
||||
Serial.println("Soft AP creation failed.");
|
||||
while (1)
|
||||
;
|
||||
bool canHandle(AsyncWebServerRequest *request){
|
||||
//request->addInterestingHeader("ANY");
|
||||
return true;
|
||||
}
|
||||
|
||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
#endif
|
||||
void handleRequest(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||
response->print("<p>This is out captive portal front page.</p>");
|
||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||
response->print("</body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
};
|
||||
|
||||
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
|
||||
// more handlers...
|
||||
|
||||
void setup(){
|
||||
//your other setup stuff...
|
||||
WiFi.softAP("esp-captive");
|
||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
|
||||
//more handlers...
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
void loop(){
|
||||
dnsServer.processNextRequest();
|
||||
}
|
||||
|
257
examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino
Normal file
@ -0,0 +1,257 @@
|
||||
// Defaulut is SPIFFS, FatFS: only on ESP32, also choose partition scheme w/ ffat.
|
||||
// Comment 2 lines below or uncomment only one of them
|
||||
|
||||
//#define USE_LittleFS
|
||||
//#define USE_FatFS // Only ESP32
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#ifdef ESP32
|
||||
#include <FS.h>
|
||||
#ifdef USE_LittleFS
|
||||
#define MYFS LITTLEFS
|
||||
#include "LITTLEFS.h"
|
||||
#elif defined(USE_FatFS)
|
||||
#define MYFS FFat
|
||||
#include "FFat.h"
|
||||
#else
|
||||
#define MYFS SPIFFS
|
||||
#include <SPIFFS.h>
|
||||
#endif
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#elif defined(ESP8266)
|
||||
#ifdef USE_LittleFS
|
||||
#include <FS.h>
|
||||
#define MYFS LittleFS
|
||||
#include <LittleFS.h>
|
||||
#elif defined(USE_FatFS)
|
||||
#error "FatFS only on ESP32 for now!"
|
||||
#else
|
||||
#define MYFS SPIFFS
|
||||
#endif
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SPIFFSEditor.h>
|
||||
|
||||
|
||||
// SKETCH BEGIN
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
AsyncEventSource events("/events");
|
||||
|
||||
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
|
||||
if(type == WS_EVT_CONNECT){
|
||||
Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
|
||||
client->printf("Hello Client %u :)", client->id());
|
||||
client->ping();
|
||||
} else if(type == WS_EVT_DISCONNECT){
|
||||
Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id());
|
||||
} else if(type == WS_EVT_ERROR){
|
||||
Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
|
||||
} else if(type == WS_EVT_PONG){
|
||||
Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
|
||||
} else if(type == WS_EVT_DATA){
|
||||
AwsFrameInfo * info = (AwsFrameInfo*)arg;
|
||||
String msg = "";
|
||||
if(info->final && info->index == 0 && info->len == len){
|
||||
//the whole message is in a single frame and we got all of it's data
|
||||
Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
|
||||
|
||||
if(info->opcode == WS_TEXT){
|
||||
for(size_t i=0; i < info->len; i++) {
|
||||
msg += (char) data[i];
|
||||
}
|
||||
} else {
|
||||
char buff[3];
|
||||
for(size_t i=0; i < info->len; i++) {
|
||||
sprintf(buff, "%02x ", (uint8_t) data[i]);
|
||||
msg += buff ;
|
||||
}
|
||||
}
|
||||
Serial.printf("%s\n",msg.c_str());
|
||||
|
||||
if(info->opcode == WS_TEXT)
|
||||
client->text("I got your text message");
|
||||
else
|
||||
client->binary("I got your binary message");
|
||||
} else {
|
||||
//message is comprised of multiple frames or the frame is split into multiple packets
|
||||
if(info->index == 0){
|
||||
if(info->num == 0)
|
||||
Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
|
||||
Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
}
|
||||
|
||||
Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
|
||||
|
||||
if(info->opcode == WS_TEXT){
|
||||
for(size_t i=0; i < len; i++) {
|
||||
msg += (char) data[i];
|
||||
}
|
||||
} else {
|
||||
char buff[3];
|
||||
for(size_t i=0; i < len; i++) {
|
||||
sprintf(buff, "%02x ", (uint8_t) data[i]);
|
||||
msg += buff ;
|
||||
}
|
||||
}
|
||||
Serial.printf("%s\n",msg.c_str());
|
||||
|
||||
if((info->index + len) == info->len){
|
||||
Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
if(info->final){
|
||||
Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
|
||||
if(info->message_opcode == WS_TEXT)
|
||||
client->text("I got your text message");
|
||||
else
|
||||
client->binary("I got your binary message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ssid = "*****";
|
||||
const char* password = "*****";
|
||||
const char* hostName = "esp-async";
|
||||
const char* http_username = "admin";
|
||||
const char* http_password = "admin";
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAP(hostName);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.printf("STA: Failed!\n");
|
||||
WiFi.disconnect(false);
|
||||
delay(1000);
|
||||
WiFi.begin(ssid, password);
|
||||
}
|
||||
|
||||
Serial.print(F("*CONNECTED* IP:"));
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
//Send OTA events to the browser
|
||||
ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); });
|
||||
ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); });
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
char p[32];
|
||||
sprintf(p, "Progress: %u%%\n", (progress/(total/100)));
|
||||
events.send(p, "ota");
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota");
|
||||
else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota");
|
||||
else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota");
|
||||
else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota");
|
||||
else if(error == OTA_END_ERROR) events.send("End Failed", "ota");
|
||||
});
|
||||
ArduinoOTA.setHostname(hostName);
|
||||
ArduinoOTA.begin();
|
||||
|
||||
MDNS.addService("http","tcp",80);
|
||||
|
||||
//FS
|
||||
#ifdef USE_FatFS
|
||||
if (MYFS.begin(false,"/ffat",3)) { //limit the RAM usage, bottom line 8kb + 4kb takes per each file, default is 10
|
||||
#else
|
||||
if (MYFS.begin()) {
|
||||
#endif
|
||||
Serial.print(F("FS mounted\n"));
|
||||
} else {
|
||||
Serial.print(F("FS mount failed\n"));
|
||||
}
|
||||
|
||||
ws.onEvent(onWsEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
events.onConnect([](AsyncEventSourceClient *client){
|
||||
client->send("hello!",NULL,millis(),1000);
|
||||
});
|
||||
server.addHandler(&events);
|
||||
|
||||
#ifdef ESP32
|
||||
server.addHandler(new SPIFFSEditor(MYFS, http_username,http_password));
|
||||
#elif defined(ESP8266)
|
||||
server.addHandler(new SPIFFSEditor(http_username,http_password, MYFS));
|
||||
#endif
|
||||
|
||||
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/plain", String(ESP.getFreeHeap()));
|
||||
});
|
||||
|
||||
server.serveStatic("/", MYFS, "/").setDefaultFile("index.htm");
|
||||
|
||||
server.onNotFound([](AsyncWebServerRequest *request){
|
||||
Serial.printf("NOT_FOUND: ");
|
||||
if(request->method() == HTTP_GET)
|
||||
Serial.printf("GET");
|
||||
else if(request->method() == HTTP_POST)
|
||||
Serial.printf("POST");
|
||||
else if(request->method() == HTTP_DELETE)
|
||||
Serial.printf("DELETE");
|
||||
else if(request->method() == HTTP_PUT)
|
||||
Serial.printf("PUT");
|
||||
else if(request->method() == HTTP_PATCH)
|
||||
Serial.printf("PATCH");
|
||||
else if(request->method() == HTTP_HEAD)
|
||||
Serial.printf("HEAD");
|
||||
else if(request->method() == HTTP_OPTIONS)
|
||||
Serial.printf("OPTIONS");
|
||||
else
|
||||
Serial.printf("UNKNOWN");
|
||||
Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
|
||||
|
||||
if(request->contentLength()){
|
||||
Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
|
||||
Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
|
||||
}
|
||||
|
||||
int headers = request->headers();
|
||||
int i;
|
||||
for(i=0;i<headers;i++){
|
||||
AsyncWebHeader* h = request->getHeader(i);
|
||||
Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
|
||||
}
|
||||
|
||||
int params = request->params();
|
||||
for(i=0;i<params;i++){
|
||||
AsyncWebParameter* p = request->getParam(i);
|
||||
if(p->isFile()){
|
||||
Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
|
||||
} else if(p->isPost()){
|
||||
Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
} else {
|
||||
Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
request->send(404);
|
||||
});
|
||||
server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
|
||||
if(!index)
|
||||
Serial.printf("UploadStart: %s\n", filename.c_str());
|
||||
Serial.printf("%s", (const char*)data);
|
||||
if(final)
|
||||
Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len);
|
||||
});
|
||||
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
|
||||
if(!index)
|
||||
Serial.printf("BodyStart: %u\n", total);
|
||||
Serial.printf("%s", (const char*)data);
|
||||
if(index + len == total)
|
||||
Serial.printf("BodyEnd: %u\n", total);
|
||||
});
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
ArduinoOTA.handle();
|
||||
ws.cleanupClients();
|
||||
}
|
3
examples/ESP_AsyncFSBrowser/data/.exclude.files
Normal file
@ -0,0 +1,3 @@
|
||||
/*.gz
|
||||
/edit_gz
|
||||
/.exclude.files
|
BIN
examples/ESP_AsyncFSBrowser/data/ace.ico.gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/acefull.js.gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/edit_gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/ESP_AsyncFSBrowser/data/folder/image.jpg
Normal file
After Width: | Height: | Size: 5.9 KiB |
1
examples/ESP_AsyncFSBrowser/data/folder/test.txt
Normal file
@ -0,0 +1 @@
|
||||
Test
|
131
examples/ESP_AsyncFSBrowser/data/index.htm
Normal file
@ -0,0 +1,131 @@
|
||||
<!--
|
||||
FSWebServer - Example Index Page
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the ESP8266WebServer library for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>WebSocketTester</title>
|
||||
<style type="text/css" media="screen">
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#dbg, #input_div, #input_el {
|
||||
font-family: monaco;
|
||||
font-size: 12px;
|
||||
line-height: 13px;
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
#dbg, #input_div {
|
||||
margin:0;
|
||||
padding:0;
|
||||
padding-left:4px;
|
||||
}
|
||||
|
||||
#input_el {
|
||||
width:98%;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border: 0px;
|
||||
}
|
||||
#input_el:focus {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var ws = null;
|
||||
function ge(s){ return document.getElementById(s);}
|
||||
function ce(s){ return document.createElement(s);}
|
||||
function stb(){ window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); }
|
||||
function sendBlob(str){
|
||||
var buf = new Uint8Array(str.length);
|
||||
for (var i = 0; i < str.length; ++i) buf[i] = str.charCodeAt(i);
|
||||
ws.send(buf);
|
||||
}
|
||||
function addMessage(m){
|
||||
var msg = ce("div");
|
||||
msg.innerText = m;
|
||||
ge("dbg").appendChild(msg);
|
||||
stb();
|
||||
}
|
||||
function startSocket(){
|
||||
ws = new WebSocket('ws://'+document.location.host+'/ws',['arduino']);
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.onopen = function(e){
|
||||
addMessage("Connected");
|
||||
};
|
||||
ws.onclose = function(e){
|
||||
addMessage("Disconnected");
|
||||
};
|
||||
ws.onerror = function(e){
|
||||
console.log("ws error", e);
|
||||
addMessage("Error");
|
||||
};
|
||||
ws.onmessage = function(e){
|
||||
var msg = "";
|
||||
if(e.data instanceof ArrayBuffer){
|
||||
msg = "BIN:";
|
||||
var bytes = new Uint8Array(e.data);
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
msg += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
} else {
|
||||
msg = "TXT:"+e.data;
|
||||
}
|
||||
addMessage(msg);
|
||||
};
|
||||
ge("input_el").onkeydown = function(e){
|
||||
stb();
|
||||
if(e.keyCode == 13 && ge("input_el").value != ""){
|
||||
ws.send(ge("input_el").value);
|
||||
ge("input_el").value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
function startEvents(){
|
||||
var es = new EventSource('/events');
|
||||
es.onopen = function(e) {
|
||||
addMessage("Events Opened");
|
||||
};
|
||||
es.onerror = function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
addMessage("Events Closed");
|
||||
}
|
||||
};
|
||||
es.onmessage = function(e) {
|
||||
addMessage("Event: " + e.data);
|
||||
};
|
||||
es.addEventListener('ota', function(e) {
|
||||
addMessage("Event[ota]: " + e.data);
|
||||
}, false);
|
||||
}
|
||||
function onBodyLoad(){
|
||||
startSocket();
|
||||
startEvents();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="body" onload="onBodyLoad()">
|
||||
<pre id="dbg"></pre>
|
||||
<div id="input_div">
|
||||
$<input type="text" value="" id="input_el">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
examples/ESP_AsyncFSBrowser/data/worker-css.js.gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/worker-html.js.gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/worker-javascript.js.gz
Normal file
BIN
examples/ESP_AsyncFSBrowser/data/worker-json.js.gz
Normal file
@ -1,124 +0,0 @@
|
||||
// Reproduced issue https://github.com/mathieucarbou/ESPAsyncWebServer/issues/26
|
||||
|
||||
#include <DNSServer.h>
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
DNSServer dnsServer;
|
||||
AsyncWebServer server(80);
|
||||
|
||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
||||
public:
|
||||
bool canHandle(__unused AsyncWebServerRequest* request) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void handleRequest(AsyncWebServerRequest* request) override {
|
||||
AsyncResponseStream* response = request->beginResponseStream("text/html");
|
||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||
response->print("<p>This is out captive portal front page.</p>");
|
||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||
#endif
|
||||
response->print("</body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
};
|
||||
|
||||
bool hit1 = false;
|
||||
bool hit2 = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
server
|
||||
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
Serial.println("Captive portal request...");
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
||||
#endif
|
||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
||||
#endif
|
||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
||||
#endif
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
||||
#endif
|
||||
request->send(200, "text/plain", "This is the captive portal");
|
||||
hit1 = true;
|
||||
})
|
||||
.setFilter(ON_AP_FILTER);
|
||||
|
||||
server
|
||||
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
Serial.println("Website request...");
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
||||
#endif
|
||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
||||
#endif
|
||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
||||
#endif
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
||||
#endif
|
||||
request->send(200, "text/plain", "This is the website");
|
||||
hit2 = true;
|
||||
})
|
||||
.setFilter(ON_STA_FILTER);
|
||||
|
||||
// assert(WiFi.softAP("esp-captive-portal"));
|
||||
// dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
// server.begin();
|
||||
// Serial.println("Captive portal started!");
|
||||
|
||||
// while (!hit1) {
|
||||
// dnsServer.processNextRequest();
|
||||
// yield();
|
||||
// }
|
||||
// delay(1000); // Wait for the client to process the response
|
||||
|
||||
// Serial.println("Captive portal opened, stopping it and connecting to WiFi...");
|
||||
// dnsServer.stop();
|
||||
// WiFi.softAPdisconnect();
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
WiFi.persistent(false);
|
||||
WiFi.begin("IoT");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString());
|
||||
#endif
|
||||
|
||||
server.begin();
|
||||
|
||||
// while (!hit2) {
|
||||
// delay(10);
|
||||
// }
|
||||
// delay(1000); // Wait for the client to process the response
|
||||
// ESP.restart();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Connect to AP and run in bash:
|
||||
*
|
||||
* > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws
|
||||
*
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <list>
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
Serial.printf("Client #%" PRIu32 " connected.\n", client->id());
|
||||
} else if (type == WS_EVT_DISCONNECT) {
|
||||
Serial.printf("Client #%" PRIu32 " disconnected.\n", client->id());
|
||||
} else if (type == WS_EVT_ERROR) {
|
||||
Serial.printf("Client #%" PRIu32 " error.\n", client->id());
|
||||
} else if (type == WS_EVT_DATA) {
|
||||
Serial.printf("Client #%" PRIu32 " len: %u\n", client->id(), len);
|
||||
} else if (type == WS_EVT_PONG) {
|
||||
Serial.printf("Client #%" PRIu32 " pong.\n", client->id());
|
||||
} else if (type == WS_EVT_PING) {
|
||||
Serial.printf("Client #%" PRIu32 " ping.\n", client->id());
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
|
||||
ws.onEvent(onWsEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
server.on("/close_all_ws_clients", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||
Serial.println("Closing all WebSocket clients...");
|
||||
ws.closeAll();
|
||||
request->send(200, "application/json", "{\"status\":\"all clients closed\"}");
|
||||
});
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/html", R"rawliteral(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<script>
|
||||
let ws = new WebSocket("ws://" + window.location.host + "/ws");
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
ws.onopen = function () {
|
||||
console.log("WebSocket connected");
|
||||
};
|
||||
});
|
||||
|
||||
function closeAllWsClients() {
|
||||
fetch("/close_all_ws_clients", {
|
||||
method : "POST",
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<body>
|
||||
<button onclick = "closeAllWsClients()" style = "width: 200px"> ws close all</button><p></p>
|
||||
</body>
|
||||
</html>
|
||||
)rawliteral");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelete(NULL);
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Connect to AP and run in bash:
|
||||
*
|
||||
* > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws
|
||||
*
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
size_t msgCount = 0;
|
||||
size_t window = 100;
|
||||
std::list<uint32_t> times;
|
||||
|
||||
void connect_wifi() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
|
||||
// Serial.print("Connecting");
|
||||
// while (WiFi.status() != WL_CONNECTED) {
|
||||
// delay(500);
|
||||
// Serial.print(".");
|
||||
// }
|
||||
// Serial.println();
|
||||
|
||||
// Serial.print("Connected, IP address: ");
|
||||
// Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void notFound(AsyncWebServerRequest* request) {
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
// initial stack
|
||||
char* stack_start;
|
||||
|
||||
void printStackSize() {
|
||||
char stack;
|
||||
Serial.print(F("stack size "));
|
||||
Serial.print(stack_start - &stack);
|
||||
Serial.print(F(" | Heap free:"));
|
||||
Serial.print(ESP.getFreeHeap());
|
||||
#ifdef ESP8266
|
||||
Serial.print(F(" frag:"));
|
||||
Serial.print(ESP.getHeapFragmentation());
|
||||
Serial.print(F(" maxFreeBlock:"));
|
||||
Serial.print(ESP.getMaxFreeBlockSize());
|
||||
#endif
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void onWsEventEmpty(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
|
||||
msgCount++;
|
||||
Serial.printf("count: %d\n", msgCount);
|
||||
|
||||
times.push_back(millis());
|
||||
while (times.size() > window)
|
||||
times.pop_front();
|
||||
if (times.size() == window)
|
||||
Serial.printf("%f req/s\n", 1000.0 * window / (times.back() - times.front()));
|
||||
|
||||
printStackSize();
|
||||
|
||||
client->text("PONG");
|
||||
}
|
||||
|
||||
void serve_upload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) {
|
||||
Serial.print("> onUpload ");
|
||||
Serial.print("index: ");
|
||||
Serial.print(index);
|
||||
Serial.print(" len:");
|
||||
Serial.print(len);
|
||||
Serial.print(" final:");
|
||||
Serial.print(final);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
char stack;
|
||||
stack_start = &stack;
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n\n\nStart");
|
||||
Serial.printf("stack_start: %p\n", stack_start);
|
||||
|
||||
connect_wifi();
|
||||
|
||||
server.onNotFound(notFound);
|
||||
|
||||
ws.onEvent(onWsEventEmpty);
|
||||
server.addHandler(&ws);
|
||||
|
||||
server.on("/upload", HTTP_POST, [](AsyncWebServerRequest* request) { request->send(200); }, serve_upload);
|
||||
|
||||
server.begin();
|
||||
Serial.println("Server started");
|
||||
}
|
||||
|
||||
String msg = "";
|
||||
uint32_t count = 0;
|
||||
void loop() {
|
||||
// ws.cleanupClients();
|
||||
// count += 1;
|
||||
// // Concatenate some string, and clear it after some time
|
||||
// static unsigned long millis_string = millis();
|
||||
// if (millis() - millis_string > 1) {
|
||||
// millis_string += 100;
|
||||
// if (count % 100 == 0) {
|
||||
// // printStackSize();
|
||||
// // Serial.print(msg);
|
||||
// msg = String();
|
||||
// } else {
|
||||
// msg += 'T';
|
||||
// }
|
||||
// }
|
||||
}
|
@ -1,737 +0,0 @@
|
||||
//
|
||||
// A simple server implementation showing how to:
|
||||
// * serve static messages
|
||||
// * read GET and POST parameters
|
||||
// * handle missing pages / 404s
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <AsyncMessagePack.h>
|
||||
#endif
|
||||
|
||||
#include <LittleFS.h>
|
||||
|
||||
const char* htmlContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
|
||||
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
|
||||
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
|
||||
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
|
||||
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
|
||||
dapibus elit, id varius sem dui id lacus.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
const size_t htmlContentLength = strlen_P(htmlContent);
|
||||
|
||||
const char* staticContent PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Sample HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, %IP%</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
AsyncWebServer server(80);
|
||||
AsyncEventSource events("/events");
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Middlewares
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// log incoming requests
|
||||
LoggingMiddleware requestLogger;
|
||||
|
||||
// CORS
|
||||
CorsMiddleware cors;
|
||||
|
||||
// maximum 5 requests per 10 seconds
|
||||
RateLimitMiddleware rateLimit;
|
||||
|
||||
// filter out specific headers from the incoming request
|
||||
HeaderFilterMiddleware headerFilter;
|
||||
|
||||
// remove all headers from the incoming request except the ones provided in the constructor
|
||||
HeaderFreeMiddleware headerFree;
|
||||
|
||||
// basicAuth
|
||||
AuthenticationMiddleware basicAuth;
|
||||
AuthenticationMiddleware basicAuthHash;
|
||||
|
||||
// simple digest authentication
|
||||
AuthenticationMiddleware digestAuth;
|
||||
AuthenticationMiddleware digestAuthHash;
|
||||
|
||||
// complex authentication which adds request attributes for the next middlewares and handler
|
||||
AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||
if (!request->authenticate("user", "password")) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
request->setAttribute("user", "Mathieu");
|
||||
request->setAttribute("role", "staff");
|
||||
|
||||
next();
|
||||
|
||||
request->getResponse()->addHeader("X-Rate-Limit", "200");
|
||||
});
|
||||
|
||||
AuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const char* PARAM_MESSAGE PROGMEM = "message";
|
||||
const char* SSE_HTLM PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Server-Sent Events</title>
|
||||
<script>
|
||||
if (!!window.EventSource) {
|
||||
var source = new EventSource('/events');
|
||||
source.addEventListener('open', function(e) {
|
||||
console.log("Events Connected");
|
||||
}, false);
|
||||
source.addEventListener('error', function(e) {
|
||||
if (e.target.readyState != EventSource.OPEN) {
|
||||
console.log("Events Disconnected");
|
||||
}
|
||||
}, false);
|
||||
source.addEventListener('message', function(e) {
|
||||
console.log("message", e.data);
|
||||
}, false);
|
||||
source.addEventListener('heartbeat', function(e) {
|
||||
console.log("heartbeat", e.data);
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Open your browser console!</h1>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
void notFound(AsyncWebServerRequest* request) {
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2");
|
||||
AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
|
||||
#endif
|
||||
|
||||
static const char characters[] = "0123456789ABCDEF";
|
||||
static size_t charactersIndex = 0;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
// WiFi.mode(WIFI_STA);
|
||||
// WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
|
||||
// if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
// Serial.printf("WiFi Failed!\n");
|
||||
// return;
|
||||
// }
|
||||
// Serial.print("IP Address: ");
|
||||
// Serial.println(WiFi.localIP());
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP("esp-captive");
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
LittleFS.begin(true);
|
||||
#else
|
||||
LittleFS.begin();
|
||||
#endif
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/index.txt", "w");
|
||||
if (f) {
|
||||
for (size_t c = 0; c < sizeof(characters); c++) {
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
f.print(characters[c]);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
File f = LittleFS.open("/index.html", "w");
|
||||
if (f) {
|
||||
f.print(staticContent);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
// curl -v -X GET http://192.168.4.1/handler-not-sending-response
|
||||
server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
// handler forgot to send a response to the client => 501 Not Implemented
|
||||
});
|
||||
|
||||
// This is possible to replace a response.
|
||||
// the previous one will be deleted.
|
||||
// response sending happens when the handler returns.
|
||||
// curl -v -X GET http://192.168.4.1/replace
|
||||
server.on("/replace", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world");
|
||||
// oups! finally we want to send a different response
|
||||
request->send(400, "text/plain", "validation error");
|
||||
#ifndef TARGET_RP2040
|
||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
||||
#endif
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Request header manipulations
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
// curl -v -X GET -H "x-remove-me: value" http://192.168.4.1/headers
|
||||
server.on("/headers", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
Serial.printf("Request Headers:\n");
|
||||
for (auto& h : request->getHeaders())
|
||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
||||
|
||||
// remove x-remove-me header
|
||||
request->removeHeader("x-remove-me");
|
||||
Serial.printf("Request Headers:\n");
|
||||
for (auto& h : request->getHeaders())
|
||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
||||
|
||||
std::vector<const char*> headers;
|
||||
request->getHeaderNames(headers);
|
||||
for (auto& h : headers)
|
||||
Serial.printf("Request Header Name: %s\n", h);
|
||||
|
||||
request->send(200);
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Middlewares at server level (will apply to all requests)
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
requestLogger.setOutput(Serial);
|
||||
|
||||
basicAuth.setUsername("admin");
|
||||
basicAuth.setPassword("admin");
|
||||
basicAuth.setRealm("MyApp");
|
||||
basicAuth.setAuthFailureMessage("Authentication failed");
|
||||
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
|
||||
basicAuth.generateHash();
|
||||
|
||||
basicAuthHash.setUsername("admin");
|
||||
basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin)
|
||||
basicAuthHash.setRealm("MyApp");
|
||||
basicAuthHash.setAuthFailureMessage("Authentication failed");
|
||||
basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);
|
||||
|
||||
digestAuth.setUsername("admin");
|
||||
digestAuth.setPassword("admin");
|
||||
digestAuth.setRealm("MyApp");
|
||||
digestAuth.setAuthFailureMessage("Authentication failed");
|
||||
digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
||||
digestAuth.generateHash();
|
||||
|
||||
digestAuthHash.setUsername("admin");
|
||||
digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass)
|
||||
digestAuthHash.setRealm("MyApp");
|
||||
digestAuthHash.setAuthFailureMessage("Authentication failed");
|
||||
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
||||
|
||||
rateLimit.setMaxRequests(5);
|
||||
rateLimit.setWindowSize(10);
|
||||
|
||||
headerFilter.filter("X-Remove-Me");
|
||||
headerFree.keep("X-Keep-Me");
|
||||
headerFree.keep("host");
|
||||
|
||||
cors.setOrigin("http://192.168.4.1");
|
||||
cors.setMethods("POST, GET, OPTIONS, DELETE");
|
||||
cors.setHeaders("X-Custom-Header");
|
||||
cors.setAllowCredentials(false);
|
||||
cors.setMaxAge(600);
|
||||
|
||||
#ifndef PERF_TEST
|
||||
// global middleware
|
||||
server.addMiddleware(&requestLogger);
|
||||
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
|
||||
#endif
|
||||
|
||||
// Test CORS preflight request
|
||||
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors
|
||||
server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
});
|
||||
|
||||
// curl -v -X GET -H "x-remove-me: value" http://192.168.4.1/middleware/test-header-filter
|
||||
// - requestLogger will log the incoming headers (including x-remove-me)
|
||||
// - headerFilter will remove x-remove-me header
|
||||
// - handler will log the remaining headers
|
||||
server.on("/middleware/test-header-filter", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
for (auto& h : request->getHeaders())
|
||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
||||
request->send(200);
|
||||
});
|
||||
|
||||
// curl -v -X GET -H "x-keep-me: value" http://192.168.4.1/middleware/test-header-free
|
||||
// - requestLogger will log the incoming headers (including x-keep-me)
|
||||
// - headerFree will remove all headers except x-keep-me and host
|
||||
// - handler will log the remaining headers (x-keep-me and host)
|
||||
server.on("/middleware/test-header-free", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
for (auto& h : request->getHeaders())
|
||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
||||
request->send(200);
|
||||
})
|
||||
.addMiddleware(&headerFree);
|
||||
|
||||
// basic authentication method
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic
|
||||
server.on("/middleware/auth-basic", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
})
|
||||
.addMiddleware(&basicAuth);
|
||||
|
||||
// basic authentication method with hash
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic-hash
|
||||
server.on("/middleware/auth-basic-hash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
})
|
||||
.addMiddleware(&basicAuthHash);
|
||||
|
||||
// digest authentication
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest
|
||||
server.on("/middleware/auth-digest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
})
|
||||
.addMiddleware(&digestAuth);
|
||||
|
||||
// digest authentication with hash
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest-hash
|
||||
server.on("/middleware/auth-digest-hash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello, world!");
|
||||
})
|
||||
.addMiddleware(&digestAuthHash);
|
||||
|
||||
// test digest auth with cors
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" --digest -u user:password http://192.168.4.1/middleware/auth-custom
|
||||
server.on("/middleware/auth-custom", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String buffer = "Hello ";
|
||||
buffer.concat(request->getAttribute("user"));
|
||||
buffer.concat(" with role: ");
|
||||
buffer.concat(request->getAttribute("role"));
|
||||
request->send(200, "text/plain", buffer);
|
||||
})
|
||||
.addMiddlewares({&complexAuth, &authz});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
// curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/redirect
|
||||
// curl -v -X POST -H "origin: http://192.168.4.1" http://192.168.4.1/redirect
|
||||
server.on("/redirect", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||
request->redirect("/");
|
||||
});
|
||||
|
||||
// PERF TEST:
|
||||
// > brew install autocannon
|
||||
// > autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/html", htmlContent);
|
||||
});
|
||||
|
||||
// curl -v -X GET http://192.168.4.1/index.txt
|
||||
server.serveStatic("/index.txt", LittleFS, "/index.txt");
|
||||
|
||||
// curl -v -X GET http://192.168.4.1/index-private.txt
|
||||
server.serveStatic("/index-private.txt", LittleFS, "/index.txt").setAuthentication("admin", "admin");
|
||||
|
||||
// ServeStatic static is used to serve static output which never changes over time.
|
||||
// This special endpoints automatyically adds caching headers.
|
||||
// If a template processor is used, it must enure that the outputed content will always be the ame over time and never changes.
|
||||
// Otherwise, do not use serveStatic.
|
||||
// Example below: IP never changes.
|
||||
// curl -v -X GET http://192.168.4.1/index-static.html
|
||||
server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String& var) -> String {
|
||||
if (var == "IP") {
|
||||
// for CI, commented out since H2 board doesn ot support WiFi
|
||||
// return WiFi.localIP().toString();
|
||||
// return WiFi.softAPIP().toString();
|
||||
return "127.0.0..1";
|
||||
}
|
||||
return emptyString;
|
||||
});
|
||||
|
||||
// to serve a template with dynamic content (output changes over time), use normal
|
||||
// Example below: content changes over tinme do not use serveStatic.
|
||||
// curl -v -X GET http://192.168.4.1/index-dynamic.html
|
||||
server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(LittleFS, "/index.html", "text/html", false, [](const String& var) -> String {
|
||||
if (var == "IP")
|
||||
return String(random(0, 1000));
|
||||
return emptyString;
|
||||
});
|
||||
});
|
||||
|
||||
// Issue #14: assert failed: tcp_update_rcv_ann_wnd (needs help to test fix)
|
||||
// > curl -v http://192.168.4.1/issue-14
|
||||
pinMode(4, OUTPUT);
|
||||
server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
digitalWrite(4, HIGH);
|
||||
request->send(LittleFS, "/index.txt", "text/pain");
|
||||
delay(500);
|
||||
digitalWrite(4, LOW);
|
||||
});
|
||||
|
||||
/*
|
||||
Chunked encoding test: sends 16k of characters.
|
||||
❯ curl -N -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/chunk
|
||||
*/
|
||||
server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
|
||||
if (index >= 16384)
|
||||
return 0;
|
||||
memset(buffer, characters[charactersIndex], maxLen);
|
||||
charactersIndex = (charactersIndex + 1) % sizeof(characters);
|
||||
return maxLen;
|
||||
});
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// curl -N -v -X GET http://192.168.4.1/chunked.html --output -
|
||||
// curl -N -v -X GET -H "if-none-match: 4272" http://192.168.4.1/chunked.html --output -
|
||||
server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String len = String(htmlContentLength);
|
||||
|
||||
if (request->header(asyncsrv::T_INM) == len) {
|
||||
request->send(304);
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
|
||||
Serial.printf("%u / %u\n", index, htmlContentLength);
|
||||
|
||||
// finished ?
|
||||
if (htmlContentLength <= index) {
|
||||
Serial.println("finished");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// serve a maximum of 1024 or maxLen bytes of the remaining content
|
||||
const int chunkSize = min((size_t)1024, min(maxLen, htmlContentLength - index));
|
||||
Serial.printf("sending: %u\n", chunkSize);
|
||||
|
||||
memcpy(buffer, htmlContent + index, chunkSize);
|
||||
|
||||
return chunkSize;
|
||||
});
|
||||
|
||||
response->addHeader(asyncsrv::T_Cache_Control, "public,max-age=60");
|
||||
response->addHeader(asyncsrv::T_ETag, len);
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
/*
|
||||
❯ curl -I -X HEAD http://192.168.4.1/download
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 1024
|
||||
Content-Type: application/octet-stream
|
||||
Connection: close
|
||||
Accept-Ranges: bytes
|
||||
*/
|
||||
// Ref: https://github.com/mathieucarbou/ESPAsyncWebServer/pull/80
|
||||
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (request->method() == HTTP_HEAD) {
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
|
||||
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
|
||||
response->addHeader(asyncsrv::T_Content_Length, 10);
|
||||
response->setContentLength(1024); // overrides previous one
|
||||
response->addHeader(asyncsrv::T_Content_Type, "foo");
|
||||
response->setContentType("application/octet-stream"); // overrides previous one
|
||||
// ...
|
||||
request->send(response);
|
||||
} else {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
// Send a GET request to <IP>/get?message=<message>
|
||||
server.on("/get", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
String message;
|
||||
if (request->hasParam(PARAM_MESSAGE)) {
|
||||
message = request->getParam(PARAM_MESSAGE)->value();
|
||||
} else {
|
||||
message = "No message sent";
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, GET: " + message);
|
||||
});
|
||||
|
||||
// Send a POST request to <IP>/post with a form field message set to <message>
|
||||
server.on("/post", HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||
String message;
|
||||
if (request->hasParam(PARAM_MESSAGE, true)) {
|
||||
message = request->getParam(PARAM_MESSAGE, true)->value();
|
||||
} else {
|
||||
message = "No message sent";
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, POST: " + message);
|
||||
});
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
// JSON
|
||||
|
||||
// sends JSON
|
||||
// curl -v -X GET http://192.168.4.1/json1
|
||||
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = "world";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// curl -v -X GET http://192.168.4.1/json2
|
||||
server.on("/json2", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncResponseStream* response = request->beginResponseStream("application/json");
|
||||
JsonDocument doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
root["foo"] = "bar";
|
||||
serializeJson(root, *response);
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
|
||||
// curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
|
||||
jsonHandler->setMethod(HTTP_POST | HTTP_PUT);
|
||||
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
|
||||
serializeJson(json, Serial);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = json.as<JsonObject>()["name"];
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// MessagePack
|
||||
|
||||
// receives MessagePack and sends MessagePack
|
||||
msgPackHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
|
||||
// JsonObject jsonObj = json.as<JsonObject>();
|
||||
// ...
|
||||
|
||||
AsyncMessagePackResponse* response = new AsyncMessagePackResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = "world";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
// sends MessagePack
|
||||
server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
AsyncMessagePackResponse* response = new AsyncMessagePackResponse();
|
||||
JsonObject root = response->getRoot().to<JsonObject>();
|
||||
root["hello"] = "world";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
#endif
|
||||
|
||||
events.onConnect([](AsyncEventSourceClient* client) {
|
||||
if (client->lastId()) {
|
||||
Serial.printf("SSE Client reconnected! Last message ID that it gat is: %" PRIu32 "\n", client->lastId());
|
||||
}
|
||||
client->send("hello!", NULL, millis(), 1000);
|
||||
});
|
||||
|
||||
server.on("/sse", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/html", SSE_HTLM);
|
||||
});
|
||||
|
||||
ws.onEvent([](AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
|
||||
(void)len;
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
Serial.println("ws connect");
|
||||
client->setCloseClientOnQueueFull(false);
|
||||
client->ping();
|
||||
} else if (type == WS_EVT_DISCONNECT) {
|
||||
Serial.println("ws disconnect");
|
||||
} else if (type == WS_EVT_ERROR) {
|
||||
Serial.println("ws error");
|
||||
} else if (type == WS_EVT_PONG) {
|
||||
Serial.println("ws pong");
|
||||
} else if (type == WS_EVT_DATA) {
|
||||
AwsFrameInfo* info = (AwsFrameInfo*)arg;
|
||||
String msg = "";
|
||||
if (info->final && info->index == 0 && info->len == len) {
|
||||
if (info->opcode == WS_TEXT) {
|
||||
data[len] = 0;
|
||||
Serial.printf("ws text: %s\n", (char*)data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// SSS endpoints
|
||||
// sends a message every 10 ms
|
||||
//
|
||||
// go to http://192.168.4.1/sse
|
||||
// > curl -v -N -H "Accept: text/event-stream" http://192.168.4.1/events
|
||||
//
|
||||
// some perf tests:
|
||||
// launch 16 concurrent workers for 30 seconds
|
||||
// > for i in {1..16}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
|
||||
//
|
||||
// With AsyncTCP, with 16 workers: a lot of Too many messages queued: deleting message
|
||||
//
|
||||
// Total: 119 events, 29.75000000000000000000 events / second
|
||||
// Total: 727 events, 181.75000000000000000000 events / second
|
||||
// Total: 1386 events, 346.50000000000000000000 events / second
|
||||
// Total: 1385 events, 346.25000000000000000000 events / second
|
||||
// Total: 1276 events, 319.00000000000000000000 events / second
|
||||
// Total: 1411 events, 352.75000000000000000000 events / second
|
||||
// Total: 1276 events, 319.00000000000000000000 events / second
|
||||
// Total: 1333 events, 333.25000000000000000000 events / second
|
||||
// Total: 1250 events, 312.50000000000000000000 events / second
|
||||
// Total: 1275 events, 318.75000000000000000000 events / second
|
||||
// Total: 1271 events, 317.75000000000000000000 events / second
|
||||
// Total: 1271 events, 317.75000000000000000000 events / second
|
||||
// Total: 1254 events, 313.50000000000000000000 events / second
|
||||
// Total: 1251 events, 312.75000000000000000000 events / second
|
||||
// Total: 1254 events, 313.50000000000000000000 events / second
|
||||
// Total: 1262 events, 315.50000000000000000000 events / second
|
||||
//
|
||||
// With AsyncTCP, with 10 workers:
|
||||
//
|
||||
// Total: 1875 events, 468.75000000000000000000 events / second
|
||||
// Total: 1870 events, 467.50000000000000000000 events / second
|
||||
// Total: 1871 events, 467.75000000000000000000 events / second
|
||||
// Total: 1875 events, 468.75000000000000000000 events / second
|
||||
// Total: 1871 events, 467.75000000000000000000 events / second
|
||||
// Total: 1805 events, 451.25000000000000000000 events / second
|
||||
// Total: 1803 events, 450.75000000000000000000 events / second
|
||||
// Total: 1873 events, 468.25000000000000000000 events / second
|
||||
// Total: 1872 events, 468.00000000000000000000 events / second
|
||||
// Total: 1805 events, 451.25000000000000000000 events / second
|
||||
//
|
||||
// With AsyncTCPSock, with 16 workers: ESP32 CRASH !!!
|
||||
//
|
||||
// With AsyncTCPSock, with 10 workers:
|
||||
//
|
||||
// Total: 1242 events, 310.50000000000000000000 events / second
|
||||
// Total: 1242 events, 310.50000000000000000000 events / second
|
||||
// Total: 1242 events, 310.50000000000000000000 events / second
|
||||
// Total: 1242 events, 310.50000000000000000000 events / second
|
||||
// Total: 1181 events, 295.25000000000000000000 events / second
|
||||
// Total: 1182 events, 295.50000000000000000000 events / second
|
||||
// Total: 1240 events, 310.00000000000000000000 events / second
|
||||
// Total: 1181 events, 295.25000000000000000000 events / second
|
||||
// Total: 1181 events, 295.25000000000000000000 events / second
|
||||
// Total: 1183 events, 295.75000000000000000000 events / second
|
||||
//
|
||||
server.addHandler(&events);
|
||||
|
||||
// Run: websocat ws://192.168.4.1/ws
|
||||
server.addHandler(&ws);
|
||||
|
||||
#if __has_include("ArduinoJson.h")
|
||||
server.addHandler(jsonHandler);
|
||||
server.addHandler(msgPackHandler);
|
||||
#endif
|
||||
|
||||
server.onNotFound(notFound);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
uint32_t lastSSE = 0;
|
||||
uint32_t deltaSSE = 10;
|
||||
|
||||
uint32_t lastWS = 0;
|
||||
uint32_t deltaWS = 100;
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
if (now - lastSSE >= deltaSSE) {
|
||||
events.send(String("ping-") + now, "heartbeat", now);
|
||||
lastSSE = millis();
|
||||
}
|
||||
if (now - lastWS >= deltaWS) {
|
||||
ws.printfAll("kp%.4f", (10.0 / 3.0));
|
||||
for (auto& client : ws.getClients()) {
|
||||
client.printf("kp%.4f", (10.0 / 3.0));
|
||||
}
|
||||
lastWS = millis();
|
||||
}
|
||||
}
|
BIN
examples/SmartSwitch/1.PNG
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
examples/SmartSwitch/2.PNG
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
examples/SmartSwitch/3.PNG
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
examples/SmartSwitch/4.PNG
Normal file
After Width: | Height: | Size: 18 KiB |
1177
examples/SmartSwitch/ESPAsyncWiFiManager.cpp
Normal file
283
examples/SmartSwitch/ESPAsyncWiFiManager.h
Normal file
@ -0,0 +1,283 @@
|
||||
/**************************************************************
|
||||
WiFiManager is a library for the ESP8266/Arduino platform
|
||||
(https://github.com/esp8266/Arduino) to enable easy
|
||||
configuration and reconfiguration of WiFi credentials using a Captive Portal
|
||||
inspired by:
|
||||
http://www.esp8266.com/viewtopic.php?f=29&t=2520
|
||||
https://github.com/chriscook8/esp-arduino-apboot
|
||||
https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced
|
||||
Built by AlexT https://github.com/tzapu
|
||||
Ported to Async Web Server by https://github.com/alanswx
|
||||
Licensed under MIT license
|
||||
**************************************************************/
|
||||
|
||||
#ifndef ESPAsyncWiFiManager_h
|
||||
#define ESPAsyncWiFiManager_h
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
|
||||
#else
|
||||
#include <WiFi.h>
|
||||
#include "esp_wps.h"
|
||||
#define ESP_WPS_MODE WPS_TYPE_PBC
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
//#define USE_EADNS //Uncomment to use ESPAsyncDNSServer
|
||||
#ifdef USE_EADNS
|
||||
#include <ESPAsyncDNSServer.h> //https://github.com/devyte/ESPAsyncDNSServer
|
||||
//https://github.com/me-no-dev/ESPAsyncUDP
|
||||
#else
|
||||
#include <DNSServer.h>
|
||||
#endif
|
||||
#include <memory>
|
||||
|
||||
// fix crash on ESP32 (see https://github.com/alanswx/ESPAsyncWiFiManager/issues/44)
|
||||
#if defined(ESP8266)
|
||||
typedef int wifi_ssid_count_t;
|
||||
#else
|
||||
typedef int16_t wifi_ssid_count_t;
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
}
|
||||
#else
|
||||
#if __has_include(<esp32/rom/rtc.h>)
|
||||
#include <esp32/rom/rtc.h>
|
||||
#else
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const char WFM_HTTP_HEAD[] PROGMEM = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/><title>{v}</title>";
|
||||
const char HTTP_STYLE[] PROGMEM = "<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;} .l{background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==\") no-repeat left center;background-size: 1em;}</style>";
|
||||
const char HTTP_SCRIPT[] PROGMEM = "<script>function c(l){document.getElementById('s').value=l.innerText||l.textContent;document.getElementById('p').focus();}</script>";
|
||||
const char HTTP_HEAD_END[] PROGMEM = "</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
|
||||
const char HTTP_PORTAL_OPTIONS[] PROGMEM = "<form action=\"/wifi\" method=\"get\"><button>Configure WiFi</button></form><br/><form action=\"/0wifi\" method=\"get\"><button>Configure WiFi (No Scan)</button></form><br/><form action=\"/i\" method=\"get\"><button>Info</button></form><br/><form action=\"/r\" method=\"post\"><button>Reset</button></form>";
|
||||
const char HTTP_ITEM[] PROGMEM = "<div><a href='#p' onclick='c(this)'>{v}</a> <span class='q {i}'>{r}%</span></div>";
|
||||
const char HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><input id='s' name='s' length=32 placeholder='SSID'><br/><input id='p' name='p' length=64 type='password' placeholder='password'><br/>";
|
||||
const char HTTP_FORM_PARAM[] PROGMEM = "<br/><input id='{i}' name='{n}' length={l} placeholder='{p}' value='{v}' {c}>";
|
||||
const char HTTP_FORM_END[] PROGMEM = "<br/><button type='submit'>save</button></form>";
|
||||
const char HTTP_SCAN_LINK[] PROGMEM = "<br/><div class=\"c\"><a href=\"/wifi\">Scan</a></div>";
|
||||
const char HTTP_SAVED[] PROGMEM = "<div>Credentials Saved<br />Trying to connect ESP to network.<br />If it fails reconnect to AP to try again</div>";
|
||||
const char HTTP_END[] PROGMEM = "</div></body></html>";
|
||||
|
||||
#define WIFI_MANAGER_MAX_PARAMS 10
|
||||
|
||||
class AsyncWiFiManagerParameter {
|
||||
public:
|
||||
AsyncWiFiManagerParameter(const char *custom);
|
||||
AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length);
|
||||
AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
|
||||
|
||||
const char *getID();
|
||||
const char *getValue();
|
||||
const char *getPlaceholder();
|
||||
int getValueLength();
|
||||
const char *getCustomHTML();
|
||||
private:
|
||||
const char *_id;
|
||||
const char *_placeholder;
|
||||
char *_value;
|
||||
int _length;
|
||||
const char *_customHTML;
|
||||
|
||||
void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
|
||||
|
||||
friend class AsyncWiFiManager;
|
||||
};
|
||||
|
||||
|
||||
class WiFiResult
|
||||
{
|
||||
public:
|
||||
bool duplicate;
|
||||
String SSID;
|
||||
uint8_t encryptionType;
|
||||
int32_t RSSI;
|
||||
uint8_t* BSSID;
|
||||
int32_t channel;
|
||||
bool isHidden;
|
||||
|
||||
WiFiResult()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class AsyncWiFiManager
|
||||
{
|
||||
public:
|
||||
#ifdef USE_EADNS
|
||||
AsyncWiFiManager(AsyncWebServer * server, AsyncDNSServer *dns);
|
||||
#else
|
||||
AsyncWiFiManager(AsyncWebServer * server, DNSServer *dns);
|
||||
#endif
|
||||
|
||||
void scan();
|
||||
String scanModal();
|
||||
void loop();
|
||||
void safeLoop();
|
||||
void criticalLoop();
|
||||
String infoAsString();
|
||||
|
||||
boolean autoConnect(unsigned long maxConnectRetries = 1, unsigned long retryDelayMs = 1000);
|
||||
boolean autoConnect(char const *apName, char const *apPassword = NULL, unsigned long maxConnectRetries = 1, unsigned long retryDelayMs = 1000);
|
||||
|
||||
//if you want to always start the config portal, without trying to connect first
|
||||
boolean startConfigPortal(char const *apName, char const *apPassword = NULL);
|
||||
void startConfigPortalModeless(char const *apName, char const *apPassword);
|
||||
|
||||
// get the AP name of the config portal, so it can be used in the callback
|
||||
String getConfigPortalSSID();
|
||||
|
||||
void resetSettings();
|
||||
|
||||
//sets timeout before webserver loop ends and exits even if there has been no setup.
|
||||
//usefully for devices that failed to connect at some point and got stuck in a webserver loop
|
||||
//in seconds setConfigPortalTimeout is a new name for setTimeout
|
||||
void setConfigPortalTimeout(unsigned long seconds);
|
||||
void setTimeout(unsigned long seconds);
|
||||
|
||||
//sets timeout for which to attempt connecting, usefull if you get a lot of failed connects
|
||||
void setConnectTimeout(unsigned long seconds);
|
||||
|
||||
//wether or not the wifi manager tries to connect to configured access point even when
|
||||
//configuration portal (ESP as access point) is running [default true/on]
|
||||
void setTryConnectDuringConfigPortal(boolean v);
|
||||
|
||||
|
||||
void setDebugOutput(boolean debug);
|
||||
//defaults to not showing anything under 8% signal quality if called
|
||||
void setMinimumSignalQuality(int quality = 8);
|
||||
//sets a custom ip /gateway /subnet configuration
|
||||
void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
|
||||
//sets config for a static IP
|
||||
void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn, IPAddress dns1=(uint32_t)0x00000000, IPAddress dns2=(uint32_t)0x00000000);
|
||||
//called when AP mode and config portal is started
|
||||
void setAPCallback( void (*func)(AsyncWiFiManager*) );
|
||||
//called when settings have been changed and connection was successful
|
||||
void setSaveConfigCallback( void (*func)(void) );
|
||||
//adds a custom parameter
|
||||
void addParameter(AsyncWiFiManagerParameter *p);
|
||||
//if this is set, it will exit after config, even if connection is unsucessful.
|
||||
void setBreakAfterConfig(boolean shouldBreak);
|
||||
//if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins)
|
||||
//TODO
|
||||
//if this is set, customise style
|
||||
void setCustomHeadElement(const char* element);
|
||||
//if this is true, remove duplicated Access Points - defaut true
|
||||
void setRemoveDuplicateAPs(boolean removeDuplicates);
|
||||
//sets a custom element to add to options page
|
||||
void setCustomOptionsElement(const char* element);
|
||||
|
||||
private:
|
||||
AsyncWebServer *server;
|
||||
#ifdef USE_EADNS
|
||||
AsyncDNSServer *dnsServer;
|
||||
#else
|
||||
DNSServer *dnsServer;
|
||||
#endif
|
||||
|
||||
|
||||
boolean _modeless;
|
||||
int scannow;
|
||||
int shouldscan;
|
||||
boolean needInfo = true;
|
||||
|
||||
//const int WM_DONE = 0;
|
||||
//const int WM_WAIT = 10;
|
||||
|
||||
//const String HTTP_HEAD = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>{v}</title>";
|
||||
|
||||
void setupConfigPortal();
|
||||
#ifdef NO_EXTRA_4K_HEAP
|
||||
void startWPS();
|
||||
#endif
|
||||
String pager;
|
||||
wl_status_t wifiStatus;
|
||||
const char* _apName = "no-net";
|
||||
const char* _apPassword = NULL;
|
||||
String _ssid = "";
|
||||
String _pass = "";
|
||||
unsigned long _configPortalTimeout = 0;
|
||||
unsigned long _connectTimeout = 0;
|
||||
unsigned long _configPortalStart = 0;
|
||||
|
||||
IPAddress _ap_static_ip;
|
||||
IPAddress _ap_static_gw;
|
||||
IPAddress _ap_static_sn;
|
||||
IPAddress _sta_static_ip;
|
||||
IPAddress _sta_static_gw;
|
||||
IPAddress _sta_static_sn;
|
||||
IPAddress _sta_static_dns1= (uint32_t)0x00000000;
|
||||
IPAddress _sta_static_dns2= (uint32_t)0x00000000;
|
||||
|
||||
int _paramsCount = 0;
|
||||
int _minimumQuality = -1;
|
||||
boolean _removeDuplicateAPs = true;
|
||||
boolean _shouldBreakAfterConfig = false;
|
||||
#ifdef NO_EXTRA_4K_HEAP
|
||||
boolean _tryWPS = false;
|
||||
#endif
|
||||
const char* _customHeadElement = "";
|
||||
const char* _customOptionsElement = "";
|
||||
|
||||
//String getEEPROMString(int start, int len);
|
||||
//void setEEPROMString(int start, int len, String string);
|
||||
|
||||
int status = WL_IDLE_STATUS;
|
||||
int connectWifi(String ssid, String pass);
|
||||
uint8_t waitForConnectResult();
|
||||
void setInfo();
|
||||
|
||||
String networkListAsString();
|
||||
|
||||
void handleRoot(AsyncWebServerRequest *);
|
||||
void handleWifi(AsyncWebServerRequest*,boolean scan);
|
||||
void handleWifiSave(AsyncWebServerRequest*);
|
||||
void handleInfo(AsyncWebServerRequest*);
|
||||
void handleReset(AsyncWebServerRequest*);
|
||||
void handleNotFound(AsyncWebServerRequest*);
|
||||
void handle204(AsyncWebServerRequest*);
|
||||
boolean captivePortal(AsyncWebServerRequest*);
|
||||
|
||||
// DNS server
|
||||
const byte DNS_PORT = 53;
|
||||
|
||||
//helpers
|
||||
int getRSSIasQuality(int RSSI);
|
||||
boolean isIp(String str);
|
||||
String toStringIp(IPAddress ip);
|
||||
|
||||
boolean connect;
|
||||
boolean _debug = true;
|
||||
|
||||
WiFiResult *wifiSSIDs;
|
||||
wifi_ssid_count_t wifiSSIDCount;
|
||||
boolean wifiSSIDscan;
|
||||
|
||||
boolean _tryConnectDuringConfigPortal = true;
|
||||
|
||||
void (*_apcallback)(AsyncWiFiManager*) = NULL;
|
||||
void (*_savecallback)(void) = NULL;
|
||||
|
||||
AsyncWiFiManagerParameter* _params[WIFI_MANAGER_MAX_PARAMS];
|
||||
|
||||
template <typename Generic>
|
||||
void DEBUG_WM(Generic text);
|
||||
|
||||
template <class T>
|
||||
auto optionalIPFromString(T *obj, const char *s) -> decltype( obj->fromString(s) ) {
|
||||
return obj->fromString(s);
|
||||
}
|
||||
auto optionalIPFromString(...) -> bool {
|
||||
DEBUG_WM("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
56
examples/SmartSwitch/PinOut_Notes.txt
Normal file
@ -0,0 +1,56 @@
|
||||
This application:
|
||||
D2 = 4; // DHT DATA I/O
|
||||
D3 = 0; // BUTTON - most modules have it populated on PCB
|
||||
D4 = 2; // LED (RELAY) - most modules have it populated, on ESP32 is with reversed logic levels
|
||||
|
||||
|
||||
|
||||
Pinout ESP12 (8266)
|
||||
D GPIO In Out Notes
|
||||
|
||||
D0 16 no interrupt no PWM or I2C support HIGH at boot used to wake up from deep sleep
|
||||
D1 5 OK OK often used as SCL (I2C)
|
||||
D2 4 OK OK often used as SDA (I2C)
|
||||
D3 0 PU OK pulled up connected to FLASH button, boot fails if pulled LOW
|
||||
D4 2 PU OK pulled up HIGH at boot connected to on-board LED, boot fails if pulled LOW
|
||||
D5 14 OK OK SPI (SCLK)
|
||||
D6 12 OK OK SPI (MISO)
|
||||
D7 13 OK OK SPI (MOSI)
|
||||
D8 15 pulled to GND OK SPI (CS) Boot fails if pulled HIGH
|
||||
RX 3 OK RX pin HIGH at boot
|
||||
TX 1 TX pin OK HIGH at boot debug output at boot, boot fails if pulled LOW
|
||||
A0 ADC0 Analog Input
|
||||
|
||||
|
||||
Pinout ESP32
|
||||
IO In Out Notes
|
||||
|
||||
0 PU OK pulled-up input, outputs PWM signal at boot
|
||||
1 TX OK debug output at boot
|
||||
2 OK OK connected to on-board LED
|
||||
3 OK RX HIGH at boot
|
||||
4 OK OK
|
||||
5 OK OK outputs PWM signal at boot
|
||||
|
||||
6-11 x x connected to the integrated SPI flash
|
||||
|
||||
12 OK OK boot fail if pulled high
|
||||
13 OK OK
|
||||
14 OK OK outputs PWM signal at boot
|
||||
15 OK OK outputs PWM signal at boot
|
||||
16 OK OK
|
||||
17 OK OK
|
||||
18 OK OK
|
||||
19 OK OK
|
||||
21 OK OK
|
||||
22 OK OK
|
||||
23 OK OK
|
||||
25 OK OK
|
||||
26 OK OK
|
||||
27 OK OK
|
||||
32 OK OK
|
||||
33 OK OK
|
||||
34 OK input only
|
||||
35 OK input only
|
||||
36 OK input only
|
||||
39 OK input only
|
19
examples/SmartSwitch/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
<img title="" src="1.PNG" alt="" width="137"> <img title="" src="2.PNG" alt="" width="138"> <img title="" src="3.PNG" alt="" width="150"> <img title="" src="4.PNG" alt="" width="150">
|
||||
|
||||
## SmartSwitch
|
||||
- Remote Temperature Control application with schedule
|
||||
(example: car block heater or car battery charger for winter)
|
||||
- Based on [ESP_AsyncFSBrowser](https://github.com/lorol/ESPAsyncWebServer/tree/master/examples/ESP_AsyncFSBrowser) example that uses embedded ACE editor
|
||||
- Wide browser compatibility, no extra server-side needed
|
||||
- HTTP server and WebSocket on same port
|
||||
- Standalone, no JS dependencies for the browser from Internet
|
||||
- [Ace Editor](https://github.com/ajaxorg/ace) embedded to source but also - editable, upgradeable see [extras folder](https://github.com/lorol/ESPAsyncWebServer/tree/master/extras)
|
||||
- Added [ESPAsyncWiFiManager](https://github.com/alanswx/ESPAsyncWiFiManager) and fallback AP mode after timeout
|
||||
- Real Time (NTP) w/ Time Zones. Sync from browser time if in AP mode
|
||||
- Memorized settings to EEPROM
|
||||
- Multiple clients can be connected at same time, they see each other' requests
|
||||
- Authentication variants including [Cookie-based](https://github.com/me-no-dev/ESPAsyncWebServer/pull/684) idea
|
||||
- Used [this Xtea implementation](https://github.com/franksmicro/Arduino/tree/master/libraries/Xtea) for getting a fancier Cookie token
|
||||
- Default credentials **smart : switch** or only **switch** as password
|
||||
- OTA included
|
||||
- Use the latest ESP8266 ESP32 cores from GitHub
|
750
examples/SmartSwitch/SmartSwitch.ino
Normal file
@ -0,0 +1,750 @@
|
||||
/*
|
||||
SmartSwitch application
|
||||
Based on ESP_AsyncFSBrowser
|
||||
Temperature Control for heater with schedule
|
||||
Main purpose - for winter outside car block heater or battery charger
|
||||
Wide browser compatibility, no other server-side needed.
|
||||
HTTP server and WebSocket, single port
|
||||
Standalone, no JS dependencies for the browser from Internet (I hope)
|
||||
Based on ESP_AsyncFSBrowser
|
||||
Real Time (NTP) w/ Time Zones
|
||||
Memorized settings to EEPROM
|
||||
Multiple clients can be connected at same time, they see each other requests
|
||||
Use latest ESP core lib (from Github)
|
||||
*/
|
||||
|
||||
// Defaulut is SPIFFS, FatFS: only on ESP32
|
||||
// Comment 2 lines below or uncomment only one of them
|
||||
|
||||
#define USE_LittleFS
|
||||
//#define USE_FatFS // select partition scheme w/ ffat!
|
||||
|
||||
|
||||
#define USE_WFM // to use ESPAsyncWiFiManager
|
||||
//#define DEL_WFM // delete Wifi credentials stored
|
||||
//(use once then comment and flash again), also HTTP /erase-wifi can do the same live
|
||||
|
||||
// AUTH COOKIE uses only the password and unsigned long MY_SECRET_NUMBER
|
||||
|
||||
#define http_username "smart"
|
||||
#define http_password "switch"
|
||||
|
||||
#define MY_SECRET_NUMBER 0xA217B02F
|
||||
|
||||
//See https://github.com/me-no-dev/ESPAsyncWebServer/pull/684
|
||||
//SSWI or other 4 chars
|
||||
#define USE_AUTH_COOKIE
|
||||
#define MY_COOKIE_DEL "SSWI=;Max-Age=-1;Path=/;"
|
||||
#define MY_COOKIE_PREF "SSWI="
|
||||
#define MY_COOKIE_SUFF ";Max-Age=31536000;Path=/;"
|
||||
|
||||
#ifndef USE_AUTH_COOKIE
|
||||
#define USE_AUTH_STAT //Base Auth for stat, /commands and SPIFFSEditor
|
||||
//#define USE_AUTH_WS //Base Auth also for WS, not very supported
|
||||
#endif
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#ifdef ESP32
|
||||
#include <FS.h>
|
||||
#ifdef USE_LittleFS
|
||||
#define HSTNM "ssw32-littlefs"
|
||||
#define MYFS LITTLEFS
|
||||
#include "LITTLEFS.h"
|
||||
#elif defined(USE_FatFS)
|
||||
#define HSTNM "ssw32-ffat"
|
||||
#define MYFS FFat
|
||||
#include "FFat.h"
|
||||
#else
|
||||
#define MYFS SPIFFS
|
||||
#include <SPIFFS.h>
|
||||
#define HSTNM "ssw32-spiffs"
|
||||
#endif
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#elif defined(ESP8266)
|
||||
#ifdef USE_LittleFS
|
||||
#include <FS.h>
|
||||
#define HSTNM "ssw8266-littlefs"
|
||||
#define MYFS LittleFS
|
||||
#include <LittleFS.h>
|
||||
#elif defined(USE_FatFS)
|
||||
#error "FatFS only on ESP32 for now!"
|
||||
#else
|
||||
#define HSTNM "ssw8266-spiffs"
|
||||
#define MYFS SPIFFS
|
||||
#endif
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#ifdef USE_WFM
|
||||
#include "ESPAsyncWiFiManager.h"
|
||||
#endif
|
||||
#include <SPIFFSEditor.h>
|
||||
#include <EEPROM.h>
|
||||
#include <Ticker.h>
|
||||
#include <DHT.h>
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
#include <stdint.h>
|
||||
#include "Xtea.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define RTC_UTC_TEST 1577836800 // Some Date
|
||||
#define MYTZ PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
|
||||
#define EESC 100 // fixed eeprom address for sched choice
|
||||
#define EECH 104 // fixed eeprom address to keep selected active channel, only for reference here
|
||||
#define EEBEGIN EECH + 1
|
||||
#define EEMARK 0x5A
|
||||
#define MEMMAX 2 // 0,1,2... last max index (only 3 channels)
|
||||
#define EEALL 512
|
||||
|
||||
#define HYST 0.5 // C +/- hysteresis
|
||||
|
||||
// DHT
|
||||
#define DHTTYPE DHT22 // DHT 11 // DHT 22, AM2302, AM2321 // DHT 21, AM2301
|
||||
#define DHTPIN 4 //D2
|
||||
|
||||
#define DHT_T_CORR -0.3 //Temperature offset compensation of the sensor (can be -)
|
||||
#define DHT_H_CORR -2.2 //Humidity offset compensation of the sensor
|
||||
|
||||
// SKETCH BEGIN MAIN DECLARATIONS
|
||||
|
||||
DHT dht(DHTPIN, DHTTYPE);
|
||||
|
||||
Ticker tim;
|
||||
AsyncWebServer server(80); //single port - easy for forwarding
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
#ifdef USE_WFM
|
||||
#ifdef USE_EADNS
|
||||
AsyncDNSServer dns;
|
||||
#else
|
||||
DNSServer dns;
|
||||
#endif
|
||||
|
||||
//Fallback timeout in seconds allowed to config or it creates an own AP, then serves 192.168.4.1
|
||||
#define FBTO 120
|
||||
const char* fbssid = "FBSSW";
|
||||
const char* fbpassword = "FBpassword4";
|
||||
|
||||
#else
|
||||
const char* ssid = "MYROUTERSSD";
|
||||
const char* password = "MYROUTERPASSWD";
|
||||
#endif
|
||||
|
||||
const char* hostName = HSTNM;
|
||||
|
||||
// RTC
|
||||
static timeval tv;
|
||||
static time_t now;
|
||||
|
||||
// HW I/O
|
||||
const int btnPin = 0; //D3
|
||||
const int ledPin = 2; //D4
|
||||
|
||||
#ifdef ESP32
|
||||
#define LED_ON 0x1
|
||||
#define LED_OFF 0x0
|
||||
#elif defined(ESP8266)
|
||||
#define LED_ON 0x0
|
||||
#define LED_OFF 0x1
|
||||
#endif
|
||||
|
||||
int btnState = HIGH;
|
||||
|
||||
// Globals
|
||||
uint8_t count = 0;
|
||||
uint8_t sched = 0; // automatic schedule
|
||||
byte memch = 0; // select memory "channel" to work with
|
||||
float t = 0;
|
||||
float h = 0;
|
||||
bool udht = false;
|
||||
bool heat_enabled_prev = false;
|
||||
bool ledState = LED_OFF;
|
||||
bool ledOut = LED_OFF;
|
||||
|
||||
struct EE_bl {
|
||||
byte memid; //here goes the EEMARK stamp
|
||||
uint8_t hstart;
|
||||
uint8_t mstart;
|
||||
uint8_t hstop;
|
||||
uint8_t mstop;
|
||||
float tempe;
|
||||
};
|
||||
|
||||
EE_bl ee = {0,0,0,0,0,0.1}; //populate as initial
|
||||
|
||||
// SUBS
|
||||
void writeEE(){
|
||||
ee.memid = EEMARK;
|
||||
//EEPROM.put(EESC, sched); // only separately when needed with commit()
|
||||
//EEPROM.put(EECH, memch); // not need to store and retrieve memch
|
||||
EEPROM.put(EEBEGIN + memch*sizeof(ee), ee);
|
||||
EEPROM.commit(); //needed for ESP8266?
|
||||
}
|
||||
|
||||
void readEE(){
|
||||
byte ChkEE;
|
||||
if (memch > MEMMAX) memch = 0;
|
||||
EEPROM.get(EEBEGIN + memch*sizeof(ee), ChkEE);
|
||||
if (ChkEE == EEMARK){ //otherwise stays with defaults
|
||||
EEPROM.get(EEBEGIN + memch*sizeof(ee), ee);
|
||||
EEPROM.get(EESC, sched);
|
||||
if (sched > MEMMAX + 1) sched = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void doOut(){
|
||||
if (ledOut != ledState){ // only if changed
|
||||
digitalWrite(ledPin, ledState); //consolidated here
|
||||
ledOut = ledState; //update
|
||||
if (ledState == LED_OFF){
|
||||
ws.textAll("led,ledoff");
|
||||
Serial.println(F("LED-OFF"));
|
||||
} else {
|
||||
ws.textAll("led,ledon");
|
||||
Serial.println(F("LED-ON"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showTime()
|
||||
{
|
||||
byte tmpch = 0;
|
||||
bool heat_enabled = false;
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
now = time(nullptr);
|
||||
const tm* tm = localtime(&now);
|
||||
ws.printfAll("Now,Clock,%02d:%02d,%d", tm->tm_hour, tm->tm_min, tm->tm_wday);
|
||||
if ((2==tm->tm_hour )&&(2==tm->tm_min)){
|
||||
configTzTime(MYTZ, "pool.ntp.org");
|
||||
Serial.print(F("Sync Clock at 02:02\n"));
|
||||
}
|
||||
Serial.printf("RTC: %02d:%02d\n", tm->tm_hour, tm->tm_min);
|
||||
|
||||
if (sched == 0){ // automatic
|
||||
if ((tm->tm_wday > 0)&&(tm->tm_wday < 6)) tmpch = 0; //Mon - Fri
|
||||
else if (tm->tm_wday == 6) tmpch = 1; //Sat
|
||||
else if (tm->tm_wday == 0) tmpch = 2; //Sun
|
||||
} else { // manual
|
||||
tmpch = sched - 1; //and stays
|
||||
}
|
||||
|
||||
if (tmpch != memch){ // update if different
|
||||
memch = tmpch;
|
||||
readEE();
|
||||
ws.printfAll("Now,Setting,%02d:%02d,%02d:%02d,%+2.1f", ee.hstart, ee.mstart, ee.hstop, ee.mstop, ee.tempe);
|
||||
}
|
||||
|
||||
// process smart switch by time and temperature
|
||||
uint16_t xmi = (uint16_t)(60*tm->tm_hour) + tm->tm_min; // max 24h = 1440min, current time
|
||||
uint16_t bmi = (uint16_t)(60*ee.hstart) + ee.mstart; // begin in minutes
|
||||
uint16_t emi = (uint16_t)(60*ee.hstop) + ee.mstop; // end in minutes
|
||||
|
||||
if (bmi == emi) heat_enabled = false;
|
||||
else { //enable smart if different
|
||||
|
||||
if (((bmi < emi)&&(bmi <= xmi)&&(xmi < emi))||
|
||||
((emi < bmi)&&((bmi <= xmi)||(xmi < emi)))){
|
||||
heat_enabled = true;
|
||||
} else heat_enabled = false;
|
||||
}
|
||||
|
||||
if (heat_enabled_prev){ // smart control (delayed one cycle)
|
||||
if (((t + HYST) < ee.tempe)&&(ledState == LED_OFF)){ // OFF->ON once
|
||||
ledState = LED_ON;
|
||||
ws.textAll("led,ledon");
|
||||
}
|
||||
if ((((t - HYST) > ee.tempe)&&(ledState == LED_ON))||(!heat_enabled)){ // ->OFF
|
||||
ledState = LED_OFF;
|
||||
ws.textAll("led,ledoff");
|
||||
}
|
||||
|
||||
Serial.printf(ledState == LED_ON ? "LED ON" : "LED OFF");
|
||||
Serial.print(F(", Smart enabled\n"));
|
||||
}
|
||||
heat_enabled_prev = heat_enabled; //update
|
||||
}
|
||||
|
||||
void updateDHT(){
|
||||
float h1 = dht.readHumidity();
|
||||
float t1 = dht.readTemperature(); //Celsius or dht.readTemperature(true) for Fahrenheit
|
||||
if (isnan(h1) || isnan(t1)){
|
||||
Serial.println(F("Failed to read from DHT sensor!"));
|
||||
} else {
|
||||
h = h1 + DHT_H_CORR;
|
||||
t = t1 + DHT_T_CORR;
|
||||
}
|
||||
}
|
||||
|
||||
void analogSample()
|
||||
{
|
||||
ws.printfAll("wpMeter,Arduino,%+2.1f,%2.1f,%d", t, h, heat_enabled_prev);
|
||||
Serial.printf("T/H.: %+2.1f°C/%2.1f%%,%d\n", t, h, heat_enabled_prev);
|
||||
}
|
||||
|
||||
void checkPhysicalButton()
|
||||
{
|
||||
if (digitalRead(btnPin) == LOW){
|
||||
if (btnState != LOW){ // btnState is used to avoid sequential toggles
|
||||
ledState = !ledState;
|
||||
}
|
||||
btnState = LOW;
|
||||
} else {
|
||||
btnState = HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
void mytimer(){
|
||||
++count; //200ms increments
|
||||
checkPhysicalButton();
|
||||
if ((count % 25) == 1){ // update temp every 5 seconds
|
||||
analogSample();
|
||||
udht = true;
|
||||
}
|
||||
if ((count % 50) == 0){ // update temp every 10 seconds
|
||||
ws.cleanupClients();
|
||||
}
|
||||
if (count >= 150){ // cycle every 30 sec
|
||||
showTime();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
unsigned long key[4] = {0x01F20304,0x05060708,0x090a0b0c,0x0d0e0f00};
|
||||
Xtea x(key);
|
||||
|
||||
void encip(String &mtk, unsigned long token){
|
||||
unsigned long res[2] = {(unsigned long)random(0xFFFFFFFF),token};
|
||||
x.encrypt(res);
|
||||
char buf1[18];
|
||||
sprintf(buf1, "%08X_%08X",res[0],res[1]); //8 bytes for encryping the IP cookie
|
||||
mtk = (String)buf1;
|
||||
}
|
||||
|
||||
unsigned long decip(const char *pch){
|
||||
unsigned long res[2] = {0,0};
|
||||
res[0] = strtoul(pch, NULL, 16);
|
||||
res[1] = strtoul(&pch[9], NULL, 16);
|
||||
x.decrypt(res);
|
||||
return res[1];
|
||||
}
|
||||
|
||||
bool myHandshake(AsyncWebServerRequest *request){ // false will 401
|
||||
bool rslt = false;
|
||||
if (request->hasHeader("Cookie")){
|
||||
String cookie = request->header("Cookie");
|
||||
Serial.println(cookie);
|
||||
|
||||
uint8_t pos = cookie.indexOf(MY_COOKIE_PREF);
|
||||
if (pos != -1){
|
||||
unsigned long ix = decip(cookie.substring(pos+5, pos+22).c_str());
|
||||
Serial.printf("Ask:%08X Got:%08X\n", MY_SECRET_NUMBER, ix);
|
||||
if (MY_SECRET_NUMBER == ix)
|
||||
rslt=true;
|
||||
} else rslt=false;
|
||||
} else rslt=false;
|
||||
Serial.printf(rslt ? "C-YES\n" : "C-NO\n");
|
||||
return rslt;
|
||||
}
|
||||
#endif
|
||||
|
||||
// server
|
||||
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
|
||||
if(type == WS_EVT_CONNECT){
|
||||
Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
|
||||
//client->printf("Hello Client %u :)", client->id());
|
||||
//client->ping();
|
||||
|
||||
IPAddress ip = client->remoteIP();
|
||||
Serial.printf("[%u] Connected from %d.%d.%d.%d\n", client->id(), ip[0], ip[1], ip[2], ip[3]);
|
||||
showTime();
|
||||
analogSample();
|
||||
if (ledState == LED_OFF) ws.textAll("led,ledoff");
|
||||
else ws.textAll("led,ledon");
|
||||
|
||||
ws.printfAll("Now,Setting,%02d:%02d,%02d:%02d,%+2.1f", ee.hstart, ee.mstart, ee.hstop, ee.mstop, ee.tempe);
|
||||
ws.printfAll("Now,sched,%d", sched);
|
||||
|
||||
} else if(type == WS_EVT_DISCONNECT){
|
||||
Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id());
|
||||
ws.textAll("Now,remoff");
|
||||
|
||||
} else if(type == WS_EVT_ERROR){
|
||||
Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
|
||||
} else if(type == WS_EVT_PONG){
|
||||
Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
|
||||
} else if(type == WS_EVT_DATA){
|
||||
AwsFrameInfo * info = (AwsFrameInfo*)arg;
|
||||
String msg = "";
|
||||
if(info->final && info->index == 0 && info->len == len){
|
||||
//the whole message is in a single frame and we got all of it's data
|
||||
Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
|
||||
|
||||
if(info->opcode == WS_TEXT){
|
||||
for(size_t i=0; i < info->len; i++){ //debug
|
||||
msg += (char) data[i];
|
||||
}
|
||||
if(data[0] == 'L'){ // LED
|
||||
if(data[1] == '1'){
|
||||
ledState = LED_ON;
|
||||
ws.textAll("led,ledon"); // for others
|
||||
}
|
||||
else if(data[1] == '0'){
|
||||
ledState = LED_OFF;
|
||||
ws.textAll("led,ledoff");
|
||||
}
|
||||
|
||||
} else if(data[0] == 'T'){ // timeset
|
||||
if (len > 11){
|
||||
data[3] = data[6] = data[9] = data[12] = 0; // cut strings
|
||||
ee.hstart = (uint8_t) atoi((const char *) &data[1]);
|
||||
ee.mstart = (uint8_t) atoi((const char *) &data[4]);
|
||||
ee.hstop = (uint8_t) atoi((const char *) &data[7]);
|
||||
ee.mstop = (uint8_t) atoi((const char *) &data[10]);
|
||||
Serial.printf("[%u] Timer set %02d:%02d - %02d:%02d\n", client->id(), ee.hstart, ee.mstart, ee.hstop, ee.mstop);
|
||||
writeEE();
|
||||
memch = 255; // to force showTime()to send Setting
|
||||
showTime();
|
||||
}
|
||||
} else if(data[0] == 'W'){ // temperatureset
|
||||
if (len > 3){
|
||||
if (ee.tempe != (float) atof((const char *) &data[1])){
|
||||
ee.tempe = (float) atof((const char *) &data[1]);
|
||||
Serial.printf("[%u] Temp set %+2.1f\n", client->id(), ee.tempe);
|
||||
writeEE();
|
||||
memch = 255; // to force showTime()to send Setting
|
||||
showTime();
|
||||
}
|
||||
}
|
||||
} else if ((data[0] == 'Z')&&(len > 2)){ // sched
|
||||
data[2] = 0;
|
||||
if (sched != (uint8_t) atoi((const char *) &data[1])){
|
||||
sched = (uint8_t) atoi((const char *) &data[1]);
|
||||
EEPROM.put(EESC, sched); //separately
|
||||
EEPROM.commit(); //needed for ESP8266?
|
||||
ws.printfAll("Now,sched,%d", sched);
|
||||
showTime();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
char buff[3];
|
||||
for(size_t i=0; i < info->len; i++){
|
||||
sprintf(buff, "%02x ", (uint8_t) data[i]);
|
||||
msg += buff ;
|
||||
}
|
||||
}
|
||||
Serial.printf("%s\n",msg.c_str());
|
||||
|
||||
if(info->opcode == WS_TEXT)
|
||||
client->text("I got your text message");
|
||||
else
|
||||
client->binary("I got your binary message");
|
||||
|
||||
} else {
|
||||
//message is comprised of multiple frames or the frame is split into multiple packets
|
||||
if(info->index == 0){
|
||||
if(info->num == 0)
|
||||
Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
|
||||
Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
}
|
||||
|
||||
Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
|
||||
|
||||
if(info->opcode == WS_TEXT){
|
||||
for(size_t i=0; i < len; i++){
|
||||
msg += (char) data[i];
|
||||
}
|
||||
} else {
|
||||
char buff[3];
|
||||
for(size_t i=0; i < len; i++){
|
||||
sprintf(buff, "%02x ", (uint8_t) data[i]);
|
||||
msg += buff ;
|
||||
}
|
||||
}
|
||||
Serial.printf("%s\n",msg.c_str());
|
||||
|
||||
if((info->index + len) == info->len){
|
||||
Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
|
||||
if(info->final){
|
||||
Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
|
||||
if(info->message_opcode == WS_TEXT)
|
||||
client->text("I got your text message");
|
||||
else
|
||||
client->binary("I got your binary message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// setup -----------------------------------
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
//Serial.setDebugOutput(true);
|
||||
|
||||
//Wifi
|
||||
#ifdef USE_WFM
|
||||
AsyncWiFiManager wifiManager(&server,&dns);
|
||||
#ifdef DEL_WFM
|
||||
wifiManager.resetSettings();
|
||||
#endif
|
||||
wifiManager.setTimeout(FBTO); // seconds to config or it creates an own AP, then browse 192.168.4.1
|
||||
if (!wifiManager.autoConnect(hostName)){
|
||||
Serial.print(F("*FALLBACK AP*\n"));
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(fbssid, fbpassword);
|
||||
// MDNS.begin(fbssid);
|
||||
// MDNS.addService("http","tcp",80); // Core SVN 5179 use STA as default interface in mDNS (#7042)
|
||||
}
|
||||
|
||||
#else
|
||||
// Manual simple STA mode to connect to known router
|
||||
//WiFi.mode(WIFI_AP_STA); // Core SVN 5179 use STA as default interface in mDNS (#7042)
|
||||
//WiFi.softAP(hostName); // Core SVN 5179 use STA as default interface in mDNS (#7042)
|
||||
WiFi.mode(WIFI_STA); // Core SVN 5179 use STA as default interface in mDNS (#7042)
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
Serial.print(F("STA: Failed!\n"));
|
||||
WiFi.disconnect(false);
|
||||
delay(1000);
|
||||
WiFi.begin(ssid, password);
|
||||
}
|
||||
#endif
|
||||
|
||||
Serial.print(F("*CONNECTED* OWN IP:"));
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
//DHT
|
||||
dht.begin();
|
||||
updateDHT(); //first reading takes time, hold here than the loop;
|
||||
|
||||
//Real Time
|
||||
time_t rtc = RTC_UTC_TEST;
|
||||
timeval tv = { rtc, 0 };
|
||||
//timezone tz = { 0, 0 }; //(insert) <#5194
|
||||
settimeofday(&tv, nullptr); //settimeofday(&tv, &tz); // <#5194
|
||||
configTzTime(MYTZ, "pool.ntp.org");
|
||||
|
||||
//MDNS (not needed)
|
||||
// MDNS.begin(hostName);
|
||||
// MDNS.addService("http","tcp",80); // Core SVN 5179 use STA as default interface in mDNS (#7042)
|
||||
|
||||
//I/O & DHT
|
||||
pinMode(ledPin, OUTPUT);
|
||||
pinMode(btnPin, INPUT_PULLUP);
|
||||
|
||||
//EE
|
||||
EEPROM.begin(EEALL);
|
||||
//EEPROM.get(EECH, memch); //current channel, no need
|
||||
readEE(); // populate structure if healthy
|
||||
Serial.printf("Timer set %02d:%02d - %02d:%02d\n", ee.hstart, ee.mstart, ee.hstop, ee.mstop);
|
||||
Serial.printf("Temp set %+2.1f\n", ee.tempe);
|
||||
|
||||
//FS
|
||||
#ifdef USE_FatFS
|
||||
if (MYFS.begin(false,"/ffat",3)){ //limit the RAM usage, bottom line 8kb + 4kb takes per each file, default is 10
|
||||
#else
|
||||
if (MYFS.begin()){
|
||||
#endif
|
||||
Serial.print(F("FS mounted\n"));
|
||||
} else {
|
||||
Serial.print(F("FS mount failed\n"));
|
||||
}
|
||||
|
||||
#ifdef USE_AUTH_WS
|
||||
ws.setAuthentication(http_username,http_password);
|
||||
#endif
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
ws.handleHandshake(myHandshake);
|
||||
#endif
|
||||
|
||||
ws.onEvent(onWsEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
#ifdef ESP32
|
||||
#ifdef USE_AUTH_STAT
|
||||
server.addHandler(new SPIFFSEditor(MYFS, http_username,http_password));
|
||||
#elif defined(USE_AUTH_COOKIE)
|
||||
server.addHandler(new SPIFFSEditor(MYFS)).setFilter(myHandshake);
|
||||
#else
|
||||
server.addHandler(new SPIFFSEditor(MYFS));
|
||||
#endif
|
||||
#elif defined(ESP8266)
|
||||
#ifdef USE_AUTH_STAT
|
||||
server.addHandler(new SPIFFSEditor(http_username,http_password,MYFS));
|
||||
#elif defined(USE_AUTH_COOKIE)
|
||||
server.addHandler(new SPIFFSEditor("","",MYFS)).setFilter(myHandshake);
|
||||
#else
|
||||
server.addHandler(new SPIFFSEditor("","",MYFS));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
server.on("/lg2n", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
|
||||
String ckx;
|
||||
encip(ckx, MY_SECRET_NUMBER);
|
||||
|
||||
AsyncWebServerResponse *response;
|
||||
|
||||
if(request->hasParam("lg0f",true)){
|
||||
response = request->beginResponse(200, "text/html;charset=utf-8", "<h1>Logged Out! <a href='/'>Back</a></h1>");
|
||||
response->addHeader("Cache-Control", "no-cache");
|
||||
response->addHeader("Set-Cookie", MY_COOKIE_DEL);
|
||||
|
||||
} else if(request->hasParam("pa2w",true) && (String(request->getParam("pa2w",true)->value().c_str()) == String(http_password))){
|
||||
response = request->beginResponse(301);
|
||||
response->addHeader("Location", "/");
|
||||
response->addHeader("Cache-Control", "no-cache");
|
||||
response->addHeader("Set-Cookie", MY_COOKIE_PREF + ckx + MY_COOKIE_SUFF);
|
||||
|
||||
} else response = request->beginResponse(200, "text/html;charset=utf-8", "<h1>Wrong password! <a href='/'>Back</a></h1>");
|
||||
|
||||
request->send(response);
|
||||
});
|
||||
#endif
|
||||
|
||||
// below paths need individual auth ////////////////////////////////////////////////
|
||||
|
||||
server.on("/free-ram", HTTP_GET, [](AsyncWebServerRequest *request){ // direct request->answer
|
||||
#ifdef USE_AUTH_STAT
|
||||
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
request->send(200, "text/plain", String(ESP.getMinFreeHeap()) + ':' + String(ESP.getFreeHeap()) + ':'+ String(ESP.getHeapSize()));
|
||||
#else
|
||||
request->send(200, "text/plain", String(ESP.getFreeHeap()));
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
}).setFilter(myHandshake);
|
||||
#else
|
||||
});
|
||||
#endif
|
||||
|
||||
server.on("/get-time", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
#ifdef USE_AUTH_STAT
|
||||
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
|
||||
#endif
|
||||
if(request->hasParam("btime")){
|
||||
time_t rtc = (request->getParam("btime")->value()).toInt();
|
||||
timeval tv = { rtc, 0 };
|
||||
settimeofday(&tv, nullptr);
|
||||
}
|
||||
request->send(200, "text/plain","Got browser time ...");
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
}).setFilter(myHandshake);
|
||||
#else
|
||||
});
|
||||
#endif
|
||||
|
||||
server.on("/hw-reset", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
#ifdef USE_AUTH_STAT
|
||||
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
|
||||
#endif
|
||||
request->onDisconnect([](){
|
||||
#ifdef ESP32
|
||||
ESP.restart();
|
||||
#elif defined(ESP8266)
|
||||
ESP.reset();
|
||||
#endif
|
||||
});
|
||||
request->send(200, "text/plain","Restarting ...");
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
}).setFilter(myHandshake);
|
||||
#else
|
||||
});
|
||||
#endif
|
||||
|
||||
server.on("/erase-wifi", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
#ifdef USE_AUTH_STAT
|
||||
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
|
||||
#endif
|
||||
request->onDisconnect([](){
|
||||
#ifdef ESP32
|
||||
/*
|
||||
//https://github.com/espressif/arduino-esp32/issues/400#issuecomment-499631249
|
||||
//WiFi.disconnect(true); // doesn't work on esp32, below needs #include "esp_wifi.h"
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //load the flash-saved configs
|
||||
esp_wifi_init(&cfg); //initiate and allocate wifi resources (does not matter if connection fails)
|
||||
if(esp_wifi_restore()!=ESP_OK){
|
||||
Serial.print(F("WiFi is not initialized by esp_wifi_init "));
|
||||
} else {
|
||||
Serial.print(F("WiFi Configurations Cleared!"));
|
||||
}
|
||||
*/
|
||||
WiFi.disconnect(true, true); // Works on esp32
|
||||
ESP.restart();
|
||||
#elif defined(ESP8266)
|
||||
WiFi.disconnect(true);
|
||||
ESP.reset();
|
||||
#endif
|
||||
});
|
||||
request->send(200, "text/plain","Erasing WiFi data ...");
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
}).setFilter(myHandshake);
|
||||
#else
|
||||
});
|
||||
#endif
|
||||
|
||||
// above paths need individual auth ////////////////////////////////////////////////
|
||||
|
||||
#ifdef USE_AUTH_COOKIE
|
||||
server.serveStatic("/", MYFS, "/").setDefaultFile("index.htm").setFilter(myHandshake);
|
||||
server.serveStatic("/", MYFS, "/login/").setDefaultFile("index.htm");
|
||||
#else
|
||||
#ifdef USE_AUTH_STAT
|
||||
server.serveStatic("/", MYFS, "/").setDefaultFile("index.htm").setAuthentication(http_username,http_password);
|
||||
#else
|
||||
server.serveStatic("/", MYFS, "/").setDefaultFile("index.htm");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
server.onNotFound([](AsyncWebServerRequest *request){ // nothing known
|
||||
Serial.print(F("NOT_FOUND: "));
|
||||
if (request->method() == HTTP_OPTIONS) request->send(200); //CORS
|
||||
else request->send(404);
|
||||
});
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");//CORS
|
||||
server.begin();
|
||||
|
||||
//Timer tick
|
||||
tim.attach(0.2, mytimer); //every 0.2s
|
||||
|
||||
//OTA
|
||||
ArduinoOTA.setHostname(hostName);
|
||||
ArduinoOTA.onStart([](){
|
||||
Serial.print(F("OTA Started ...\n"));
|
||||
MYFS.end(); // Clean FS
|
||||
ws.textAll("Now,OTA"); // for all clients
|
||||
ws.enable(false);
|
||||
ws.closeAll();
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
} // setup end
|
||||
|
||||
// loop -----------------------------------
|
||||
void loop(){
|
||||
if (udht){ // 5sec
|
||||
updateDHT();
|
||||
udht = false;
|
||||
}
|
||||
doOut();
|
||||
ArduinoOTA.handle();
|
||||
}
|
48
examples/SmartSwitch/Xtea.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Xtea.cpp - Xtea encryption/decryption
|
||||
Written by Frank Kienast in November, 2010
|
||||
https://github.com/franksmicro/Arduino/tree/master/libraries/Xtea
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "Xtea.h"
|
||||
|
||||
#define NUM_ROUNDS 32
|
||||
|
||||
Xtea::Xtea(unsigned long key[4])
|
||||
{
|
||||
_key[0] = key[0];
|
||||
_key[1] = key[1];
|
||||
_key[2] = key[2];
|
||||
_key[3] = key[3];
|
||||
}
|
||||
|
||||
void Xtea::encrypt(unsigned long v[2])
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned long v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
|
||||
|
||||
for (i=0; i < NUM_ROUNDS; i++)
|
||||
{
|
||||
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + _key[sum & 3]);
|
||||
sum += delta;
|
||||
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + _key[(sum>>11) & 3]);
|
||||
}
|
||||
|
||||
v[0]=v0; v[1]=v1;
|
||||
}
|
||||
|
||||
void Xtea::decrypt(unsigned long v[2])
|
||||
{
|
||||
unsigned int i;
|
||||
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*NUM_ROUNDS;
|
||||
|
||||
for (i=0; i < NUM_ROUNDS; i++)
|
||||
{
|
||||
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + _key[(sum>>11) & 3]);
|
||||
sum -= delta;
|
||||
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + _key[sum & 3]);
|
||||
}
|
||||
|
||||
v[0]=v0; v[1]=v1;
|
||||
}
|
||||
|
20
examples/SmartSwitch/Xtea.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Xtea.h - Crypto library
|
||||
Written by Frank Kienast in November, 2010
|
||||
https://github.com/franksmicro/Arduino/tree/master/libraries/Xtea
|
||||
*/
|
||||
#ifndef Xtea_h
|
||||
#define Xtea_h
|
||||
|
||||
|
||||
class Xtea
|
||||
{
|
||||
public:
|
||||
Xtea(unsigned long key[4]);
|
||||
void encrypt(unsigned long data[2]);
|
||||
void decrypt(unsigned long data[2]);
|
||||
private:
|
||||
unsigned long _key[4];
|
||||
};
|
||||
|
||||
#endif
|
3
examples/SmartSwitch/data/.exclude.files
Normal file
@ -0,0 +1,3 @@
|
||||
/*.gz
|
||||
/edit_gz
|
||||
/.exclude.files
|
BIN
examples/SmartSwitch/data/ace.ico.gz
Normal file
BIN
examples/SmartSwitch/data/acefull.js.gz
Normal file
BIN
examples/SmartSwitch/data/app.css.gz
Normal file
BIN
examples/SmartSwitch/data/app.min.js.gz
Normal file
BIN
examples/SmartSwitch/data/edit_gz
Normal file
BIN
examples/SmartSwitch/data/favicon.ico.gz
Normal file
626
examples/SmartSwitch/data/index.htm
Normal file
@ -0,0 +1,626 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>Smart Switch</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="apple-touch-icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="stylesheet" href="app.css" type="text/css" />
|
||||
<style>
|
||||
body {
|
||||
font-family: arial;
|
||||
color: #999
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 1%;
|
||||
padding-inline: 2%
|
||||
}
|
||||
|
||||
[id^="input-"] {
|
||||
width: 70%;
|
||||
text-align: center;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-bottom: 1px solid #2196f3;
|
||||
font-size: 22px;
|
||||
color: #2196f3;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
input[type=button] {
|
||||
background-color: #2196f3;
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 10px 10px;
|
||||
width: 62px;
|
||||
text-decoration: none;
|
||||
margin: 4px 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 34px;
|
||||
font-family: arial;
|
||||
font-weight: 700;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: #fff;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s
|
||||
}
|
||||
|
||||
input:checked+.slider {
|
||||
background-color: #2196f3
|
||||
}
|
||||
|
||||
input:focus+.slider {
|
||||
box-shadow: 0 0 1px #2196f3
|
||||
}
|
||||
|
||||
input:checked+.slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px)
|
||||
}
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.clk {
|
||||
font-size: 52px;
|
||||
color: #444;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.clk2 {
|
||||
font-size: 24px;
|
||||
color: #444
|
||||
}
|
||||
|
||||
.clear:after,
|
||||
.clear:before {
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
|
||||
.clear:after {
|
||||
clear: both
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
bottom: 0px;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
border: 0px solid #ccc
|
||||
}
|
||||
|
||||
.gauge {
|
||||
display: block;
|
||||
float: left
|
||||
}
|
||||
|
||||
#g1, #g2 {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
.son {
|
||||
color: green;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.soff {
|
||||
color: red;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.blinking {
|
||||
animation: blinkingText 1.2s infinite;
|
||||
-webkit-animation: blinkingText 1.2s infinite;
|
||||
}
|
||||
|
||||
|
||||
@keyframes blinkingText {
|
||||
0% {
|
||||
color: #ec0b0b;
|
||||
}
|
||||
49% {
|
||||
color: #ea7272;
|
||||
}
|
||||
60% {
|
||||
color: transparent;
|
||||
}
|
||||
99% {
|
||||
color: transparent;
|
||||
}
|
||||
100% {
|
||||
color: #ff0404;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding-left: 19px;
|
||||
padding-top: 4px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
|
||||
width: 12.5%;
|
||||
|
||||
user-select: none
|
||||
}
|
||||
|
||||
.container input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.container:hover input ~ .checkmark {
|
||||
background-color: #ccc
|
||||
}
|
||||
|
||||
.container input:checked ~ .checkmark {
|
||||
background-color: #2196F3
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none
|
||||
}
|
||||
|
||||
.container input:checked ~ .checkmark:after {
|
||||
display: block
|
||||
}
|
||||
|
||||
.container .checkmark:after {
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: white
|
||||
}
|
||||
*:focus {outline:none !important}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<table align="center">
|
||||
<tr align="center">
|
||||
<td colspan=3>
|
||||
<form name="sched">
|
||||
<label class="container" id="LZ0">Def
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z0"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ1">M-F
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z1"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ2">Sat
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z2"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ3">Sun
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z3"><span class="checkmark"></span></label>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td onmousedown="ent1Click();">
|
||||
<label for="input-temperature">Temp °C</label>
|
||||
<input readonly="readonly" id="input-temperature" />
|
||||
</td>
|
||||
<td onmousedown="ent2Click();">
|
||||
<label for="input-popup-start">Time On</label>
|
||||
<input readonly="readonly" id="input-popup-start" />
|
||||
</td>
|
||||
<td onmousedown="ent2Click();">
|
||||
<label for="input-popup-stop">Time Off</label>
|
||||
<input readonly="readonly" id="input-popup-stop" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<input type="button" id="W" value="Temp" onclick="button2Click(this);" />
|
||||
</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input id="cbStyle" type="checkbox" onclick="checkboxClick(this);"><span class="slider round"></span></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" id="T" value="Timer" onclick="buttonClick(this);" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<div id="reconnect">
|
||||
<input type="button" id="N" value="WSoff" onclick="statusWs();" />
|
||||
</div>
|
||||
</td>
|
||||
<td><span id="sid"></span><div id="free-ram"></div><div id="erase-wifi"></div><div id="hw-reset"></div><div id="get-time"></div></td>
|
||||
<td>
|
||||
<div id="ebut">
|
||||
<input type="button" id="E" value="WEdit" onclick="buttonEClick();" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<input type="button" id="X" value="xWiFi" onclick="loadDoc('erase-wifi', 1);"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" id="H" value="Heap" onclick="loadDoc('free-ram', 0);" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" id="R" value="HWrst" onclick="loadDoc('hw-reset', 1);"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="wrapper clear">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<div id="g2" class="gauge"></div>
|
||||
<div id="C" class="clk" onclick="getBtime();"></div>
|
||||
<div id="C2" class="clk2"></div>
|
||||
<input type="button" id="O" value="Logout" onclick="buttonOClick();" />
|
||||
</div>
|
||||
|
||||
<script src="app.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
const MYCORS = '192.168.1.12';
|
||||
var g1, g2;
|
||||
var Analog0 = new Array();
|
||||
var auto = true;
|
||||
|
||||
const successNotification = window.createNotification({
|
||||
positionClass: 'nfc-bottom-right',
|
||||
theme: 'info',
|
||||
showDuration: 3000
|
||||
});
|
||||
|
||||
const warningNotification = window.createNotification({
|
||||
positionClass: 'nfc-bottom-right',
|
||||
theme: 'warning',
|
||||
showDuration: 6000
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
console.log("DOM fully loaded and parsed");
|
||||
|
||||
document.getElementById("R").style.backgroundColor = "red";
|
||||
document.getElementById("X").style.backgroundColor = "red";
|
||||
|
||||
g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: -5.5,
|
||||
min: -40,
|
||||
max: 50,
|
||||
decimals: 1,
|
||||
labelFontColor: "#444",
|
||||
valueFontColor: "#444",
|
||||
title: "Temperature",
|
||||
titlePosition: "below",
|
||||
label: "°C",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
customSectors: [{
|
||||
color: "#328da8",
|
||||
lo: -40,
|
||||
hi: 0
|
||||
}, {
|
||||
color: "#32a852",
|
||||
lo: 0,
|
||||
hi: 35
|
||||
}, {
|
||||
color: "#ff4d4d",
|
||||
lo: 35,
|
||||
hi: 50
|
||||
}],
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: 40.8,
|
||||
min: 0,
|
||||
max: 100,
|
||||
decimals: 1,
|
||||
labelFontColor: "#444",
|
||||
valueFontColor: "#444",
|
||||
title: "Humidity",
|
||||
titlePosition: "below",
|
||||
label: "%",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
customSectors: [{
|
||||
color: "#ffc926",
|
||||
lo: 0,
|
||||
hi: 45
|
||||
}, {
|
||||
color: "#32a852",
|
||||
lo: 45,
|
||||
hi: 55
|
||||
}, {
|
||||
color: "#328da8",
|
||||
lo: 55,
|
||||
hi: 100
|
||||
}],
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var servurl = document.location.host;
|
||||
if (servurl.length < 5) servurl = MYCORS;
|
||||
var connection = new WebSocket('ws://' + servurl + '/ws', ['arduino']);
|
||||
|
||||
connection.onopen = function() {
|
||||
//connection.send('get_something');
|
||||
document.getElementById("sid").className = "son";
|
||||
document.getElementById('sid').innerHTML = "Smart Switch";
|
||||
console.log("connection opened");
|
||||
};
|
||||
|
||||
connection.onclose = function() {
|
||||
document.getElementById("sid").className = "blinking";
|
||||
document.getElementById('sid').innerHTML = "Detached";
|
||||
document.getElementById("N").value = "WSon";
|
||||
console.log("connection closed");
|
||||
};
|
||||
|
||||
connection.onerror = function(error) {
|
||||
document.getElementById("sid").className = "soff";
|
||||
document.getElementById('sid').innerHTML = "Detached";
|
||||
document.getElementById("N").value = "WSon";
|
||||
console.log('WebSocket Error ', error);
|
||||
};
|
||||
|
||||
connection.onmessage = function(evt) {
|
||||
// handle websocket message. update attributes or values of elements that match the name on incoming message
|
||||
console.log("msg rec", evt.data);
|
||||
var msgArray = evt.data.split(","); // split message by delimiter into a string array
|
||||
console.log("msgArray", msgArray[0]);
|
||||
console.log("msgArray", msgArray[1]);
|
||||
console.log("msgArray", msgArray[2]);
|
||||
console.log("msgArray", msgArray[3]);
|
||||
console.log("msgArray", msgArray[4]);
|
||||
var indicator = msgArray[1]; // the first element in the message array is the ID of the object to update
|
||||
console.log("indiactor", indicator);
|
||||
var a = document.getElementById('cbStyle');
|
||||
if (indicator) // if an object by the name of the message exists, update its value or its attributes
|
||||
{
|
||||
switch (msgArray[1]) {
|
||||
case "Arduino":
|
||||
console.log("Got Temp / Humidity");
|
||||
g1.refresh(msgArray[2], null);
|
||||
g2.refresh(msgArray[3], null);
|
||||
if (msgArray[4] == "1") document.getElementById("T").style.backgroundColor = "#32a852";
|
||||
else document.getElementById("T").style.backgroundColor = "#2196f3";
|
||||
var delta = (parseFloat(document.getElementById('input-temperature').value).toFixed(1) - parseFloat(msgArray[2]).toFixed(1));
|
||||
if (delta > 0.5) document.getElementById("W").style.backgroundColor = "red";
|
||||
else if (delta < -0.5) document.getElementById("W").style.backgroundColor = "#2196f3";
|
||||
break;
|
||||
case "Clock":
|
||||
var dn = parseInt(msgArray[3]);
|
||||
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
var dayName = days[dn] || '*'; // 0...6
|
||||
var shedtype = 0;
|
||||
if (dn == 0) shedtype = 3;
|
||||
else if (dn == 6) shedtype = 2;
|
||||
else if ((dn > 0) && (dn < 6)) shedtype = 1;
|
||||
document.getElementById('C').innerHTML = msgArray[2];
|
||||
document.getElementById('C2').innerHTML = dayName;
|
||||
if (auto) document.getElementById('LZ' + shedtype).classList.add('son');
|
||||
else document.getElementById('LZ' + shedtype).classList.remove('son');
|
||||
break;
|
||||
case "Setting":
|
||||
document.getElementById('input-popup-start').value = msgArray[2];
|
||||
document.getElementById('input-popup-stop').value = msgArray[3];
|
||||
document.getElementById('input-temperature').value = parseFloat(msgArray[4]).toFixed(1);
|
||||
|
||||
document.getElementById('input-popup-start').className = "";
|
||||
document.getElementById('input-popup-stop').className = "";
|
||||
document.getElementById('input-temperature').className = "";
|
||||
|
||||
tpick.attach("input-popup-start");
|
||||
tpick.attach("input-popup-stop");
|
||||
fpick.attach("input-temperature");
|
||||
warningNotification({
|
||||
message: 'Client UPD'
|
||||
});
|
||||
break;
|
||||
case "ledon":
|
||||
a.checked = true;
|
||||
break;
|
||||
case "ledoff":
|
||||
a.checked = false;
|
||||
break;
|
||||
case "remoff":
|
||||
successNotification({
|
||||
message: 'Client quit'
|
||||
});
|
||||
break;
|
||||
case "OTA":
|
||||
warningNotification({
|
||||
message: 'OTA begin'
|
||||
});
|
||||
statusWs();
|
||||
break;
|
||||
case "sched":
|
||||
document.sched.radio[msgArray[2]].checked = true;
|
||||
break;
|
||||
default:
|
||||
// unrecognized message type. do nothing
|
||||
break;
|
||||
} // switch
|
||||
} // if (indicator)
|
||||
}; // connection.onmessage
|
||||
|
||||
function buttonClick(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.send(e.id + document.getElementById("input-popup-start").value + "|" + document.getElementById("input-popup-stop").value + "|");
|
||||
successNotification({
|
||||
message: 'Timer REQ'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function button2Click(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.send(e.id + parseFloat(document.getElementById("input-temperature").value).toFixed(1) + "|");
|
||||
successNotification({
|
||||
message: 'Temp. REQ'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buttonEClick() {
|
||||
var murl = '/edit';
|
||||
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/edit'; //CORS
|
||||
successNotification({message: 'Editor'});
|
||||
window.open(murl, '_blank');
|
||||
}
|
||||
|
||||
function buttonOClick() {
|
||||
var murl = "";
|
||||
// If base auth
|
||||
//murl = document.location.href.replace("http://", "http://" + new Date().getTime() + "@");
|
||||
// If cookie auth
|
||||
murl += 'login/';
|
||||
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/login/'; //CORS
|
||||
warningNotification({ message: 'Logout'});
|
||||
window.open(murl, '_self');
|
||||
}
|
||||
|
||||
function checkboxClick(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
if (e.checked) connection.send('L1');
|
||||
else connection.send('L0');
|
||||
}
|
||||
}
|
||||
|
||||
function ent1Click() {
|
||||
document.getElementById("input-temperature").className = "blinking";
|
||||
}
|
||||
|
||||
function ent2Click() {
|
||||
document.getElementById("input-popup-stop").className = "blinking";
|
||||
document.getElementById("input-popup-start").className = "blinking";
|
||||
}
|
||||
|
||||
function handleClick(e) {
|
||||
if (e.value == 'Z0' ) auto = true;
|
||||
else auto = false;
|
||||
document.getElementById('L' + e.value).classList.remove('son');
|
||||
if (connection.readyState === WebSocket.OPEN) connection.send(e.value + "|");
|
||||
}
|
||||
|
||||
// XMLHttpRequest /non WebSocket/ command. same as command' div' id to get response to
|
||||
function loadDoc(cmd, r, param) {
|
||||
var par = param || '';
|
||||
var murl = '/' + cmd + par;
|
||||
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/' + cmd + par; //CORS
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
document.getElementById(cmd).innerHTML = this.responseText;
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", murl, true);
|
||||
xhttp.send();
|
||||
successNotification({
|
||||
message: 'Cmd: ' + cmd
|
||||
});
|
||||
if (r) { //restart?
|
||||
connection.close();
|
||||
document.getElementById("N").value = "WSon";
|
||||
}
|
||||
};
|
||||
|
||||
function getBtime() {
|
||||
loadDoc('get-time', 0, '?btime=' + Math.round(new Date().getTime()/1000));
|
||||
document.getElementById('C').innerHTML ='';
|
||||
document.getElementById('C2').innerHTML ='';
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function statusWs() {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.close();
|
||||
document.getElementById("N").value = "WSon";
|
||||
warningNotification({ message: 'WS Closed'});
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
examples/SmartSwitch/data/login/favicon.ico.gz
Normal file
36
examples/SmartSwitch/data/login/index.htm
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>Login</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="apple-touch-icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
<body style="background-color:#bbb;font-family:arial;">
|
||||
<center>
|
||||
<br><br>
|
||||
<h4>Login - Logout</h4>
|
||||
<form action="/lg2n" method="post">
|
||||
<input type="password" id="pwd" name="pa2w" placeholder="Enter the password"><br><br>
|
||||
<input type="checkbox" id="lof" name="lg0f" onclick="hidep()"><label for="lg0f">Logout</label><br><br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
<br><a href='/'>Back</a>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
function hidep() {
|
||||
var checkBox = document.getElementById("lof");
|
||||
var text = document.getElementById("pwd");
|
||||
if (checkBox.checked == true){
|
||||
text.style.display = "none";
|
||||
} else {
|
||||
text.style.display = "inline";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
examples/SmartSwitch/data/worker-css.js.gz
Normal file
BIN
examples/SmartSwitch/data/worker-html.js.gz
Normal file
BIN
examples/SmartSwitch/data/worker-javascript.js.gz
Normal file
BIN
examples/SmartSwitch/data/worker-json.js.gz
Normal file
2
examples/SmartSwitch/data_src/.exclude.files
Normal file
@ -0,0 +1,2 @@
|
||||
*.gz
|
||||
.exclude.files
|
30
examples/SmartSwitch/data_src/acefull.js
Normal file
1
examples/SmartSwitch/data_src/app.css
Normal file
533
examples/SmartSwitch/data_src/app.min.js
vendored
Normal file
@ -0,0 +1,533 @@
|
||||
var tpick={attach:function(target){var dig=document.getElementById(target).value.split(":");var t1=dig[0]||"23";var t2=dig[1]||"59";var uniqueID=0;while(document.getElementById("tpick-"+uniqueID)!=null){uniqueID=Math.floor(Math.random()*(100-2))+1;}
|
||||
var tw=document.createElement("div");tw.id="tpick-"+uniqueID;tw.classList.add("tpop");tw.dataset.target=target;tw.addEventListener("click",function(evt){if(evt.target.classList.contains("tpop")){this.classList.remove("show");}});var tp=document.createElement("div");tp.classList.add("tpicker");tp.appendChild(this.draw("h",t1));tp.appendChild(this.draw("m",t2));var bottom=document.createElement("div"),ok=document.createElement("input");ok.setAttribute("type","button");ok.value="OK";ok.addEventListener("click",function(){tpick.set(this);});bottom.classList.add("tpicker-btn");bottom.appendChild(ok);tp.appendChild(bottom);tw.appendChild(tp);document.body.appendChild(tw);var target=document.getElementById(target);target.dataset.dp=uniqueID;target.onfocus=function(){document.getElementById("tpick-"+this.dataset.dp).classList.add("show");};},draw:function(type,tv){var docket=document.createElement("div"),up=document.createElement("div"),down=document.createElement("div"),text=document.createElement("input");docket.classList.add("tpicker-"+type);up.classList.add("tpicker-up");down.classList.add("tpicker-down");up.innerHTML="︿";down.innerHTML="﹀";text.readOnly=true;text.setAttribute("type","text");if(type=="h"){text.value=tv;up.addEventListener("click",function(){tpick.spin("h",1,this);});down.addEventListener("click",function(){tpick.spin("h",0,this);});}else if(type=="m"){text.value=tv;up.addEventListener("click",function(){tpick.spin("m",1,this);});down.addEventListener("click",function(){tpick.spin("m",0,this);});}
|
||||
docket.appendChild(up);docket.appendChild(text);docket.appendChild(down);return docket;},spin:function(type,direction,el){var parent=el.parentElement,field=parent.getElementsByTagName("input")[0],value=field.value;if(type=="h"){value=parseInt(value);if(direction){value++;}else{value--;}
|
||||
if(value==-1){value=23;}else if(value>23){value=0;}}else if(type=="m"){value=parseInt(value);if(direction){value+=5;}else{value-=5;}
|
||||
if(value<0){value=55;}else if(value>59){value=0;}
|
||||
if(value<10){value="0"+value;}}
|
||||
field.value=('00'+value).substr(-2);},set:function(el){var parent=el.parentElement;while(parent.classList.contains("tpop")==false){parent=parent.parentElement;}
|
||||
var input=parent.querySelectorAll("input[type=text]");var time=input[0].value+":"+input[1].value;document.getElementById(parent.dataset.target).value=time;parent.classList.remove("show");}};var fpick={attach:function(target){var dig=document.getElementById(target).value.split(".");var t1=dig[0]||"1";var t2=dig[1]||"2";var uniqueID=0;while(document.getElementById("fpick-"+uniqueID)!=null){uniqueID=Math.floor(Math.random()*(100-2))+1;}
|
||||
var tw=document.createElement("div");tw.id="fpick-"+uniqueID;tw.classList.add("tpop");tw.dataset.target=target;tw.addEventListener("click",function(evt){if(evt.target.classList.contains("tpop")){this.classList.remove("show");}});var tp=document.createElement("div");tp.classList.add("fpicker");tp.appendChild(this.draw("h",t1));tp.appendChild(this.draw("m",t2));var bottom=document.createElement("div"),ok=document.createElement("input");ok.setAttribute("type","button");ok.value="OK";ok.addEventListener("click",function(){fpick.set(this);});bottom.classList.add("fpicker-btn");bottom.appendChild(ok);tp.appendChild(bottom);tw.appendChild(tp);document.body.appendChild(tw);var target=document.getElementById(target);target.dataset.dp=uniqueID;target.onfocus=function(){document.getElementById("fpick-"+this.dataset.dp).classList.add("show");};},draw:function(type,tv){var docket=document.createElement("div"),up=document.createElement("div"),down=document.createElement("div"),text=document.createElement("input");docket.classList.add("fpicker-"+type);up.classList.add("fpicker-up");down.classList.add("fpicker-down");up.innerHTML="︿";down.innerHTML="﹀";text.readOnly=true;text.setAttribute("type","text");if(type=="h"){text.value=tv;up.addEventListener("click",function(){fpick.spin("h",1,this);});down.addEventListener("click",function(){fpick.spin("h",0,this);});}else if(type=="m"){text.value=tv;up.addEventListener("click",function(){fpick.spin("m",1,this);});down.addEventListener("click",function(){fpick.spin("m",0,this);});}
|
||||
docket.appendChild(up);docket.appendChild(text);docket.appendChild(down);return docket;},spin:function(type,direction,el){var parent=el.parentElement,field=parent.getElementsByTagName("input")[0],value=field.value;if(type=="h"){value=parseInt(value);if(direction){value++;}else{value--;}
|
||||
if(value==-41){value=99;}else if(value>99){value=-40;}}else if(type=="m"){value=parseInt(value);if(direction){value++;}else{value--;}
|
||||
if(value<0){value=9;}else if(value>9){value=0;}}
|
||||
field.value=value;},set:function(el){var parent=el.parentElement;while(parent.classList.contains("tpop")==false){parent=parent.parentElement;}
|
||||
var input=parent.querySelectorAll("input[type=text]");var temperature=input[0].value+"."+input[1].value;document.getElementById(parent.dataset.target).value=temperature;parent.classList.remove("show");}};!function(t){function n(i){if(e[i])
|
||||
return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}
|
||||
var e={};n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:i})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=0)}
|
||||
([function(t,n,e){e(1),t.exports=e(4)},function(t,n,e){"use strict";var i=Object.assign||function(t){for(var n=1;n<arguments.length;n++){var e=arguments[n];for(var i in e)
|
||||
Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])}
|
||||
return t};e(2);var o=e(3);!function(t){function n(t){return t=i({},c,t),function(t){return["nfc-top-left","nfc-top-right","nfc-bottom-left","nfc-bottom-right"].indexOf(t)>-1}
|
||||
(t.positionClass)||(console.warn("An invalid notification position class has been specified."),t.positionClass=c.positionClass),t.onclick&&"function"!=typeof t.onclick&&(console.warn("Notification on click must be a function."),t.onclick=c.onclick),"number"!=typeof t.showDuration&&(t.showDuration=c.showDuration),(0,o.isString)(t.theme)&&0!==t.theme.length||(console.warn("Notification theme must be a string with length"),t.theme=c.theme),t}
|
||||
function e(t){return t=n(t),function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=n.title,i=n.message,c=r(t.positionClass);if(!e&&!i)
|
||||
return console.warn("Notification must contain a title or a message!");var a=(0,o.createElement)("div","ncf",t.theme);if(!0===t.closeOnClick&&a.addEventListener("click",function(){return c.removeChild(a)}),t.onclick&&a.addEventListener("click",function(n){return t.onclick(n)}),t.displayCloseButton){var s=(0,o.createElement)("button");s.innerText="X",!1===t.closeOnClick&&s.addEventListener("click",function(){return c.removeChild(a)}),(0,o.append)(a,s)}
|
||||
if((0,o.isString)(e)&&e.length&&(0,o.append)(a,(0,o.createParagraph)("ncf-title")(e)),(0,o.isString)(i)&&i.length&&(0,o.append)(a,(0,o.createParagraph)("nfc-message")(i)),(0,o.append)(c,a),t.showDuration&&t.showDuration>0){var l=setTimeout(function(){c.removeChild(a),0===c.querySelectorAll(".ncf").length&&document.body.removeChild(c)},t.showDuration);(t.closeOnClick||t.displayCloseButton)&&a.addEventListener("click",function(){return clearTimeout(l)})}}}
|
||||
function r(t){var n=document.querySelector("."+t);return n||(n=(0,o.createElement)("div","ncf-container",t),(0,o.append)(document.body,n)),n}
|
||||
var c={closeOnClick:!0,displayCloseButton:!1,positionClass:"nfc-top-right",onclick:!1,showDuration:3500,theme:"success"};t.createNotification?console.warn("Window already contains a create notification function. Have you included the script twice?"):t.createNotification=e}
|
||||
(window)},function(t,n,e){"use strict";!function(){function t(t){this.el=t;for(var n=t.className.replace(/^\s+|\s+$/g,"").split(/\s+/),i=0;i<n.length;i++)
|
||||
e.call(this,n[i])}
|
||||
if(!(void 0===window.Element||"classList"in document.documentElement)){var n=Array.prototype,e=n.push,i=n.splice,o=n.join;t.prototype={add:function(t){this.contains(t)||(e.call(this,t),this.el.className=this.toString())},contains:function(t){return-1!=this.el.className.indexOf(t)},item:function(t){return this[t]||null},remove:function(t){if(this.contains(t)){for(var n=0;n<this.length&&this[n]!=t;n++);i.call(this,n,1),this.el.className=this.toString()}},toString:function(){return o.call(this," ")},toggle:function(t){return this.contains(t)?this.remove(t):this.add(t),this.contains(t)}},window.DOMTokenList=t,function(t,n,e){Object.defineProperty?Object.defineProperty(t,n,{get:e}):t.__defineGetter__(n,e)}
|
||||
(Element.prototype,"classList",function(){return new t(this)})}}
|
||||
()},function(t,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i=n.partial=function(t){for(var n=arguments.length,e=Array(n>1?n-1:0),i=1;i<n;i++)
|
||||
e[i-1]=arguments[i];return function(){for(var n=arguments.length,i=Array(n),o=0;o<n;o++)
|
||||
i[o]=arguments[o];return t.apply(void 0,e.concat(i))}},o=(n.append=function(t){for(var n=arguments.length,e=Array(n>1?n-1:0),i=1;i<n;i++)
|
||||
e[i-1]=arguments[i];return e.forEach(function(n){return t.appendChild(n)})},n.isString=function(t){return"string"==typeof t},n.createElement=function(t){for(var n=arguments.length,e=Array(n>1?n-1:0),i=1;i<n;i++)
|
||||
e[i-1]=arguments[i];var o=document.createElement(t);return e.length&&e.forEach(function(t){return o.classList.add(t)}),o}),r=function(t,n){return t.innerText=n,t},c=function(t){for(var n=arguments.length,e=Array(n>1?n-1:0),c=1;c<n;c++)
|
||||
e[c-1]=arguments[c];return i(r,o.apply(void 0,[t].concat(e)))};n.createParagraph=function(){for(var t=arguments.length,n=Array(t),e=0;e<t;e++)
|
||||
n[e]=arguments[e];return c.apply(void 0,["p"].concat(n))}},function(t,n){}]);!function(a,b){"function"==typeof define&&define.amd?define("eve",function(){return b()}):"object"==typeof exports?module.exports=b():a.eve=b()}
|
||||
(this,function(){var a,b,c="0.4.2",d="hasOwnProperty",e=/[\.\/]/,f="*",g=function(){},h=function(a,b){return a-b},i={n:{}},j=function(c,d){c=String(c);var e,f=b,g=Array.prototype.slice.call(arguments,2),i=j.listeners(c),k=0,l=[],m={},n=[],o=a;a=c,b=0;for(var p=0,q=i.length;q>p;p++)"zIndex"in i[p]&&(l.push(i[p].zIndex),i[p].zIndex<0&&(m[i[p].zIndex]=i[p]));for(l.sort(h);l[k]<0;)
|
||||
if(e=m[l[k++]],n.push(e.apply(d,g)),b)
|
||||
return b=f,n;for(p=0;q>p;p++)
|
||||
if(e=i[p],"zIndex"in e)
|
||||
if(e.zIndex==l[k]){if(n.push(e.apply(d,g)),b)
|
||||
break;do
|
||||
if(k++,e=m[l[k]],e&&n.push(e.apply(d,g)),b)
|
||||
break;while(e)}else
|
||||
m[e.zIndex]=e;else if(n.push(e.apply(d,g)),b)
|
||||
break;return b=f,a=o,n.length?n:null};return j._events=i,j.listeners=function(a){var b,c,d,g,h,j,k,l,m=a.split(e),n=i,o=[n],p=[];for(g=0,h=m.length;h>g;g++){for(l=[],j=0,k=o.length;k>j;j++)
|
||||
for(n=o[j].n,c=[n[m[g]],n[f]],d=2;d--;)
|
||||
b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}
|
||||
return p},j.on=function(a,b){if(a=String(a),"function"!=typeof b)
|
||||
return function(){};for(var c=a.split(e),d=i,f=0,h=c.length;h>f;f++)
|
||||
d=d.n,d=d.hasOwnProperty(c[f])&&d[c[f]]||(d[c[f]]={n:{}});for(d.f=d.f||[],f=0,h=d.f.length;h>f;f++)
|
||||
if(d.f[f]==b)
|
||||
return g;return d.f.push(b),function(a){+a==+a&&(b.zIndex=+a)}},j.f=function(a){var b=[].slice.call(arguments,1);return function(){j.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},j.stop=function(){b=1},j.nt=function(b){return b?new RegExp("(?:\\.|\\/|^)"+b+"(?:\\.|\\/|$)").test(a):a},j.nts=function(){return a.split(e)},j.off=j.unbind=function(a,b){if(!a)
|
||||
return void(j._events=i={n:{}});var c,g,h,k,l,m,n,o=a.split(e),p=[i];for(k=0,l=o.length;l>k;k++)
|
||||
for(m=0;m<p.length;m+=h.length-2){if(h=[m,1],c=p[m].n,o[k]!=f)
|
||||
c[o[k]]&&h.push(c[o[k]]);else
|
||||
for(g in c)
|
||||
c[d](g)&&h.push(c[g]);p.splice.apply(p,h)}
|
||||
for(k=0,l=p.length;l>k;k++)
|
||||
for(c=p[k];c.n;){if(b){if(c.f){for(m=0,n=c.f.length;n>m;m++)
|
||||
if(c.f[m]==b){c.f.splice(m,1);break}
|
||||
!c.f.length&&delete c.f}
|
||||
for(g in c.n)
|
||||
if(c.n[d](g)&&c.n[g].f){var q=c.n[g].f;for(m=0,n=q.length;n>m;m++)
|
||||
if(q[m]==b){q.splice(m,1);break}
|
||||
!q.length&&delete c.n[g].f}}else{delete c.f;for(g in c.n)
|
||||
c.n[d](g)&&c.n[g].f&&delete c.n[g].f}
|
||||
c=c.n}},j.once=function(a,b){var c=function(){return j.unbind(a,c),b.apply(this,arguments)};return j.on(a,c)},j.version=c,j.toString=function(){return"You are running Eve "+c},j}),function(a,b){"function"==typeof define&&define.amd?define("raphael.core",["eve"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("eve")):a.Raphael=b(a.eve)}
|
||||
(this,function(a){function b(c){if(b.is(c,"function"))
|
||||
return t?c():a.on("raphael.DOMload",c);if(b.is(c,U))
|
||||
return b._engine.create[C](b,c.splice(0,3+b.is(c[0],S))).add(c);var d=Array.prototype.slice.call(arguments,0);if(b.is(d[d.length-1],"function")){var e=d.pop();return t?e.call(b._engine.create[C](b,d)):a.on("raphael.DOMload",function(){e.call(b._engine.create[C](b,d))})}
|
||||
return b._engine.create[C](b,arguments)}
|
||||
function c(a){if("function"==typeof a||Object(a)!==a)
|
||||
return a;var b=new a.constructor;for(var d in a)
|
||||
a[y](d)&&(b[d]=c(a[d]));return b}
|
||||
function d(a,b){for(var c=0,d=a.length;d>c;c++)
|
||||
if(a[c]===b)
|
||||
return a.push(a.splice(c,1)[0])}
|
||||
function e(a,b,c){function e(){var f=Array.prototype.slice.call(arguments,0),g=f.join("␀"),h=e.cache=e.cache||{},i=e.count=e.count||[];return h[y](g)?(d(i,g),c?c(h[g]):h[g]):(i.length>=1e3&&delete h[i.shift()],i.push(g),h[g]=a[C](b,f),c?c(h[g]):h[g])}
|
||||
return e}
|
||||
function f(){return this.hex}
|
||||
function g(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}
|
||||
return c}
|
||||
function h(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}
|
||||
function i(a,b,c,d,e,f,g,i,j){null==j&&(j=1),j=j>1?1:0>j?0:j;for(var k=j/2,l=12,m=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;l>p;p++){var q=k*m[p]+k,r=h(q,a,c,e,g),s=h(q,b,d,f,i),t=r*r+s*s;o+=n[p]*M.sqrt(t)}
|
||||
return k*o}
|
||||
function j(a,b,c,d,e,f,g,h,j){if(!(0>j||i(a,b,c,d,e,f,g,h)<j)){var k,l=1,m=l/2,n=l-m,o=.01;for(k=i(a,b,c,d,e,f,g,h,n);P(k-j)>o;)
|
||||
m/=2,n+=(j>k?1:-1)*m,k=i(a,b,c,d,e,f,g,h,n);return n}}
|
||||
function k(a,b,c,d,e,f,g,h){if(!(N(a,c)<O(e,g)||O(a,c)>N(e,g)||N(b,d)<O(f,h)||O(b,d)>N(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+O(a,c).toFixed(2)||n>+N(a,c).toFixed(2)||n<+O(e,g).toFixed(2)||n>+N(e,g).toFixed(2)||o<+O(b,d).toFixed(2)||o>+N(b,d).toFixed(2)||o<+O(f,h).toFixed(2)||o>+N(f,h).toFixed(2)))
|
||||
return{x:l,y:m}}}}
|
||||
function l(a,c,d){var e=b.bezierBBox(a),f=b.bezierBBox(c);if(!b.isBBoxIntersect(e,f))
|
||||
return d?0:[];for(var g=i.apply(0,a),h=i.apply(0,c),j=N(~~(g/5),1),l=N(~~(h/5),1),m=[],n=[],o={},p=d?0:[],q=0;j+1>q;q++){var r=b.findDotsAtSegment.apply(b,a.concat(q/j));m.push({x:r.x,y:r.y,t:q/j})}
|
||||
for(q=0;l+1>q;q++)
|
||||
r=b.findDotsAtSegment.apply(b,c.concat(q/l)),n.push({x:r.x,y:r.y,t:q/l});for(q=0;j>q;q++)
|
||||
for(var s=0;l>s;s++){var t=m[q],u=m[q+1],v=n[s],w=n[s+1],x=P(u.x-t.x)<.001?"y":"x",y=P(w.x-v.x)<.001?"y":"x",z=k(t.x,t.y,u.x,u.y,v.x,v.y,w.x,w.y);if(z){if(o[z.x.toFixed(4)]==z.y.toFixed(4))
|
||||
continue;o[z.x.toFixed(4)]=z.y.toFixed(4);var A=t.t+P((z[x]-t[x])/(u[x]-t[x]))*(u.t-t.t),B=v.t+P((z[y]-v[y])/(w[y]-v[y]))*(w.t-v.t);A>=0&&1.001>=A&&B>=0&&1.001>=B&&(d?p++:p.push({x:z.x,y:z.y,t1:O(A,1),t2:O(B,1)}))}}
|
||||
return p}
|
||||
function m(a,c,d){a=b._path2curve(a),c=b._path2curve(c);for(var e,f,g,h,i,j,k,m,n,o,p=d?0:[],q=0,r=a.length;r>q;q++){var s=a[q];if("M"==s[0])
|
||||
e=i=s[1],f=j=s[2];else{"C"==s[0]?(n=[e,f].concat(s.slice(1)),e=n[6],f=n[7]):(n=[e,f,e,f,i,j,i,j],e=i,f=j);for(var t=0,u=c.length;u>t;t++){var v=c[t];if("M"==v[0])
|
||||
g=k=v[1],h=m=v[2];else{"C"==v[0]?(o=[g,h].concat(v.slice(1)),g=o[6],h=o[7]):(o=[g,h,g,h,k,m,k,m],g=k,h=m);var w=l(n,o,d);if(d)
|
||||
p+=w;else{for(var x=0,y=w.length;y>x;x++)
|
||||
w[x].segment1=q,w[x].segment2=t,w[x].bez1=n,w[x].bez2=o;p=p.concat(w)}}}}}
|
||||
return p}
|
||||
function n(a,b,c,d,e,f){null!=a?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}
|
||||
function o(){return this.x+G+this.y+G+this.width+" × "+this.height}
|
||||
function p(a,b,c,d,e,f){function g(a){return((l*a+k)*a+j)*a}
|
||||
function h(a,b){var c=i(a,b);return((o*c+n)*c+m)*c}
|
||||
function i(a,b){var c,d,e,f,h,i;for(e=a,i=0;8>i;i++){if(f=g(e)-a,P(f)<b)
|
||||
return e;if(h=(3*l*e+2*k)*e+j,P(h)<1e-6)
|
||||
break;e-=f/h}
|
||||
if(c=0,d=1,e=a,c>e)
|
||||
return c;if(e>d)
|
||||
return d;for(;d>c;){if(f=g(e),P(f-a)<b)
|
||||
return e;a>f?c=e:d=e,e=(d-c)/2+c}
|
||||
return e}
|
||||
var j=3*b,k=3*(d-b)-j,l=1-j-k,m=3*c,n=3*(e-c)-m,o=1-m-n;return h(a,1/(200*f))}
|
||||
function q(a,b){var c=[],d={};if(this.ms=b,this.times=1,a){for(var e in a)
|
||||
a[y](e)&&(d[$(e)]=a[e],c.push($(e)));c.sort(ka)}
|
||||
this.anim=d,this.top=c[c.length-1],this.percents=c}
|
||||
function r(c,d,e,f,g,h){e=$(e);var i,j,k,l,m,o,q=c.ms,r={},s={},t={};if(f)
|
||||
for(w=0,x=fb.length;x>w;w++){var u=fb[w];if(u.el.id==d.id&&u.anim==c){u.percent!=e?(fb.splice(w,1),k=1):j=u,d.attr(u.totalOrigin);break}}
|
||||
else
|
||||
f=+s;for(var w=0,x=c.percents.length;x>w;w++){if(c.percents[w]==e||c.percents[w]>f*c.top){e=c.percents[w],m=c.percents[w-1]||0,q=q/c.top*(e-m),l=c.percents[w+1],i=c.anim[e];break}
|
||||
f&&d.attr(c.anim[c.percents[w]])}
|
||||
if(i){if(j)
|
||||
j.initstatus=f,j.start=new Date-j.ms*f;else{for(var z in i)
|
||||
if(i[y](z)&&(ca[y](z)||d.paper.customAttributes[y](z)))
|
||||
switch(r[z]=d.attr(z),null==r[z]&&(r[z]=ba[z]),s[z]=i[z],ca[z]){case S:t[z]=(s[z]-r[z])/q;break;case"colour":r[z]=b.getRGB(r[z]);var A=b.getRGB(s[z]);t[z]={r:(A.r-r[z].r)/q,g:(A.g-r[z].g)/q,b:(A.b-r[z].b)/q};break;case"path":var B=Ia(r[z],s[z]),C=B[1];for(r[z]=B[0],t[z]=[],w=0,x=r[z].length;x>w;w++){t[z][w]=[0];for(var E=1,F=r[z][w].length;F>E;E++)
|
||||
t[z][w][E]=(C[w][E]-r[z][w][E])/q}
|
||||
break;case"transform":var G=d._,J=Na(G[z],s[z]);if(J)
|
||||
for(r[z]=J.from,s[z]=J.to,t[z]=[],t[z].real=!0,w=0,x=r[z].length;x>w;w++)
|
||||
for(t[z][w]=[r[z][w][0]],E=1,F=r[z][w].length;F>E;E++)
|
||||
t[z][w][E]=(s[z][w][E]-r[z][w][E])/q;else{var K=d.matrix||new n,L={_:{transform:G.transform},getBBox:function(){return d.getBBox(1)}};r[z]=[K.a,K.b,K.c,K.d,K.e,K.f],La(L,s[z]),s[z]=L._.transform,t[z]=[(L.matrix.a-K.a)/q,(L.matrix.b-K.b)/q,(L.matrix.c-K.c)/q,(L.matrix.d-K.d)/q,(L.matrix.e-K.e)/q,(L.matrix.f-K.f)/q]}
|
||||
break;case"csv":var M=H(i[z])[I](v),N=H(r[z])[I](v);if("clip-rect"==z)
|
||||
for(r[z]=N,t[z]=[],w=N.length;w--;)
|
||||
t[z][w]=(M[w]-r[z][w])/q;s[z]=M;break;default:for(M=[][D](i[z]),N=[][D](r[z]),t[z]=[],w=d.paper.customAttributes[z].length;w--;)
|
||||
t[z][w]=((M[w]||0)-(N[w]||0))/q}
|
||||
var O=i.easing,P=b.easing_formulas[O];if(!P)
|
||||
if(P=H(O).match(Y),P&&5==P.length){var Q=P;P=function(a){return p(a,+Q[1],+Q[2],+Q[3],+Q[4],q)}}else
|
||||
P=la;if(o=i.start||c.start||+new Date,u={anim:c,percent:e,timestamp:o,start:o+(c.del||0),status:0,initstatus:f||0,stop:!1,ms:q,easing:P,from:r,diff:t,to:s,el:d,callback:i.callback,prev:m,next:l,repeat:h||c.times,origin:d.attr(),totalOrigin:g},fb.push(u),f&&!j&&!k&&(u.stop=!0,u.start=new Date-q*f,1==fb.length))
|
||||
return hb();k&&(u.start=new Date-u.ms*f),1==fb.length&&gb(hb)}
|
||||
a("raphael.anim.start."+d.id,d,c)}}
|
||||
function s(a){for(var b=0;b<fb.length;b++)
|
||||
fb[b].el.paper==a&&fb.splice(b--,1)}
|
||||
b.version="2.1.4",b.eve=a;var t,u,v=/[, ]+/,w={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},x=/\{(\d+)\}/g,y="hasOwnProperty",z={doc:document,win:window},A={was:Object.prototype[y].call(z.win,"Raphael"),is:z.win.Raphael},B=function(){this.ca=this.customAttributes={}},C="apply",D="concat",E="ontouchstart"in z.win||z.win.DocumentTouch&&z.doc instanceof DocumentTouch,F="",G=" ",H=String,I="split",J="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[I](G),K={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},L=H.prototype.toLowerCase,M=Math,N=M.max,O=M.min,P=M.abs,Q=M.pow,R=M.PI,S="number",T="string",U="array",V=Object.prototype.toString,W=(b._ISURL=/^url\(['"]?(.+?)['"]?\)$/i,/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i),X={NaN:1,Infinity:1,"-Infinity":1},Y=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,Z=M.round,$=parseFloat,_=parseInt,aa=H.prototype.toUpperCase,ba=b._availableAttrs={"arrow-end":"none","arrow-start":"none",blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/","letter-spacing":0,opacity:1,path:"M0,0",r:0,rx:0,ry:0,src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",transform:"",width:0,x:0,y:0},ca=b._availableAnimAttrs={blur:S,"clip-rect":"csv",cx:S,cy:S,fill:"colour","fill-opacity":S,"font-size":S,height:S,opacity:S,path:"path",r:S,rx:S,ry:S,stroke:"colour","stroke-opacity":S,"stroke-width":S,transform:"transform",width:S,x:S,y:S},da=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,ea={hs:1,rg:1},fa=/,?([achlmqrstvxz]),?/gi,ga=/([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,ha=/([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,ia=/(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/gi,ja=(b._radial_gradient=/^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,{}),ka=function(a,b){return $(a)-$(b)},la=function(a){return a},ma=b._rectPath=function(a,b,c,d,e){return e?[["M",a+e,b],["l",c-2*e,0],["a",e,e,0,0,1,e,e],["l",0,d-2*e],["a",e,e,0,0,1,-e,e],["l",2*e-c,0],["a",e,e,0,0,1,-e,-e],["l",0,2*e-d],["a",e,e,0,0,1,e,-e],["z"]]:[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]]},na=function(a,b,c,d){return null==d&&(d=c),[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]]},oa=b._getPath={path:function(a){return a.attr("path")},circle:function(a){var b=a.attrs;return na(b.cx,b.cy,b.r)},ellipse:function(a){var b=a.attrs;return na(b.cx,b.cy,b.rx,b.ry)},rect:function(a){var b=a.attrs;return ma(b.x,b.y,b.width,b.height,b.r)},image:function(a){var b=a.attrs;return ma(b.x,b.y,b.width,b.height)},text:function(a){var b=a._getBBox();return ma(b.x,b.y,b.width,b.height)},set:function(a){var b=a._getBBox();return ma(b.x,b.y,b.width,b.height)}},pa=b.mapPath=function(a,b){if(!b)
|
||||
return a;var c,d,e,f,g,h,i;for(a=Ia(a),e=0,g=a.length;g>e;e++)
|
||||
for(i=a[e],f=1,h=i.length;h>f;f+=2)
|
||||
c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a};if(b._g=z,b.type=z.win.SVGAngle||z.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML","VML"==b.type){var qa,ra=z.doc.createElement("div");if(ra.innerHTML='<v:shape adj="1"/>',qa=ra.firstChild,qa.style.behavior="url(#default#VML)",!qa||"object"!=typeof qa.adj)
|
||||
return b.type=F;ra=null}
|
||||
b.svg=!(b.vml="VML"==b.type),b._Paper=B,b.fn=u=B.prototype=b.prototype,b._id=0,b._oid=0,b.is=function(a,b){return b=L.call(b),"finite"==b?!X[y](+a):"array"==b?a instanceof Array:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||"array"==b&&Array.isArray&&Array.isArray(a)||V.call(a).slice(8,-1).toLowerCase()==b},b.angle=function(a,c,d,e,f,g){if(null==f){var h=a-d,i=c-e;return h||i?(180+180*M.atan2(-i,-h)/R+360)%360:0}
|
||||
return b.angle(a,c,f,g)-b.angle(d,e,f,g)},b.rad=function(a){return a%360*R/180},b.deg=function(a){return Math.round(180*a/R%360*1e3)/1e3},b.snapTo=function(a,c,d){if(d=b.is(d,"finite")?d:10,b.is(a,U)){for(var e=a.length;e--;)
|
||||
if(P(a[e]-c)<=d)
|
||||
return a[e]}else{a=+a;var f=c%a;if(d>f)
|
||||
return c-f;if(f>a-d)
|
||||
return c-f+a}
|
||||
return c};b.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}
|
||||
(/[xy]/g,function(a){var b=16*M.random()|0,c="x"==a?b:3&b|8;return c.toString(16)});b.setWindow=function(c){a("raphael.setWindow",b,z.win,c),z.win=c,z.doc=z.win.document,b._engine.initWin&&b._engine.initWin(z.win)};var sa=function(a){if(b.vml){var c,d=/^\s+|\s+$/g;try{var f=new ActiveXObject("htmlfile");f.write("<body>"),f.close(),c=f.body}catch(g){c=createPopup().document.body}
|
||||
var h=c.createTextRange();sa=e(function(a){try{c.style.color=H(a).replace(d,F);var b=h.queryCommandValue("ForeColor");return b=(255&b)<<16|65280&b|(16711680&b)>>>16,"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=z.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",z.doc.body.appendChild(i),sa=e(function(a){return i.style.color=a,z.doc.defaultView.getComputedStyle(i,F).getPropertyValue("color")})}
|
||||
return sa(a)},ta=function(){return"hsb("+[this.h,this.s,this.b]+")"},ua=function(){return"hsl("+[this.h,this.s,this.l]+")"},va=function(){return this.hex},wa=function(a,c,d){if(null==c&&b.is(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,c=a.g,a=a.r),null==c&&b.is(a,T)){var e=b.getRGB(a);a=e.r,c=e.g,d=e.b}
|
||||
return(a>1||c>1||d>1)&&(a/=255,c/=255,d/=255),[a,c,d]},xa=function(a,c,d,e){a*=255,c*=255,d*=255;var f={r:a,g:c,b:d,hex:b.rgb(a,c,d),toString:va};return b.is(e,"finite")&&(f.opacity=e),f};b.color=function(a){var c;return b.is(a,"object")&&"h"in a&&"s"in a&&"b"in a?(c=b.hsb2rgb(a),a.r=c.r,a.g=c.g,a.b=c.b,a.hex=c.hex):b.is(a,"object")&&"h"in a&&"s"in a&&"l"in a?(c=b.hsl2rgb(a),a.r=c.r,a.g=c.g,a.b=c.b,a.hex=c.hex):(b.is(a,"string")&&(a=b.getRGB(a)),b.is(a,"object")&&"r"in a&&"g"in a&&"b"in a?(c=b.rgb2hsl(a),a.h=c.h,a.s=c.s,a.l=c.l,c=b.rgb2hsb(a),a.v=c.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1)),a.toString=va,a},b.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-P(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],xa(e,f,g,d)},b.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var e,f,g,h,i;return a=a%360/60,i=2*b*(.5>c?c:1-c),h=i*(1-P(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],xa(e,f,g,d)},b.rgb2hsb=function(a,b,c){c=wa(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=N(a,b,c),g=f-O(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:ta}},b.rgb2hsl=function(a,b,c){c=wa(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=N(a,b,c),h=O(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:ua}},b._path2string=function(){return this.join(",").replace(fa,"$1")};b._preload=function(a,b){var c=z.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,z.doc.body.removeChild(this)},c.onerror=function(){z.doc.body.removeChild(this)},z.doc.body.appendChild(c),c.src=a};b.getRGB=e(function(a){if(!a||(a=H(a)).indexOf("-")+1)
|
||||
return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:f};if("none"==a)
|
||||
return{r:-1,g:-1,b:-1,hex:"none",toString:f};!(ea[y](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=sa(a));var c,d,e,g,h,i,j=a.match(W);return j?(j[2]&&(e=_(j[2].substring(5),16),d=_(j[2].substring(3,5),16),c=_(j[2].substring(1,3),16)),j[3]&&(e=_((h=j[3].charAt(3))+h,16),d=_((h=j[3].charAt(2))+h,16),c=_((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4][I](da),c=$(i[0]),"%"==i[0].slice(-1)&&(c*=2.55),d=$(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=$(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=$(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5][I](da),c=$(i[0]),"%"==i[0].slice(-1)&&(c*=2.55),d=$(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=$(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(c/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=$(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),b.hsb2rgb(c,d,e,g)):j[6]?(i=j[6][I](da),c=$(i[0]),"%"==i[0].slice(-1)&&(c*=2.55),d=$(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=$(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(c/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=$(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),b.hsl2rgb(c,d,e,g)):(j={r:c,g:d,b:e,toString:f},j.hex="#"+(16777216|e|d<<8|c<<16).toString(16).slice(1),b.is(g,"finite")&&(j.opacity=g),j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:f}},b),b.hsb=e(function(a,c,d){return b.hsb2rgb(a,c,d).hex}),b.hsl=e(function(a,c,d){return b.hsl2rgb(a,c,d).hex}),b.rgb=e(function(a,b,c){function d(a){return a+.5|0}
|
||||
return"#"+(16777216|d(c)|d(b)<<8|d(a)<<16).toString(16).slice(1)}),b.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);return b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b})),c.hex},b.getColor.reset=function(){delete this.start},b.parsePathString=function(a){if(!a)
|
||||
return null;var c=ya(a);if(c.arr)
|
||||
return Aa(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];return b.is(a,U)&&b.is(a[0],U)&&(e=Aa(a)),e.length||H(a).replace(ga,function(a,b,c){var f=[],g=b.toLowerCase();if(c.replace(ia,function(a,b){b&&f.push(+b)}),"m"==g&&f.length>2&&(e.push([b][D](f.splice(0,2))),g="l",b="m"==b?"l":"L"),"r"==g)
|
||||
e.push([b][D](f));else
|
||||
for(;f.length>=d[g]&&(e.push([b][D](f.splice(0,d[g]))),d[g]););}),e.toString=b._path2string,c.arr=Aa(e),e},b.parseTransformString=e(function(a){if(!a)
|
||||
return null;var c=[];return b.is(a,U)&&b.is(a[0],U)&&(c=Aa(a)),c.length||H(a).replace(ha,function(a,b,d){{var e=[];L.call(b)}
|
||||
d.replace(ia,function(a,b){b&&e.push(+b)}),c.push([b][D](e))}),c.toString=b._path2string,c});var ya=function(a){var b=ya.ps=ya.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)
|
||||
b[y](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]};b.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=Q(j,3),l=Q(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*M.atan2(q-s,r-t)/R;return(q>s||t>r)&&(y+=180),{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}},b.bezierBBox=function(a,c,d,e,f,g,h,i){b.is(a,"array")||(a=[a,c,d,e,f,g,h,i]);var j=Ha.apply(null,a);return{x:j.min.x,y:j.min.y,x2:j.max.x,y2:j.max.y,width:j.max.x-j.min.x,height:j.max.y-j.min.y}},b.isPointInsideBBox=function(a,b,c){return b>=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},b.isBBoxIntersect=function(a,c){var d=b.isPointInsideBBox;return d(c,a.x,a.y)||d(c,a.x2,a.y)||d(c,a.x,a.y2)||d(c,a.x2,a.y2)||d(a,c.x,c.y)||d(a,c.x2,c.y)||d(a,c.x,c.y2)||d(a,c.x2,c.y2)||(a.x<c.x2&&a.x>c.x||c.x<a.x2&&c.x>a.x)&&(a.y<c.y2&&a.y>c.y||c.y<a.y2&&c.y>a.y)},b.pathIntersection=function(a,b){return m(a,b)},b.pathIntersectionNumber=function(a,b){return m(a,b,1)},b.isPointInsidePath=function(a,c,d){var e=b.pathBBox(a);return b.isPointInsideBBox(e,c,d)&&m(a,[["M",c,d],["H",e.x2+10]],1)%2==1},b._removedFactory=function(b){return function(){a("raphael.log",null,"Raphaël: you are calling to method “"+b+"” of removed object",b)}};var za=b.pathBBox=function(a){var b=ya(a);if(b.bbox)
|
||||
return c(b.bbox);if(!a)
|
||||
return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=Ia(a);for(var d,e=0,f=0,g=[],h=[],i=0,j=a.length;j>i;i++)
|
||||
if(d=a[i],"M"==d[0])
|
||||
e=d[1],f=d[2],g.push(e),h.push(f);else{var k=Ha(e,f,d[1],d[2],d[3],d[4],d[5],d[6]);g=g[D](k.min.x,k.max.x),h=h[D](k.min.y,k.max.y),e=d[5],f=d[6]}
|
||||
var l=O[C](0,g),m=O[C](0,h),n=N[C](0,g),o=N[C](0,h),p=n-l,q=o-m,r={x:l,y:m,x2:n,y2:o,width:p,height:q,cx:l+p/2,cy:m+q/2};return b.bbox=c(r),r},Aa=function(a){var d=c(a);return d.toString=b._path2string,d},Ba=b._pathToRelative=function(a){var c=ya(a);if(c.rel)
|
||||
return Aa(c.rel);b.is(a,U)&&b.is(a&&a[0],U)||(a=b.parsePathString(a));var d=[],e=0,f=0,g=0,h=0,i=0;"M"==a[0][0]&&(e=a[0][1],f=a[0][2],g=e,h=f,i++,d.push(["M",e,f]));for(var j=i,k=a.length;k>j;j++){var l=d[j]=[],m=a[j];if(m[0]!=L.call(m[0]))
|
||||
switch(l[0]=L.call(m[0]),l[0]){case"a":l[1]=m[1],l[2]=m[2],l[3]=m[3],l[4]=m[4],l[5]=m[5],l[6]=+(m[6]-e).toFixed(3),l[7]=+(m[7]-f).toFixed(3);break;case"v":l[1]=+(m[1]-f).toFixed(3);break;case"m":g=m[1],h=m[2];default:for(var n=1,o=m.length;o>n;n++)
|
||||
l[n]=+(m[n]-(n%2?e:f)).toFixed(3)}
|
||||
else{l=d[j]=[],"m"==m[0]&&(g=m[1]+e,h=m[2]+f);for(var p=0,q=m.length;q>p;p++)
|
||||
d[j][p]=m[p]}
|
||||
var r=d[j].length;switch(d[j][0]){case"z":e=g,f=h;break;case"h":e+=+d[j][r-1];break;case"v":f+=+d[j][r-1];break;default:e+=+d[j][r-2],f+=+d[j][r-1]}}
|
||||
return d.toString=b._path2string,c.rel=Aa(d),d},Ca=b._pathToAbsolute=function(a){var c=ya(a);if(c.abs)
|
||||
return Aa(c.abs);if(b.is(a,U)&&b.is(a&&a[0],U)||(a=b.parsePathString(a)),!a||!a.length)
|
||||
return[["M",0,0]];var d=[],e=0,f=0,h=0,i=0,j=0;"M"==a[0][0]&&(e=+a[0][1],f=+a[0][2],h=e,i=f,j++,d[0]=["M",e,f]);for(var k,l,m=3==a.length&&"M"==a[0][0]&&"R"==a[1][0].toUpperCase()&&"Z"==a[2][0].toUpperCase(),n=j,o=a.length;o>n;n++){if(d.push(k=[]),l=a[n],l[0]!=aa.call(l[0]))
|
||||
switch(k[0]=aa.call(l[0]),k[0]){case"A":k[1]=l[1],k[2]=l[2],k[3]=l[3],k[4]=l[4],k[5]=l[5],k[6]=+(l[6]+e),k[7]=+(l[7]+f);break;case"V":k[1]=+l[1]+f;break;case"H":k[1]=+l[1]+e;break;case"R":for(var p=[e,f][D](l.slice(1)),q=2,r=p.length;r>q;q++)
|
||||
p[q]=+p[q]+e,p[++q]=+p[q]+f;d.pop(),d=d[D](g(p,m));break;case"M":h=+l[1]+e,i=+l[2]+f;default:for(q=1,r=l.length;r>q;q++)
|
||||
k[q]=+l[q]+(q%2?e:f)}
|
||||
else if("R"==l[0])
|
||||
p=[e,f][D](l.slice(1)),d.pop(),d=d[D](g(p,m)),k=["R"][D](l.slice(-2));else
|
||||
for(var s=0,t=l.length;t>s;s++)
|
||||
k[s]=l[s];switch(k[0]){case"Z":e=h,f=i;break;case"H":e=k[1];break;case"V":f=k[1];break;case"M":h=k[k.length-2],i=k[k.length-1];default:e=k[k.length-2],f=k[k.length-1]}}
|
||||
return d.toString=b._path2string,c.abs=Aa(d),d},Da=function(a,b,c,d){return[a,b,c,d,c,d]},Ea=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},Fa=function(a,b,c,d,f,g,h,i,j,k){var l,m=120*R/180,n=R/180*(+f||0),o=[],p=e(function(a,b,c){var d=a*M.cos(c)-b*M.sin(c),e=a*M.sin(c)+b*M.cos(c);return{x:d,y:e}});if(k)
|
||||
y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(a,b,-n),a=l.x,b=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(M.cos(R/180*f),M.sin(R/180*f),(a-i)/2),r=(b-j)/2,s=q*q/(c*c)+r*r/(d*d);s>1&&(s=M.sqrt(s),c=s*c,d=s*d);var t=c*c,u=d*d,v=(g==h?-1:1)*M.sqrt(P((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*c*r/d+(a+i)/2,x=v* -d*q/c+(b+j)/2,y=M.asin(((b-x)/d).toFixed(9)),z=M.asin(((j-x)/d).toFixed(9));y=w>a?R-y:y,z=w>i?R-z:z,0>y&&(y=2*R+y),0>z&&(z=2*R+z),h&&y>z&&(y-=2*R),!h&&z>y&&(z-=2*R)}
|
||||
var A=z-y;if(P(A)>m){var B=z,C=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+c*M.cos(z),j=x+d*M.sin(z),o=Fa(i,j,c,d,f,0,h,C,E,[z,B,w,x])}
|
||||
A=z-y;var F=M.cos(y),G=M.sin(y),H=M.cos(z),J=M.sin(z),K=M.tan(A/4),L=4/3*c*K,N=4/3*d*K,O=[a,b],Q=[a+L*G,b-N*F],S=[i+L*J,j-N*H],T=[i,j];if(Q[0]=2*O[0]-Q[0],Q[1]=2*O[1]-Q[1],k)
|
||||
return[Q,S,T][D](o);o=[Q,S,T][D](o).join()[I](",");for(var U=[],V=0,W=o.length;W>V;V++)
|
||||
U[V]=V%2?p(o[V-1],o[V],n).y:p(o[V],o[V+1],n).x;return U},Ga=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:Q(j,3)*a+3*Q(j,2)*i*c+3*j*i*i*e+Q(i,3)*g,y:Q(j,3)*b+3*Q(j,2)*i*d+3*j*i*i*f+Q(i,3)*h}},Ha=e(function(a,b,c,d,e,f,g,h){var i,j=e-2*c+a-(g-2*e+c),k=2*(c-a)-2*(e-c),l=a-c,m=(-k+M.sqrt(k*k-4*j*l))/2/j,n=(-k-M.sqrt(k*k-4*j*l))/2/j,o=[b,h],p=[a,g];return P(m)>"1e12"&&(m=.5),P(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=Ga(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=Ga(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),j=f-2*d+b-(h-2*f+d),k=2*(d-b)-2*(f-d),l=b-d,m=(-k+M.sqrt(k*k-4*j*l))/2/j,n=(-k-M.sqrt(k*k-4*j*l))/2/j,P(m)>"1e12"&&(m=.5),P(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=Ga(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=Ga(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),{min:{x:O[C](0,p),y:O[C](0,o)},max:{x:N[C](0,p),y:N[C](0,o)}}}),Ia=b._path2curve=e(function(a,b){var c=!b&&ya(a);if(!b&&c.curve)
|
||||
return Aa(c.curve);for(var d=Ca(a),e=b&&Ca(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=(function(a,b,c){var d,e,f={T:1,Q:1};if(!a)
|
||||
return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in f)&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][D](Fa[C](0,[b.x,b.y][D](a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e][D](a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"][D](Ea(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][D](Ea(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][D](Da(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][D](Da(b.x,b.y,a[1],b.y));break;case"V":a=["C"][D](Da(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][D](Da(b.x,b.y,b.X,b.Y))}
|
||||
return a}),i=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)
|
||||
k[b]="A",e&&(l[b]="A"),a.splice(b++,0,["C"][D](c.splice(0,6)));a.splice(b,1),p=N(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&"M"==a[g][0]&&"M"!=b[g][0]&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],p=N(d.length,e&&e.length||0))},k=[],l=[],m="",n="",o=0,p=N(d.length,e&&e.length||0);p>o;o++){d[o]&&(m=d[o][0]),"C"!=m&&(k[o]=m,o&&(n=k[o-1])),d[o]=h(d[o],f,n),"A"!=k[o]&&"C"==m&&(k[o]="C"),i(d,o),e&&(e[o]&&(m=e[o][0]),"C"!=m&&(l[o]=m,o&&(n=l[o-1])),e[o]=h(e[o],g,n),"A"!=l[o]&&"C"==m&&(l[o]="C"),i(e,o)),j(d,e,f,g,o),j(e,d,g,f,o);var q=d[o],r=e&&e[o],s=q.length,t=e&&r.length;f.x=q[s-2],f.y=q[s-1],f.bx=$(q[s-4])||f.x,f.by=$(q[s-3])||f.y,g.bx=e&&($(r[t-4])||g.x),g.by=e&&($(r[t-3])||g.y),g.x=e&&r[t-2],g.y=e&&r[t-1]}
|
||||
return e||(c.curve=Aa(d)),e?[d,e]:d},null,Aa),Ja=(b._parseDots=e(function(a){for(var c=[],d=0,e=a.length;e>d;d++){var f={},g=a[d].match(/^([^:]*):?([\d\.]*)/);if(f.color=b.getRGB(g[1]),f.color.error)
|
||||
return null;f.opacity=f.color.opacity,f.color=f.color.hex,g[2]&&(f.offset=g[2]+"%"),c.push(f)}
|
||||
for(d=1,e=c.length-1;e>d;d++)
|
||||
if(!c[d].offset){for(var h=$(c[d-1].offset||0),i=0,j=d+1;e>j;j++)
|
||||
if(c[j].offset){i=c[j].offset;break}
|
||||
i||(i=100,j=e),i=$(i);for(var k=(i-h)/(j-d+1);j>d;d++)
|
||||
h+=k,c[d].offset=h+"%"}
|
||||
return c}),b._tear=function(a,b){a==b.top&&(b.top=a.prev),a==b.bottom&&(b.bottom=a.next),a.next&&(a.next.prev=a.prev),a.prev&&(a.prev.next=a.next)}),Ka=(b._tofront=function(a,b){b.top!==a&&(Ja(a,b),a.next=null,a.prev=b.top,b.top.next=a,b.top=a)},b._toback=function(a,b){b.bottom!==a&&(Ja(a,b),a.next=b.bottom,a.prev=null,b.bottom.prev=a,b.bottom=a)},b._insertafter=function(a,b,c){Ja(a,c),b==c.top&&(c.top=a),b.next&&(b.next.prev=a),a.next=b.next,a.prev=b,b.next=a},b._insertbefore=function(a,b,c){Ja(a,c),b==c.bottom&&(c.bottom=a),b.prev&&(b.prev.next=a),a.prev=b.prev,b.prev=a,a.next=b},b.toMatrix=function(a,b){var c=za(a),d={_:{transform:F},getBBox:function(){return c}};return La(d,b),d.matrix}),La=(b.transformPath=function(a,b){return pa(a,Ka(a,b))},b._extractTransform=function(a,c){if(null==c)
|
||||
return a._.transform;c=H(c).replace(/\.{3}|\u2026/g,a._.transform||F);var d=b.parseTransformString(c),e=0,f=0,g=0,h=1,i=1,j=a._,k=new n;if(j.transform=d||[],d)
|
||||
for(var l=0,m=d.length;m>l;l++){var o,p,q,r,s,t=d[l],u=t.length,v=H(t[0]).toLowerCase(),w=t[0]!=v,x=w?k.invert():0;"t"==v&&3==u?w?(o=x.x(0,0),p=x.y(0,0),q=x.x(t[1],t[2]),r=x.y(t[1],t[2]),k.translate(q-o,r-p)):k.translate(t[1],t[2]):"r"==v?2==u?(s=s||a.getBBox(1),k.rotate(t[1],s.x+s.width/2,s.y+s.height/2),e+=t[1]):4==u&&(w?(q=x.x(t[2],t[3]),r=x.y(t[2],t[3]),k.rotate(t[1],q,r)):k.rotate(t[1],t[2],t[3]),e+=t[1]):"s"==v?2==u||3==u?(s=s||a.getBBox(1),k.scale(t[1],t[u-1],s.x+s.width/2,s.y+s.height/2),h*=t[1],i*=t[u-1]):5==u&&(w?(q=x.x(t[3],t[4]),r=x.y(t[3],t[4]),k.scale(t[1],t[2],q,r)):k.scale(t[1],t[2],t[3],t[4]),h*=t[1],i*=t[2]):"m"==v&&7==u&&k.add(t[1],t[2],t[3],t[4],t[5],t[6]),j.dirtyT=1,a.matrix=k}
|
||||
a.matrix=k,j.sx=h,j.sy=i,j.deg=e,j.dx=f=k.e,j.dy=g=k.f,1==h&&1==i&&!e&&j.bbox?(j.bbox.x+=+f,j.bbox.y+=+g):j.dirtyT=1}),Ma=function(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}},Na=b._equaliseTransform=function(a,c){c=H(c).replace(/\.{3}|\u2026/g,a),a=b.parseTransformString(a)||[],c=b.parseTransformString(c)||[];for(var d,e,f,g,h=N(a.length,c.length),i=[],j=[],k=0;h>k;k++){if(f=a[k]||Ma(c[k]),g=c[k]||Ma(f),f[0]!=g[0]||"r"==f[0].toLowerCase()&&(f[2]!=g[2]||f[3]!=g[3])||"s"==f[0].toLowerCase()&&(f[3]!=g[3]||f[4]!=g[4]))
|
||||
return;for(i[k]=[],j[k]=[],d=0,e=N(f.length,g.length);e>d;d++)
|
||||
d in f&&(i[k][d]=f[d]),d in g&&(j[k][d]=g[d])}
|
||||
return{from:i,to:j}};b._getContainer=function(a,c,d,e){var f;return f=null!=e||b.is(a,"object")?a:z.doc.getElementById(a),null!=f?f.tagName?null==c?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d}:{container:1,x:a,y:c,width:d,height:e}:void 0},b.pathToRelative=Ba,b._engine={},b.path2curve=Ia,b.matrix=function(a,b,c,d,e,f){return new n(a,b,c,d,e,f)},function(a){function c(a){return a[0]*a[0]+a[1]*a[1]}
|
||||
function d(a){var b=M.sqrt(c(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}
|
||||
a.add=function(a,b,c,d,e,f){var g,h,i,j,k=[[],[],[]],l=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],m=[[a,c,e],[b,d,f],[0,0,1]];for(a&&a instanceof n&&(m=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),g=0;3>g;g++)
|
||||
for(h=0;3>h;h++){for(j=0,i=0;3>i;i++)
|
||||
j+=l[g][i]*m[i][h];k[g][h]=j}
|
||||
this.a=k[0][0],this.b=k[1][0],this.c=k[0][1],this.d=k[1][1],this.e=k[0][2],this.f=k[1][2]},a.invert=function(){var a=this,b=a.a*a.d-a.b*a.c;return new n(a.d/b,-a.b/b,-a.c/b,a.a/b,(a.c*a.f-a.d*a.e)/b,(a.b*a.e-a.a*a.f)/b)},a.clone=function(){return new n(this.a,this.b,this.c,this.d,this.e,this.f)},a.translate=function(a,b){this.add(1,0,0,1,a,b)},a.scale=function(a,b,c,d){null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d)},a.rotate=function(a,c,d){a=b.rad(a),c=c||0,d=d||0;var e=+M.cos(a).toFixed(9),f=+M.sin(a).toFixed(9);this.add(e,f,-f,e,c,d),this.add(1,0,0,1,-c,-d)},a.x=function(a,b){return a*this.a+b*this.c+this.e},a.y=function(a,b){return a*this.b+b*this.d+this.f},a.get=function(a){return+this[H.fromCharCode(97+a)].toFixed(4)},a.toString=function(){return b.svg?"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")":[this.get(0),this.get(2),this.get(1),this.get(3),0,0].join()},a.toFilter=function(){return"progid:DXImageTransform.Microsoft.Matrix(M11="+this.get(0)+", M12="+this.get(2)+", M21="+this.get(1)+", M22="+this.get(3)+", Dx="+this.get(4)+", Dy="+this.get(5)+", sizingmethod='auto expand')"},a.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},a.split=function(){var a={};a.dx=this.e,a.dy=this.f;var e=[[this.a,this.c],[this.b,this.d]];a.scalex=M.sqrt(c(e[0])),d(e[0]),a.shear=e[0][0]*e[1][0]+e[0][1]*e[1][1],e[1]=[e[1][0]-e[0][0]*a.shear,e[1][1]-e[0][1]*a.shear],a.scaley=M.sqrt(c(e[1])),d(e[1]),a.shear/=a.scaley;var f=-e[0][1],g=e[1][1];return 0>g?(a.rotate=b.deg(M.acos(g)),0>f&&(a.rotate=360-a.rotate)):a.rotate=b.deg(M.asin(f)),a.isSimple=!(+a.shear.toFixed(9)||a.scalex.toFixed(9)!=a.scaley.toFixed(9)&&a.rotate),a.isSuperSimple=!+a.shear.toFixed(9)&&a.scalex.toFixed(9)==a.scaley.toFixed(9)&&!a.rotate,a.noRotation=!+a.shear.toFixed(9)&&!a.rotate,a},a.toTransformString=function(a){var b=a||this[I]();return b.isSimple?(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[b.dx,b.dy]:F)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:F)+(b.rotate?"r"+[b.rotate,0,0]:F)):"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}
|
||||
(n.prototype);for(var Oa=function(){this.returnValue=!1},Pa=function(){return this.originalEvent.preventDefault()},Qa=function(){this.cancelBubble=!0},Ra=function(){return this.originalEvent.stopPropagation()},Sa=function(a){var b=z.doc.documentElement.scrollTop||z.doc.body.scrollTop,c=z.doc.documentElement.scrollLeft||z.doc.body.scrollLeft;return{x:a.clientX+c,y:a.clientY+b}},Ta=function(){return z.doc.addEventListener?function(a,b,c,d){var e=function(a){var b=Sa(a);return c.call(d,a,b.x,b.y)};if(a.addEventListener(b,e,!1),E&&K[b]){var f=function(b){for(var e=Sa(b),f=b,g=0,h=b.targetTouches&&b.targetTouches.length;h>g;g++)
|
||||
if(b.targetTouches[g].target==a){b=b.targetTouches[g],b.originalEvent=f,b.preventDefault=Pa,b.stopPropagation=Ra;break}
|
||||
return c.call(d,b,e.x,e.y)};a.addEventListener(K[b],f,!1)}
|
||||
return function(){return a.removeEventListener(b,e,!1),E&&K[b]&&a.removeEventListener(K[b],f,!1),!0}}:z.doc.attachEvent?function(a,b,c,d){var e=function(a){a=a||z.win.event;var b=z.doc.documentElement.scrollTop||z.doc.body.scrollTop,e=z.doc.documentElement.scrollLeft||z.doc.body.scrollLeft,f=a.clientX+e,g=a.clientY+b;return a.preventDefault=a.preventDefault||Oa,a.stopPropagation=a.stopPropagation||Qa,c.call(d,a,f,g)};a.attachEvent("on"+b,e);var f=function(){return a.detachEvent("on"+b,e),!0};return f}:void 0}
|
||||
(),Ua=[],Va=function(b){for(var c,d=b.clientX,e=b.clientY,f=z.doc.documentElement.scrollTop||z.doc.body.scrollTop,g=z.doc.documentElement.scrollLeft||z.doc.body.scrollLeft,h=Ua.length;h--;){if(c=Ua[h],E&&b.touches){for(var i,j=b.touches.length;j--;)
|
||||
if(i=b.touches[j],i.identifier==c.el._drag.id){d=i.clientX,e=i.clientY,(b.originalEvent?b.originalEvent:b).preventDefault();break}}else
|
||||
b.preventDefault();var k,l=c.el.node,m=l.nextSibling,n=l.parentNode,o=l.style.display;z.win.opera&&n.removeChild(l),l.style.display="none",k=c.el.paper.getElementByPoint(d,e),l.style.display=o,z.win.opera&&(m?n.insertBefore(l,m):n.appendChild(l)),k&&a("raphael.drag.over."+c.el.id,c.el,k),d+=g,e+=f,a("raphael.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,b)}},Wa=function(c){b.unmousemove(Va).unmouseup(Wa);for(var d,e=Ua.length;e--;)
|
||||
d=Ua[e],d.el._drag={},a("raphael.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c);Ua=[]},Xa=b.el={},Ya=J.length;Ya--;)
|
||||
!function(a){b[a]=Xa[a]=function(c,d){return b.is(c,"function")&&(this.events=this.events||[],this.events.push({name:a,f:c,unbind:Ta(this.shape||this.node||z.doc,a,c,d||this)})),this},b["un"+a]=Xa["un"+a]=function(c){for(var d=this.events||[],e=d.length;e--;)
|
||||
d[e].name!=a||!b.is(c,"undefined")&&d[e].f!=c||(d[e].unbind(),d.splice(e,1),!d.length&&delete this.events);return this}}
|
||||
(J[Ya]);Xa.data=function(c,d){var e=ja[this.id]=ja[this.id]||{};if(0==arguments.length)
|
||||
return e;if(1==arguments.length){if(b.is(c,"object")){for(var f in c)
|
||||
c[y](f)&&this.data(f,c[f]);return this}
|
||||
return a("raphael.data.get."+this.id,this,e[c],c),e[c]}
|
||||
return e[c]=d,a("raphael.data.set."+this.id,this,d,c),this},Xa.removeData=function(a){return null==a?ja[this.id]={}:ja[this.id]&&delete ja[this.id][a],this},Xa.getData=function(){return c(ja[this.id]||{})},Xa.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},Xa.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var Za=[];Xa.drag=function(c,d,e,f,g,h){function i(i){(i.originalEvent||i).preventDefault();var j=i.clientX,k=i.clientY,l=z.doc.documentElement.scrollTop||z.doc.body.scrollTop,m=z.doc.documentElement.scrollLeft||z.doc.body.scrollLeft;if(this._drag.id=i.identifier,E&&i.touches)
|
||||
for(var n,o=i.touches.length;o--;)
|
||||
if(n=i.touches[o],this._drag.id=n.identifier,n.identifier==this._drag.id){j=n.clientX,k=n.clientY;break}
|
||||
this._drag.x=j+m,this._drag.y=k+l,!Ua.length&&b.mousemove(Va).mouseup(Wa),Ua.push({el:this,move_scope:f,start_scope:g,end_scope:h}),d&&a.on("raphael.drag.start."+this.id,d),c&&a.on("raphael.drag.move."+this.id,c),e&&a.on("raphael.drag.end."+this.id,e),a("raphael.drag.start."+this.id,g||f||this,i.clientX+m,i.clientY+l,i)}
|
||||
return this._drag={},Za.push({el:this,start:i}),this.mousedown(i),this},Xa.onDragOver=function(b){b?a.on("raphael.drag.over."+this.id,b):a.unbind("raphael.drag.over."+this.id)},Xa.undrag=function(){for(var c=Za.length;c--;)
|
||||
Za[c].el==this&&(this.unmousedown(Za[c].start),Za.splice(c,1),a.unbind("raphael.drag.*."+this.id));!Za.length&&b.unmousemove(Va).unmouseup(Wa),Ua=[]},u.circle=function(a,c,d){var e=b._engine.circle(this,a||0,c||0,d||0);return this.__set__&&this.__set__.push(e),e},u.rect=function(a,c,d,e,f){var g=b._engine.rect(this,a||0,c||0,d||0,e||0,f||0);return this.__set__&&this.__set__.push(g),g},u.ellipse=function(a,c,d,e){var f=b._engine.ellipse(this,a||0,c||0,d||0,e||0);return this.__set__&&this.__set__.push(f),f},u.path=function(a){a&&!b.is(a,T)&&!b.is(a[0],U)&&(a+=F);var c=b._engine.path(b.format[C](b,arguments),this);return this.__set__&&this.__set__.push(c),c},u.image=function(a,c,d,e,f){var g=b._engine.image(this,a||"about:blank",c||0,d||0,e||0,f||0);return this.__set__&&this.__set__.push(g),g},u.text=function(a,c,d){var e=b._engine.text(this,a||0,c||0,H(d));return this.__set__&&this.__set__.push(e),e},u.set=function(a){!b.is(a,"array")&&(a=Array.prototype.splice.call(arguments,0,arguments.length));var c=new jb(a);return this.__set__&&this.__set__.push(c),c.paper=this,c.type="set",c},u.setStart=function(a){this.__set__=a||this.set()},u.setFinish=function(a){var b=this.__set__;return delete this.__set__,b},u.getSize=function(){var a=this.canvas.parentNode;return{width:a.offsetWidth,height:a.offsetHeight}},u.setSize=function(a,c){return b._engine.setSize.call(this,a,c)},u.setViewBox=function(a,c,d,e,f){return b._engine.setViewBox.call(this,a,c,d,e,f)},u.top=u.bottom=null,u.raphael=b;var $a=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,g=e.clientLeft||d.clientLeft||0,h=b.top+(z.win.pageYOffset||e.scrollTop||d.scrollTop)-f,i=b.left+(z.win.pageXOffset||e.scrollLeft||d.scrollLeft)-g;return{y:h,x:i}};u.getElementByPoint=function(a,b){var c=this,d=c.canvas,e=z.doc.elementFromPoint(a,b);if(z.win.opera&&"svg"==e.tagName){var f=$a(d),g=d.createSVGRect();g.x=a-f.x,g.y=b-f.y,g.width=g.height=1;var h=d.getIntersectionList(g,null);h.length&&(e=h[h.length-1])}
|
||||
if(!e)
|
||||
return null;for(;e.parentNode&&e!=d.parentNode&&!e.raphael;)
|
||||
e=e.parentNode;return e==c.canvas.parentNode&&(e=d),e=e&&e.raphael?c.getById(e.raphaelid):null},u.getElementsByBBox=function(a){var c=this.set();return this.forEach(function(d){b.isBBoxIntersect(d.getBBox(),a)&&c.push(d)}),c},u.getById=function(a){for(var b=this.bottom;b;){if(b.id==a)
|
||||
return b;b=b.next}
|
||||
return null},u.forEach=function(a,b){for(var c=this.bottom;c;){if(a.call(b,c)===!1)
|
||||
return this;c=c.next}
|
||||
return this},u.getElementsByPoint=function(a,b){var c=this.set();return this.forEach(function(d){d.isPointInside(a,b)&&c.push(d)}),c},Xa.isPointInside=function(a,c){var d=this.realPath=oa[this.type](this);return this.attr("transform")&&this.attr("transform").length&&(d=b.transformPath(d,this.attr("transform"))),b.isPointInsidePath(d,a,c)},Xa.getBBox=function(a){if(this.removed)
|
||||
return{};var b=this._;return a?((b.dirty||!b.bboxwt)&&(this.realPath=oa[this.type](this),b.bboxwt=za(this.realPath),b.bboxwt.toString=o,b.dirty=0),b.bboxwt):((b.dirty||b.dirtyT||!b.bbox)&&((b.dirty||!this.realPath)&&(b.bboxwt=0,this.realPath=oa[this.type](this)),b.bbox=za(pa(this.realPath,this.matrix)),b.bbox.toString=o,b.dirty=b.dirtyT=0),b.bbox)},Xa.clone=function(){if(this.removed)
|
||||
return null;var a=this.paper[this.type]().attr(this.attr());return this.__set__&&this.__set__.push(a),a},Xa.glow=function(a){if("text"==this.type)
|
||||
return null;a=a||{};var b={width:(a.width||10)+(+this.attr("stroke-width")||1),fill:a.fill||!1,opacity:null==a.opacity?.5:a.opacity,offsetx:a.offsetx||0,offsety:a.offsety||0,color:a.color||"#000"},c=b.width/2,d=this.paper,e=d.set(),f=this.realPath||oa[this.type](this);f=this.matrix?pa(f,this.matrix):f;for(var g=1;c+1>g;g++)
|
||||
e.push(d.path(f).attr({stroke:b.color,fill:b.fill?b.color:"none","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":+(b.width/c*g).toFixed(3),opacity:+(b.opacity/c).toFixed(3)}));return e.insertBefore(this).translate(b.offsetx,b.offsety)};var _a=function(a,c,d,e,f,g,h,k,l){return null==l?i(a,c,d,e,f,g,h,k):b.findDotsAtSegment(a,c,d,e,f,g,h,k,j(a,c,d,e,f,g,h,k,l))},ab=function(a,c){return function(d,e,f){d=Ia(d);for(var g,h,i,j,k,l="",m={},n=0,o=0,p=d.length;p>o;o++){if(i=d[o],"M"==i[0])
|
||||
g=+i[1],h=+i[2];else{if(j=_a(g,h,i[1],i[2],i[3],i[4],i[5],i[6]),n+j>e){if(c&&!m.start){if(k=_a(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),l+=["C"+k.start.x,k.start.y,k.m.x,k.m.y,k.x,k.y],f)
|
||||
return l;m.start=l,l=["M"+k.x,k.y+"C"+k.n.x,k.n.y,k.end.x,k.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}
|
||||
if(!a&&!c)
|
||||
return k=_a(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),{x:k.x,y:k.y,alpha:k.alpha}}
|
||||
n+=j,g=+i[5],h=+i[6]}
|
||||
l+=i.shift()+i}
|
||||
return m.end=l,k=a?n:c?m:b.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),k.alpha&&(k={x:k.x,y:k.y,alpha:k.alpha}),k}},bb=ab(1),cb=ab(),db=ab(0,1);b.getTotalLength=bb,b.getPointAtLength=cb,b.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)
|
||||
return db(a,b).end;var d=db(a,c,1);return b?db(d,b).end:d},Xa.getTotalLength=function(){var a=this.getPath();if(a)
|
||||
return this.node.getTotalLength?this.node.getTotalLength():bb(a)},Xa.getPointAtLength=function(a){var b=this.getPath();if(b)
|
||||
return cb(b,a)},Xa.getPath=function(){var a,c=b._getPath[this.type];if("text"!=this.type&&"set"!=this.type)
|
||||
return c&&(a=c(this)),a},Xa.getSubpath=function(a,c){var d=this.getPath();if(d)
|
||||
return b.getSubpath(d,a,c)};var eb=b.easing_formulas={linear:function(a){return a},"<":function(a){return Q(a,1.7)},">":function(a){return Q(a,.48)},"<>":function(a){var b=.48-a/1.04,c=M.sqrt(.1734+b*b),d=c-b,e=Q(P(d),1/3)*(0>d?-1:1),f=-c-b,g=Q(P(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){return a==!!a?a:Q(2,-10*a)*M.sin(2*(a-.075)*R/.3)+1},bounce:function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b}};eb.easeIn=eb["ease-in"]=eb["<"],eb.easeOut=eb["ease-out"]=eb[">"],eb.easeInOut=eb["ease-in-out"]=eb["<>"],eb["back-in"]=eb.backIn,eb["back-out"]=eb.backOut;var fb=[],gb=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},hb=function(){for(var c=+new Date,d=0;d<fb.length;d++){var e=fb[d];if(!e.el.removed&&!e.paused){var f,g,h=c-e.start,i=e.ms,j=e.easing,k=e.from,l=e.diff,m=e.to,n=(e.t,e.el),o={},p={};if(e.initstatus?(h=(e.initstatus*e.anim.top-e.prev)/(e.percent-e.prev)*i,e.status=e.initstatus,delete e.initstatus,e.stop&&fb.splice(d--,1)):e.status=(e.prev+(e.percent-e.prev)*(h/i))/e.anim.top,!(0>h))
|
||||
if(i>h){var q=j(h/i);for(var s in k)
|
||||
if(k[y](s)){switch(ca[s]){case S:f=+k[s]+q*i*l[s];break;case"colour":f="rgb("+[ib(Z(k[s].r+q*i*l[s].r)),ib(Z(k[s].g+q*i*l[s].g)),ib(Z(k[s].b+q*i*l[s].b))].join(",")+")";break;case"path":f=[];for(var t=0,u=k[s].length;u>t;t++){f[t]=[k[s][t][0]];for(var v=1,w=k[s][t].length;w>v;v++)
|
||||
f[t][v]=+k[s][t][v]+q*i*l[s][t][v];f[t]=f[t].join(G)}
|
||||
f=f.join(G);break;case"transform":if(l[s].real)
|
||||
for(f=[],t=0,u=k[s].length;u>t;t++)
|
||||
for(f[t]=[k[s][t][0]],v=1,w=k[s][t].length;w>v;v++)
|
||||
f[t][v]=k[s][t][v]+q*i*l[s][t][v];else{var x=function(a){return+k[s][a]+q*i*l[s][a]};f=[["m",x(0),x(1),x(2),x(3),x(4),x(5)]]}
|
||||
break;case"csv":if("clip-rect"==s)
|
||||
for(f=[],t=4;t--;)
|
||||
f[t]=+k[s][t]+q*i*l[s][t];break;default:var z=[][D](k[s]);for(f=[],t=n.paper.customAttributes[s].length;t--;)
|
||||
f[t]=+z[t]+q*i*l[s][t]}
|
||||
o[s]=f}
|
||||
n.attr(o),function(b,c,d){setTimeout(function(){a("raphael.anim.frame."+b,c,d)})}
|
||||
(n.id,n,e.anim)}else{if(function(c,d,e){setTimeout(function(){a("raphael.anim.frame."+d.id,d,e),a("raphael.anim.finish."+d.id,d,e),b.is(c,"function")&&c.call(d)})}
|
||||
(e.callback,n,e.anim),n.attr(m),fb.splice(d--,1),e.repeat>1&&!e.next){for(g in m)
|
||||
m[y](g)&&(p[g]=e.totalOrigin[g]);e.el.attr(p),r(e.anim,e.el,e.anim.percents[0],null,e.totalOrigin,e.repeat-1)}
|
||||
e.next&&!e.stop&&r(e.anim,e.el,e.next,null,e.totalOrigin,e.repeat)}}}
|
||||
fb.length&&gb(hb)},ib=function(a){return a>255?255:0>a?0:a};Xa.animateWith=function(a,c,d,e,f,g){var h=this;if(h.removed)
|
||||
return g&&g.call(h),h;var i=d instanceof q?d:b.animation(d,e,f,g);r(i,h,i.percents[0],null,h.attr());for(var j=0,k=fb.length;k>j;j++)
|
||||
if(fb[j].anim==c&&fb[j].el==a){fb[k-1].start=fb[j].start;break}
|
||||
return h},Xa.onAnimation=function(b){return b?a.on("raphael.anim.frame."+this.id,b):a.unbind("raphael.anim.frame."+this.id),this},q.prototype.delay=function(a){var b=new q(this.anim,this.ms);return b.times=this.times,b.del=+a||0,b},q.prototype.repeat=function(a){var b=new q(this.anim,this.ms);return b.del=this.del,b.times=M.floor(N(a,0))||1,b},b.animation=function(a,c,d,e){if(a instanceof q)
|
||||
return a;(b.is(d,"function")||!d)&&(e=e||d||null,d=null),a=Object(a),c=+c||0;var f,g,h={};for(g in a)
|
||||
a[y](g)&&$(g)!=g&&$(g)+"%"!=g&&(f=!0,h[g]=a[g]);if(f)
|
||||
return d&&(h.easing=d),e&&(h.callback=e),new q({100:h},c);if(e){var i=0;for(var j in a){var k=_(j);a[y](j)&&k>i&&(i=k)}
|
||||
i+="%",!a[i].callback&&(a[i].callback=e)}
|
||||
return new q(a,c)},Xa.animate=function(a,c,d,e){var f=this;if(f.removed)
|
||||
return e&&e.call(f),f;var g=a instanceof q?a:b.animation(a,c,d,e);return r(g,f,g.percents[0],null,f.attr()),f},Xa.setTime=function(a,b){return a&&null!=b&&this.status(a,O(b,a.ms)/a.ms),this},Xa.status=function(a,b){var c,d,e=[],f=0;if(null!=b)
|
||||
return r(a,this,-1,O(b,1)),this;for(c=fb.length;c>f;f++)
|
||||
if(d=fb[f],d.el.id==this.id&&(!a||d.anim==a)){if(a)
|
||||
return d.status;e.push({anim:d.anim,status:d.status})}
|
||||
return a?0:e},Xa.pause=function(b){for(var c=0;c<fb.length;c++)
|
||||
fb[c].el.id!=this.id||b&&fb[c].anim!=b||a("raphael.anim.pause."+this.id,this,fb[c].anim)!==!1&&(fb[c].paused=!0);return this},Xa.resume=function(b){for(var c=0;c<fb.length;c++)
|
||||
if(fb[c].el.id==this.id&&(!b||fb[c].anim==b)){var d=fb[c];a("raphael.anim.resume."+this.id,this,d.anim)!==!1&&(delete d.paused,this.status(d.anim,d.status))}
|
||||
return this},Xa.stop=function(b){for(var c=0;c<fb.length;c++)
|
||||
fb[c].el.id!=this.id||b&&fb[c].anim!=b||a("raphael.anim.stop."+this.id,this,fb[c].anim)!==!1&&fb.splice(c--,1);return this},a.on("raphael.remove",s),a.on("raphael.clear",s),Xa.toString=function(){return"Raphaël’s object"};var jb=function(a){if(this.items=[],this.length=0,this.type="set",a)
|
||||
for(var b=0,c=a.length;c>b;b++)
|
||||
!a[b]||a[b].constructor!=Xa.constructor&&a[b].constructor!=jb||(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},kb=jb.prototype;kb.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)
|
||||
a=arguments[c],!a||a.constructor!=Xa.constructor&&a.constructor!=jb||(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},kb.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},kb.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)
|
||||
if(a.call(b,this.items[c],c)===!1)
|
||||
return this;return this};for(var lb in Xa)
|
||||
Xa[y](lb)&&(kb[lb]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a][C](c,b)})}}
|
||||
(lb));return kb.attr=function(a,c){if(a&&b.is(a,U)&&b.is(a[0],"object"))
|
||||
for(var d=0,e=a.length;e>d;d++)
|
||||
this.items[d].attr(a[d]);else
|
||||
for(var f=0,g=this.items.length;g>f;f++)
|
||||
this.items[f].attr(a,c);return this},kb.clear=function(){for(;this.length;)
|
||||
this.pop()},kb.splice=function(a,b,c){a=0>a?N(this.length+a,0):a,b=N(0,O(this.length-a,b));var d,e=[],f=[],g=[];for(d=2;d<arguments.length;d++)
|
||||
g.push(arguments[d]);for(d=0;b>d;d++)
|
||||
f.push(this[a+d]);for(;d<this.length-a;d++)
|
||||
e.push(this[a+d]);var h=g.length;for(d=0;d<h+e.length;d++)
|
||||
this.items[a+d]=this[a+d]=h>d?g[d]:e[d-h];for(d=this.items.length=this.length-=b-h;this[d];)
|
||||
delete this[d++];return new jb(f)},kb.exclude=function(a){for(var b=0,c=this.length;c>b;b++)
|
||||
if(this[b]==a)
|
||||
return this.splice(b,1),!0},kb.animate=function(a,c,d,e){(b.is(d,"function")||!d)&&(e=d||null);var f,g,h=this.items.length,i=h,j=this;if(!h)
|
||||
return this;e&&(g=function(){!--h&&e.call(j)}),d=b.is(d,T)?d:g;var k=b.animation(a,c,d,g);for(f=this.items[--i].animate(k);i--;)
|
||||
this.items[i]&&!this.items[i].removed&&this.items[i].animateWith(f,k,k),this.items[i]&&!this.items[i].removed||h--;return this},kb.insertAfter=function(a){for(var b=this.items.length;b--;)
|
||||
this.items[b].insertAfter(a);return this},kb.getBBox=function(){for(var a=[],b=[],c=[],d=[],e=this.items.length;e--;)
|
||||
if(!this.items[e].removed){var f=this.items[e].getBBox();a.push(f.x),b.push(f.y),c.push(f.x+f.width),d.push(f.y+f.height)}
|
||||
return a=O[C](0,a),b=O[C](0,b),c=N[C](0,c),d=N[C](0,d),{x:a,y:b,x2:c,y2:d,width:c-a,height:d-b}},kb.clone=function(a){a=this.paper.set();for(var b=0,c=this.items.length;c>b;b++)
|
||||
a.push(this.items[b].clone());return a},kb.toString=function(){return"Raphaël‘s set"},kb.glow=function(a){var b=this.paper.set();return this.forEach(function(c,d){var e=c.glow(a);null!=e&&e.forEach(function(a,c){b.push(a)})}),b},kb.isPointInside=function(a,b){var c=!1;return this.forEach(function(d){return d.isPointInside(a,b)?(c=!0,!1):void 0}),c},b.registerFont=function(a){if(!a.face)
|
||||
return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)
|
||||
a.face[y](d)&&(b.face[d]=a.face[d]);if(this.fonts[c]?this.fonts[c].push(b):this.fonts[c]=[b],!a.svg){b.face["units-per-em"]=_(a.face["units-per-em"],10);for(var e in a.glyphs)
|
||||
if(a.glyphs[y](e)){var f=a.glyphs[e];if(b.glyphs[e]={w:f.w,k:{},d:f.d&&"M"+f.d.replace(/[mlcxtrv]/g,function(a){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}
|
||||
[a]||"M"})+"z"},f.k)
|
||||
for(var g in f.k)
|
||||
f[y](g)&&(b.glyphs[e].k[g]=f.k[g])}}
|
||||
return a},u.getFont=function(a,c,d,e){if(e=e||"normal",d=d||"normal",c=+c||{normal:400,bold:700,lighter:300,bolder:800}
|
||||
[c]||400,b.fonts){var f=b.fonts[a];if(!f){var g=new RegExp("(^|\\s)"+a.replace(/[^\w\d\s+!~.:_-]/g,F)+"(\\s|$)","i");for(var h in b.fonts)
|
||||
if(b.fonts[y](h)&&g.test(h)){f=b.fonts[h];break}}
|
||||
var i;if(f)
|
||||
for(var j=0,k=f.length;k>j&&(i=f[j],i.face["font-weight"]!=c||i.face["font-style"]!=d&&i.face["font-style"]||i.face["font-stretch"]!=e);j++);return i}},u.print=function(a,c,d,e,f,g,h,i){g=g||"middle",h=N(O(h||0,1),-1),i=N(O(i||1,3),1);var j,k=H(d)[I](F),l=0,m=0,n=F;if(b.is(e,"string")&&(e=this.getFont(e)),e){j=(f||16)/e.face["units-per-em"];for(var o=e.face.bbox[I](v),p=+o[0],q=o[3]-o[1],r=0,s=+o[1]+("baseline"==g?q+ +e.face.descent:q/2),t=0,u=k.length;u>t;t++){if("\n"==k[t])
|
||||
l=0,x=0,m=0,r+=q*i;else{var w=m&&e.glyphs[k[t-1]]||{},x=e.glyphs[k[t]];l+=m?(w.w||e.w)+(w.k&&w.k[k[t]]||0)+e.w*h:0,m=1}
|
||||
x&&x.d&&(n+=b.transformPath(x.d,["t",l*j,r*j,"s",j,j,p,s,"t",(a-p)/j,(c-s)/j]))}}
|
||||
return this.path(n).attr({fill:"#000",stroke:"none"})},u.add=function(a){if(b.is(a,"array"))
|
||||
for(var c,d=this.set(),e=0,f=a.length;f>e;e++)
|
||||
c=a[e]||{},w[y](c.type)&&d.push(this[c.type]().attr(c));return d},b.format=function(a,c){var d=b.is(c,U)?[0][D](c):arguments;return a&&b.is(a,T)&&d.length-1&&(a=a.replace(x,function(a,b){return null==d[++b]?F:d[b]})),a||F},b.fullfill=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return String(b).replace(a,function(a,b){return c(a,b,d)})}}
|
||||
(),b.ninja=function(){return A.was?z.win.Raphael=A.is:delete Raphael,b},b.st=kb,a.on("raphael.DOMload",function(){t=!0}),function(a,c,d){function e(){/in/.test(a.readyState)?setTimeout(e,9):b.eve("raphael.DOMload")}
|
||||
null==a.readyState&&a.addEventListener&&(a.addEventListener(c,d=function(){a.removeEventListener(c,d,!1),a.readyState="complete"},!1),a.readyState="loading"),e()}
|
||||
(document,"DOMContentLoaded"),b}),function(a,b){"function"==typeof define&&define.amd?define("raphael.svg",["raphael.core"],function(a){return b(a)}):b("object"==typeof exports?require("./raphael.core"):a.Raphael)}
|
||||
(this,function(a){if(!a||a.svg){var b="hasOwnProperty",c=String,d=parseFloat,e=parseInt,f=Math,g=f.max,h=f.abs,i=f.pow,j=/[, ]+/,k=a.eve,l="",m=" ",n="http://www.w3.org/1999/xlink",o={block:"M5,0 0,2.5 5,5z",classic:"M5,0 0,2.5 5,5 3.5,3 3.5,2z",diamond:"M2.5,0 5,2.5 2.5,5 0,2.5z",open:"M6,1 1,3.5 6,6",oval:"M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"},p={};a.toString=function(){return"Your browser supports SVG.\nYou are running Raphaël "+this.version};var q=function(d,e){if(e){"string"==typeof d&&(d=q(d));for(var f in e)
|
||||
e[b](f)&&("xlink:"==f.substring(0,6)?d.setAttributeNS(n,f.substring(6),c(e[f])):d.setAttribute(f,c(e[f])))}else
|
||||
d=a._g.doc.createElementNS("http://www.w3.org/2000/svg",d),d.style&&(d.style.webkitTapHighlightColor="rgba(0,0,0,0)");return d},r=function(b,e){var j="linear",k=b.id+e,m=.5,n=.5,o=b.node,p=b.paper,r=o.style,s=a._g.doc.getElementById(k);if(!s){if(e=c(e).replace(a._radial_gradient,function(a,b,c){if(j="radial",b&&c){m=d(b),n=d(c);var e=2*(n>.5)-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&.5!=n&&(n=n.toFixed(5)-1e-5*e)}
|
||||
return l}),e=e.split(/\s*\-\s*/),"linear"==j){var t=e.shift();if(t=-d(t),isNaN(t))
|
||||
return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}
|
||||
var w=a._parseDots(e);if(!w)
|
||||
return null;if(k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient),!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,"radial"==j?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;y>x;x++)
|
||||
s.appendChild(q("stop",{offset:w[x].offset?w[x].offset:x?"100%":"0%","stop-color":w[x].color||"#fff","stop-opacity":isFinite(w[x].opacity)?w[x].opacity:1}))}}
|
||||
return q(o,{fill:"url('"+document.location.origin+document.location.pathname+"#"+k+"')",opacity:1,"fill-opacity":1}),r.fill=l,r.opacity=1,r.fillOpacity=1,1},s=function(a){var b=a.getBBox(1);q(a.pattern,{patternTransform:a.matrix.invert()+" translate("+b.x+","+b.y+")"})},t=function(d,e,f){if("path"==d.type){for(var g,h,i,j,k,m=c(e).toLowerCase().split("-"),n=d.paper,r=f?"end":"start",s=d.node,t=d.attrs,u=t["stroke-width"],v=m.length,w="classic",x=3,y=3,z=5;v--;)
|
||||
switch(m[v]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":w=m[v];break;case"wide":y=5;break;case"narrow":y=2;break;case"long":x=5;break;case"short":x=2}
|
||||
if("open"==w?(x+=2,y+=2,z+=2,i=1,j=f?4:1,k={fill:"none",stroke:t.stroke}):(j=i=x/2,k={fill:t.stroke,stroke:"none"}),d._.arrows?f?(d._.arrows.endPath&&p[d._.arrows.endPath]--,d._.arrows.endMarker&&p[d._.arrows.endMarker]--):(d._.arrows.startPath&&p[d._.arrows.startPath]--,d._.arrows.startMarker&&p[d._.arrows.startMarker]--):d._.arrows={},"none"!=w){var A="raphael-marker-"+w,B="raphael-marker-"+r+w+x+y+"-obj"+d.id;a._g.doc.getElementById(A)?p[A]++:(n.defs.appendChild(q(q("path"),{"stroke-linecap":"round",d:o[w],id:A})),p[A]=1);var C,D=a._g.doc.getElementById(B);D?(p[B]++,C=D.getElementsByTagName("use")[0]):(D=q(q("marker"),{id:B,markerHeight:y,markerWidth:x,orient:"auto",refX:j,refY:y/2}),C=q(q("use"),{"xlink:href":"#"+A,transform:(f?"rotate(180 "+x/2+" "+y/2+") ":l)+"scale("+x/z+","+y/z+")","stroke-width":(1/((x/z+y/z)/2)).toFixed(4)}),D.appendChild(C),n.defs.appendChild(D),p[B]=1),q(C,k);var E=i*("diamond"!=w&&"oval"!=w);f?(g=d._.arrows.startdx*u||0,h=a.getTotalLength(t.path)-E*u):(g=E*u,h=a.getTotalLength(t.path)-(d._.arrows.enddx*u||0)),k={},k["marker-"+r]="url(#"+B+")",(h||g)&&(k.d=a.getSubpath(t.path,g,h)),q(s,k),d._.arrows[r+"Path"]=A,d._.arrows[r+"Marker"]=B,d._.arrows[r+"dx"]=E,d._.arrows[r+"Type"]=w,d._.arrows[r+"String"]=e}else
|
||||
f?(g=d._.arrows.startdx*u||0,h=a.getTotalLength(t.path)-g):(g=0,h=a.getTotalLength(t.path)-(d._.arrows.enddx*u||0)),d._.arrows[r+"Path"]&&q(s,{d:a.getSubpath(t.path,g,h)}),delete d._.arrows[r+"Path"],delete d._.arrows[r+"Marker"],delete d._.arrows[r+"dx"],delete d._.arrows[r+"Type"],delete d._.arrows[r+"String"];for(k in p)
|
||||
if(p[b](k)&&!p[k]){var F=a._g.doc.getElementById(k);F&&F.parentNode.removeChild(F)}}},u={"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},v=function(a,b,d){if(b=u[c(b).toLowerCase()]){for(var e=a.attrs["stroke-width"]||"1",f={round:e,square:e,butt:0}
|
||||
[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],h=b.length;h--;)
|
||||
g[h]=b[h]*e+(h%2?1:-1)*f;q(a.node,{"stroke-dasharray":g.join(",")})}else
|
||||
q(a.node,{"stroke-dasharray":"none"})},w=function(d,f){var i=d.node,k=d.attrs,m=i.style.visibility;i.style.visibility="hidden";for(var o in f)
|
||||
if(f[b](o)){if(!a._availableAttrs[b](o))
|
||||
continue;var p=f[o];switch(k[o]=p,o){case"blur":d.blur(p);break;case"title":var u=i.getElementsByTagName("title");if(u.length&&(u=u[0]))
|
||||
u.firstChild.nodeValue=p;else{u=q("title");var w=a._g.doc.createTextNode(p);u.appendChild(w),i.appendChild(u)}
|
||||
break;case"href":case"target":var x=i.parentNode;if("a"!=x.tagName.toLowerCase()){var z=q("a");x.insertBefore(z,i),z.appendChild(i),x=z}"target"==o?x.setAttributeNS(n,"show","blank"==p?"new":p):x.setAttributeNS(n,o,p);break;case"cursor":i.style.cursor=p;break;case"transform":d.transform(p);break;case"arrow-start":t(d,p);break;case"arrow-end":t(d,p,1);break;case"clip-rect":var A=c(p).split(j);if(4==A.length){d.clip&&d.clip.parentNode.parentNode.removeChild(d.clip.parentNode);var B=q("clipPath"),C=q("rect");B.id=a.createUUID(),q(C,{x:A[0],y:A[1],width:A[2],height:A[3]}),B.appendChild(C),d.paper.defs.appendChild(B),q(i,{"clip-path":"url(#"+B.id+")"}),d.clip=C}
|
||||
if(!p){var D=i.getAttribute("clip-path");if(D){var E=a._g.doc.getElementById(D.replace(/(^url\(#|\)$)/g,l));E&&E.parentNode.removeChild(E),q(i,{"clip-path":l}),delete d.clip}}
|
||||
break;case"path":"path"==d.type&&(q(i,{d:p?k.path=a._pathToAbsolute(p):"M0,0"}),d._.dirty=1,d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1)));break;case"width":if(i.setAttribute(o,p),d._.dirty=1,!k.fx)
|
||||
break;o="x",p=k.x;case"x":k.fx&&(p=-k.x-(k.width||0));case"rx":if("rx"==o&&"rect"==d.type)
|
||||
break;case"cx":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"height":if(i.setAttribute(o,p),d._.dirty=1,!k.fy)
|
||||
break;o="y",p=k.y;case"y":k.fy&&(p=-k.y-(k.height||0));case"ry":if("ry"==o&&"rect"==d.type)
|
||||
break;case"cy":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"r":"rect"==d.type?q(i,{rx:p,ry:p}):i.setAttribute(o,p),d._.dirty=1;break;case"src":"image"==d.type&&i.setAttributeNS(n,"href",p);break;case"stroke-width":(1!=d._.sx||1!=d._.sy)&&(p/=g(h(d._.sx),h(d._.sy))||1),i.setAttribute(o,p),k["stroke-dasharray"]&&v(d,k["stroke-dasharray"],f),d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"stroke-dasharray":v(d,p,f);break;case"fill":var F=c(p).match(a._ISURL);if(F){B=q("pattern");var G=q("image");B.id=a.createUUID(),q(B,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1}),q(G,{x:0,y:0,"xlink:href":F[1]}),B.appendChild(G),function(b){a._preload(F[1],function(){var a=this.offsetWidth,c=this.offsetHeight;q(b,{width:a,height:c}),q(G,{width:a,height:c})})}
|
||||
(B),d.paper.defs.appendChild(B),q(i,{fill:"url(#"+B.id+")"}),d.pattern=B,d.pattern&&s(d);break}
|
||||
var H=a.getRGB(p);if(H.error){if(("circle"==d.type||"ellipse"==d.type||"r"!=c(p).charAt())&&r(d,p)){if("opacity"in k||"fill-opacity"in k){var I=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l));if(I){var J=I.getElementsByTagName("stop");q(J[J.length-1],{"stop-opacity":("opacity"in k?k.opacity:1)*("fill-opacity"in k?k["fill-opacity"]:1)})}}
|
||||
k.gradient=p,k.fill="none";break}}else
|
||||
delete f.gradient,delete k.gradient,!a.is(k.opacity,"undefined")&&a.is(f.opacity,"undefined")&&q(i,{opacity:k.opacity}),!a.is(k["fill-opacity"],"undefined")&&a.is(f["fill-opacity"],"undefined")&&q(i,{"fill-opacity":k["fill-opacity"]});H[b]("opacity")&&q(i,{"fill-opacity":H.opacity>1?H.opacity/100:H.opacity});case"stroke":H=a.getRGB(p),i.setAttribute(o,H.hex),"stroke"==o&&H[b]("opacity")&&q(i,{"stroke-opacity":H.opacity>1?H.opacity/100:H.opacity}),"stroke"==o&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":("circle"==d.type||"ellipse"==d.type||"r"!=c(p).charAt())&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){I=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),I&&(J=I.getElementsByTagName("stop"),q(J[J.length-1],{"stop-opacity":p}));break}
|
||||
default:"font-size"==o&&(p=e(p,10)+"px");var K=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[K]=p,d._.dirty=1,i.setAttribute(o,p)}}
|
||||
y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if("text"==d.type&&(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){for(g.text=f.text;h.firstChild;)
|
||||
h.removeChild(h.firstChild);for(var j,k=c(f.text).split("\n"),m=[],n=0,o=k.length;o>n;n++)
|
||||
j=q("tspan"),n&&q(j,{dy:i*x,x:g.x}),j.appendChild(a._g.doc.createTextNode(k[n])),h.appendChild(j),m[n]=j}else
|
||||
for(m=h.getElementsByTagName("tspan"),n=0,o=m.length;o>n;n++)
|
||||
n?q(m[n],{dy:i*x,x:g.x}):q(m[0],{dy:0});q(h,{x:g.x,y:g.y}),d._.dirty=1;var p=d._getBBox(),r=g.y-(p.y+p.height/2);r&&a.is(r,"finite")&&q(m[0],{dy:r})}},z=function(a){return a.parentNode&&"a"===a.parentNode.tagName.toLowerCase()?a.parentNode:a},A=function(b,c){this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.matrix=a.matrix(),this.realPath=null,this.paper=c,this.attrs=this.attrs||{},this._={transform:[],sx:1,sy:1,deg:0,dx:0,dy:0,dirty:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},B=a.el;A.prototype=B,B.constructor=A,a._engine.path=function(a,b){var c=q("path");b.canvas&&b.canvas.appendChild(c);var d=new A(c,b);return d.type="path",w(d,{fill:"none",stroke:"#000",path:a}),d},B.rotate=function(a,b,e){if(this.removed)
|
||||
return this;if(a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),null==e&&(b=e),null==b||null==e){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}
|
||||
return this.transform(this._.transform.concat([["r",a,b,e]])),this},B.scale=function(a,b,e,f){if(this.removed)
|
||||
return this;if(a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3])),a=d(a[0]),null==b&&(b=a),null==f&&(e=f),null==e||null==f)
|
||||
var g=this.getBBox(1);return e=null==e?g.x+g.width/2:e,f=null==f?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]])),this},B.translate=function(a,b){return this.removed?this:(a=c(a).split(j),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this.transform(this._.transform.concat([["t",a,b]])),this)},B.transform=function(c){var d=this._;if(null==c)
|
||||
return d.transform;if(a._extractTransform(this,c),this.clip&&q(this.clip,{transform:this.matrix.invert()}),this.pattern&&s(this),this.node&&q(this.node,{transform:this.matrix}),1!=d.sx||1!=d.sy){var e=this.attrs[b]("stroke-width")?this.attrs["stroke-width"]:1;this.attr({"stroke-width":e})}
|
||||
return this},B.hide=function(){return this.removed||(this.node.style.display="none"),this},B.show=function(){return this.removed||(this.node.style.display=""),this},B.remove=function(){var b=z(this.node);if(!this.removed&&b.parentNode){var c=this.paper;c.__set__&&c.__set__.exclude(this),k.unbind("raphael.*.*."+this.id),this.gradient&&c.defs.removeChild(this.gradient),a._tear(this,c),b.parentNode.removeChild(b),this.removeData();for(var d in this)
|
||||
this[d]="function"==typeof this[d]?a._removedFactory(d):null;this.removed=!0}},B._getBBox=function(){if("none"==this.node.style.display){this.show();var a=!0}
|
||||
var b,c=!1;this.paper.canvas.parentElement?b=this.paper.canvas.parentElement.style:this.paper.canvas.parentNode&&(b=this.paper.canvas.parentNode.style),b&&"none"==b.display&&(c=!0,b.display="");var d={};try{d=this.node.getBBox()}catch(e){d={x:this.node.clientLeft,y:this.node.clientTop,width:this.node.clientWidth,height:this.node.clientHeight}}
|
||||
finally{d=d||{},c&&(b.display="none")}
|
||||
return a&&this.hide(),d},B.attr=function(c,d){if(this.removed)
|
||||
return this;if(null==c){var e={};for(var f in this.attrs)
|
||||
this.attrs[b](f)&&(e[f]=this.attrs[f]);return e.gradient&&"none"==e.fill&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform,e}
|
||||
if(null==d&&a.is(c,"string")){if("fill"==c&&"none"==this.attrs.fill&&this.attrs.gradient)
|
||||
return this.attrs.gradient;if("transform"==c)
|
||||
return this._.transform;for(var g=c.split(j),h={},i=0,l=g.length;l>i;i++)
|
||||
c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return l-1?h:h[g[0]]}
|
||||
if(null==d&&a.is(c,"array")){for(h={},i=0,l=c.length;l>i;i++)
|
||||
h[c[i]]=this.attr(c[i]);return h}
|
||||
if(null!=d){var m={};m[c]=d}else
|
||||
null!=c&&a.is(c,"object")&&(m=c);for(var n in m)
|
||||
k("raphael.attr."+n+"."+this.id,this,m[n]);for(n in this.paper.customAttributes)
|
||||
if(this.paper.customAttributes[b](n)&&m[b](n)&&a.is(this.paper.customAttributes[n],"function")){var o=this.paper.customAttributes[n].apply(this,[].concat(m[n]));this.attrs[n]=m[n];for(var p in o)
|
||||
o[b](p)&&(m[p]=o[p])}
|
||||
return w(this,m),this},B.toFront=function(){if(this.removed)
|
||||
return this;var b=z(this.node);b.parentNode.appendChild(b);var c=this.paper;return c.top!=this&&a._tofront(this,c),this},B.toBack=function(){if(this.removed)
|
||||
return this;var b=z(this.node),c=b.parentNode;c.insertBefore(b,c.firstChild),a._toback(this,this.paper);this.paper;return this},B.insertAfter=function(b){if(this.removed||!b)
|
||||
return this;var c=z(this.node),d=z(b.node||b[b.length-1].node);return d.nextSibling?d.parentNode.insertBefore(c,d.nextSibling):d.parentNode.appendChild(c),a._insertafter(this,b,this.paper),this},B.insertBefore=function(b){if(this.removed||!b)
|
||||
return this;var c=z(this.node),d=z(b.node||b[0].node);return d.parentNode.insertBefore(c,d),a._insertbefore(this,b,this.paper),this},B.blur=function(b){var c=this;if(0!==+b){var d=q("filter"),e=q("feGaussianBlur");c.attrs.blur=b,d.id=a.createUUID(),q(e,{stdDeviation:+b||1.5}),d.appendChild(e),c.paper.defs.appendChild(d),c._blur=d,q(c.node,{filter:"url(#"+d.id+")"})}else
|
||||
c._blur&&(c._blur.parentNode.removeChild(c._blur),delete c._blur,delete c.attrs.blur),c.node.removeAttribute("filter");return c},a._engine.circle=function(a,b,c,d){var e=q("circle");a.canvas&&a.canvas.appendChild(e);var f=new A(e,a);return f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"},f.type="circle",q(e,f.attrs),f},a._engine.rect=function(a,b,c,d,e,f){var g=q("rect");a.canvas&&a.canvas.appendChild(g);var h=new A(g,a);return h.attrs={x:b,y:c,width:d,height:e,rx:f||0,ry:f||0,fill:"none",stroke:"#000"},h.type="rect",q(g,h.attrs),h},a._engine.ellipse=function(a,b,c,d,e){var f=q("ellipse");a.canvas&&a.canvas.appendChild(f);var g=new A(f,a);return g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"},g.type="ellipse",q(f,g.attrs),g},a._engine.image=function(a,b,c,d,e,f){var g=q("image");q(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"}),g.setAttributeNS(n,"href",b),a.canvas&&a.canvas.appendChild(g);var h=new A(g,a);return h.attrs={x:c,y:d,width:e,height:f,src:b},h.type="image",h},a._engine.text=function(b,c,d,e){var f=q("text");b.canvas&&b.canvas.appendChild(f);var g=new A(f,b);return g.attrs={x:c,y:d,"text-anchor":"middle",text:e,"font-family":a._availableAttrs["font-family"],"font-size":a._availableAttrs["font-size"],stroke:"none",fill:"#000"},g.type="text",w(g,g.attrs),g},a._engine.setSize=function(a,b){return this.width=a||this.width,this.height=b||this.height,this.canvas.setAttribute("width",this.width),this.canvas.setAttribute("height",this.height),this._viewBox&&this.setViewBox.apply(this,this._viewBox),this},a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,g=b.height;if(!c)
|
||||
throw new Error("SVG container not found.");var h,i=q("svg"),j="overflow:hidden;";return d=d||0,e=e||0,f=f||512,g=g||342,q(i,{height:g,version:1.1,width:f,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}),1==c?(i.style.cssText=j+"position:absolute;left:"+d+"px;top:"+e+"px",a._g.doc.body.appendChild(i),h=1):(i.style.cssText=j+"position:relative",c.firstChild?c.insertBefore(i,c.firstChild):c.appendChild(i)),c=new a._Paper,c.width=f,c.height=g,c.canvas=i,c.clear(),c._left=c._top=0,h&&(c.renderfix=function(){}),c.renderfix(),c},a._engine.setViewBox=function(a,b,c,d,e){k("raphael.setViewBox",this,this._viewBox,[a,b,c,d,e]);var f,h,i=this.getSize(),j=g(c/i.width,d/i.height),l=this.top,n=e?"xMidYMid meet":"xMinYMin";for(null==a?(this._vbSize&&(j=1),delete this._vbSize,f="0 0 "+this.width+m+this.height):(this._vbSize=j,f=a+m+b+m+c+m+d),q(this.canvas,{viewBox:f,preserveAspectRatio:n});j&&l;)
|
||||
h="stroke-width"in l.attrs?l.attrs["stroke-width"]:1,l.attr({"stroke-width":h}),l._.dirty=1,l._.dirtyT=1,l=l.prev;return this._viewBox=[a,b,c,d,!!e],this},a.prototype.renderfix=function(){var a,b=this.canvas,c=b.style;try{a=b.getScreenCTM()||b.createSVGMatrix()}catch(d){a=b.createSVGMatrix()}
|
||||
var e=-a.e%1,f=-a.f%1;(e||f)&&(e&&(this._left=(this._left+e)%1,c.left=this._left+"px"),f&&(this._top=(this._top+f)%1,c.top=this._top+"px"))},a.prototype.clear=function(){a.eve("raphael.clear",this);for(var b=this.canvas;b.firstChild;)
|
||||
b.removeChild(b.firstChild);this.bottom=this.top=null,(this.desc=q("desc")).appendChild(a._g.doc.createTextNode("Created with Raphaël "+a.version)),b.appendChild(this.desc),b.appendChild(this.defs=q("defs"))},a.prototype.remove=function(){k("raphael.remove",this),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var b in this)
|
||||
this[b]="function"==typeof this[b]?a._removedFactory(b):null};var C=a.st;for(var D in B)
|
||||
B[b](D)&&!C[b](D)&&(C[D]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}
|
||||
(D))}}),function(a,b){"function"==typeof define&&define.amd?define("raphael.vml",["raphael.core"],function(a){return b(a)}):b("object"==typeof exports?require("./raphael.core"):a.Raphael)}
|
||||
(this,function(a){if(!a||a.vml){var b="hasOwnProperty",c=String,d=parseFloat,e=Math,f=e.round,g=e.max,h=e.min,i=e.abs,j="fill",k=/[, ]+/,l=a.eve,m=" progid:DXImageTransform.Microsoft",n=" ",o="",p={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},q=/([clmz]),?([^clmz]*)/gi,r=/ progid:\S+Blur\([^\)]+\)/g,s=/-?[^,\s-]+/g,t="position:absolute;left:0;top:0;width:1px;height:1px;behavior:url(#default#VML)",u=21600,v={path:1,rect:1,image:1},w={circle:1,ellipse:1},x=function(b){var d=/[ahqstv]/gi,e=a._pathToAbsolute;if(c(b).match(d)&&(e=a._path2curve),d=/[clmz]/g,e==a._pathToAbsolute&&!c(b).match(d)){var g=c(b).replace(q,function(a,b,c){var d=[],e="m"==b.toLowerCase(),g=p[b];return c.replace(s,function(a){e&&2==d.length&&(g+=d+p["m"==b?"l":"L"],d=[]),d.push(f(a*u))}),g+d});return g}
|
||||
var h,i,j=e(b);g=[];for(var k=0,l=j.length;l>k;k++){h=j[k],i=j[k][0].toLowerCase(),"z"==i&&(i="x");for(var m=1,r=h.length;r>m;m++)
|
||||
i+=f(h[m]*u)+(m!=r-1?",":o);g.push(i)}
|
||||
return g.join(n)},y=function(b,c,d){var e=a.matrix();return e.rotate(-b,.5,.5),{dx:e.x(c,d),dy:e.y(c,d)}},z=function(a,b,c,d,e,f){var g=a._,h=a.matrix,k=g.fillpos,l=a.node,m=l.style,o=1,p="",q=u/b,r=u/c;if(m.visibility="hidden",b&&c){if(l.coordsize=i(q)+n+i(r),m.rotation=f*(0>b*c?-1:1),f){var s=y(f,d,e);d=s.dx,e=s.dy}
|
||||
if(0>b&&(p+="x"),0>c&&(p+=" y")&&(o=-1),m.flip=p,l.coordorigin=d* -q+n+e* -r,k||g.fillsize){var t=l.getElementsByTagName(j);t=t&&t[0],l.removeChild(t),k&&(s=y(f,h.x(k[0],k[1]),h.y(k[0],k[1])),t.position=s.dx*o+n+s.dy*o),g.fillsize&&(t.size=g.fillsize[0]*i(b)+n+g.fillsize[1]*i(c)),l.appendChild(t)}
|
||||
m.visibility="visible"}};a.toString=function(){return"Your browser doesn’t support SVG. Falling down to VML.\nYou are running Raphaël "+this.version};var A=function(a,b,d){for(var e=c(b).toLowerCase().split("-"),f=d?"end":"start",g=e.length,h="classic",i="medium",j="medium";g--;)
|
||||
switch(e[g]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":h=e[g];break;case"wide":case"narrow":j=e[g];break;case"long":case"short":i=e[g]}
|
||||
var k=a.node.getElementsByTagName("stroke")[0];k[f+"arrow"]=h,k[f+"arrowlength"]=i,k[f+"arrowwidth"]=j},B=function(e,i){e.attrs=e.attrs||{};var l=e.node,m=e.attrs,p=l.style,q=v[e.type]&&(i.x!=m.x||i.y!=m.y||i.width!=m.width||i.height!=m.height||i.cx!=m.cx||i.cy!=m.cy||i.rx!=m.rx||i.ry!=m.ry||i.r!=m.r),r=w[e.type]&&(m.cx!=i.cx||m.cy!=i.cy||m.r!=i.r||m.rx!=i.rx||m.ry!=i.ry),s=e;for(var t in i)
|
||||
i[b](t)&&(m[t]=i[t]);if(q&&(m.path=a._getPath[e.type](e),e._.dirty=1),i.href&&(l.href=i.href),i.title&&(l.title=i.title),i.target&&(l.target=i.target),i.cursor&&(p.cursor=i.cursor),"blur"in i&&e.blur(i.blur),(i.path&&"path"==e.type||q)&&(l.path=x(~c(m.path).toLowerCase().indexOf("r")?a._pathToAbsolute(m.path):m.path),e._.dirty=1,"image"==e.type&&(e._.fillpos=[m.x,m.y],e._.fillsize=[m.width,m.height],z(e,1,1,0,0,0))),"transform"in i&&e.transform(i.transform),r){var y=+m.cx,B=+m.cy,D=+m.rx||+m.r||0,E=+m.ry||+m.r||0;l.path=a.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x",f((y-D)*u),f((B-E)*u),f((y+D)*u),f((B+E)*u),f(y*u)),e._.dirty=1}
|
||||
if("clip-rect"in i){var G=c(i["clip-rect"]).split(k);if(4==G.length){G[2]=+G[2]+ +G[0],G[3]=+G[3]+ +G[1];var H=l.clipRect||a._g.doc.createElement("div"),I=H.style;I.clip=a.format("rect({1}px {2}px {3}px {0}px)",G),l.clipRect||(I.position="absolute",I.top=0,I.left=0,I.width=e.paper.width+"px",I.height=e.paper.height+"px",l.parentNode.insertBefore(H,l),H.appendChild(l),l.clipRect=H)}
|
||||
i["clip-rect"]||l.clipRect&&(l.clipRect.style.clip="auto")}
|
||||
if(e.textpath){var J=e.textpath.style;i.font&&(J.font=i.font),i["font-family"]&&(J.fontFamily='"'+i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g,o)+'"'),i["font-size"]&&(J.fontSize=i["font-size"]),i["font-weight"]&&(J.fontWeight=i["font-weight"]),i["font-style"]&&(J.fontStyle=i["font-style"])}
|
||||
if("arrow-start"in i&&A(s,i["arrow-start"]),"arrow-end"in i&&A(s,i["arrow-end"],1),null!=i.opacity||null!=i["stroke-width"]||null!=i.fill||null!=i.src||null!=i.stroke||null!=i["stroke-width"]||null!=i["stroke-opacity"]||null!=i["fill-opacity"]||null!=i["stroke-dasharray"]||null!=i["stroke-miterlimit"]||null!=i["stroke-linejoin"]||null!=i["stroke-linecap"]){var K=l.getElementsByTagName(j),L=!1;if(K=K&&K[0],!K&&(L=K=F(j)),"image"==e.type&&i.src&&(K.src=i.src),i.fill&&(K.on=!0),(null==K.on||"none"==i.fill||null===i.fill)&&(K.on=!1),K.on&&i.fill){var M=c(i.fill).match(a._ISURL);if(M){K.parentNode==l&&l.removeChild(K),K.rotate=!0,K.src=M[1],K.type="tile";var N=e.getBBox(1);K.position=N.x+n+N.y,e._.fillpos=[N.x,N.y],a._preload(M[1],function(){e._.fillsize=[this.offsetWidth,this.offsetHeight]})}else
|
||||
K.color=a.getRGB(i.fill).hex,K.src=o,K.type="solid",a.getRGB(i.fill).error&&(s.type in{circle:1,ellipse:1}||"r"!=c(i.fill).charAt())&&C(s,i.fill,K)&&(m.fill="none",m.gradient=i.fill,K.rotate=!1)}
|
||||
if("fill-opacity"in i||"opacity"in i){var O=((+m["fill-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+a.getRGB(i.fill).o+1||2)-1);O=h(g(O,0),1),K.opacity=O,K.src&&(K.color="none")}
|
||||
l.appendChild(K);var P=l.getElementsByTagName("stroke")&&l.getElementsByTagName("stroke")[0],Q=!1;!P&&(Q=P=F("stroke")),(i.stroke&&"none"!=i.stroke||i["stroke-width"]||null!=i["stroke-opacity"]||i["stroke-dasharray"]||i["stroke-miterlimit"]||i["stroke-linejoin"]||i["stroke-linecap"])&&(P.on=!0),("none"==i.stroke||null===i.stroke||null==P.on||0==i.stroke||0==i["stroke-width"])&&(P.on=!1);var R=a.getRGB(i.stroke);P.on&&i.stroke&&(P.color=R.hex),O=((+m["stroke-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+R.o+1||2)-1);var S=.75*(d(i["stroke-width"])||1);if(O=h(g(O,0),1),null==i["stroke-width"]&&(S=m["stroke-width"]),i["stroke-width"]&&(P.weight=S),S&&1>S&&(O*=S)&&(P.weight=1),P.opacity=O,i["stroke-linejoin"]&&(P.joinstyle=i["stroke-linejoin"]||"miter"),P.miterlimit=i["stroke-miterlimit"]||8,i["stroke-linecap"]&&(P.endcap="butt"==i["stroke-linecap"]?"flat":"square"==i["stroke-linecap"]?"square":"round"),"stroke-dasharray"in i){var T={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};P.dashstyle=T[b](i["stroke-dasharray"])?T[i["stroke-dasharray"]]:o}
|
||||
Q&&l.appendChild(P)}
|
||||
if("text"==s.type){s.paper.canvas.style.display=o;var U=s.paper.span,V=100,W=m.font&&m.font.match(/\d+(?:\.\d*)?(?=px)/);p=U.style,m.font&&(p.font=m.font),m["font-family"]&&(p.fontFamily=m["font-family"]),m["font-weight"]&&(p.fontWeight=m["font-weight"]),m["font-style"]&&(p.fontStyle=m["font-style"]),W=d(m["font-size"]||W&&W[0])||10,p.fontSize=W*V+"px",s.textpath.string&&(U.innerHTML=c(s.textpath.string).replace(/</g,"<").replace(/&/g,"&").replace(/\n/g,"<br>"));var X=U.getBoundingClientRect();s.W=m.w=(X.right-X.left)/V,s.H=m.h=(X.bottom-X.top)/V,s.X=m.x,s.Y=m.y+s.H/2,("x"in i||"y"in i)&&(s.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));for(var Y=["x","y","text","font","font-family","font-weight","font-style","font-size"],Z=0,$=Y.length;$>Z;Z++)
|
||||
if(Y[Z]in i){s._.dirty=1;break}
|
||||
switch(m["text-anchor"]){case"start":s.textpath.style["v-text-align"]="left",s.bbx=s.W/2;break;case"end":s.textpath.style["v-text-align"]="right",s.bbx=-s.W/2;break;default:s.textpath.style["v-text-align"]="center",s.bbx=0}
|
||||
s.textpath.style["v-text-kern"]=!0}},C=function(b,f,g){b.attrs=b.attrs||{};var h=(b.attrs,Math.pow),i="linear",j=".5 .5";if(b.attrs.gradient=f,f=c(f).replace(a._radial_gradient,function(a,b,c){return i="radial",b&&c&&(b=d(b),c=d(c),h(b-.5,2)+h(c-.5,2)>.25&&(c=e.sqrt(.25-h(b-.5,2))*(2*(c>.5)-1)+.5),j=b+n+c),o}),f=f.split(/\s*\-\s*/),"linear"==i){var k=f.shift();if(k=-d(k),isNaN(k))
|
||||
return null}
|
||||
var l=a._parseDots(f);if(!l)
|
||||
return null;if(b=b.shape||b.node,l.length){b.removeChild(g),g.on=!0,g.method="none",g.color=l[0].color,g.color2=l[l.length-1].color;for(var m=[],p=0,q=l.length;q>p;p++)
|
||||
l[p].offset&&m.push(l[p].offset+n+l[p].color);g.colors=m.length?m.join():"0% "+g.color,"radial"==i?(g.type="gradientTitle",g.focus="100%",g.focussize="0 0",g.focusposition=j,g.angle=0):(g.type="gradient",g.angle=(270-k)%360),b.appendChild(g)}
|
||||
return 1},D=function(b,c){this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.X=0,this.Y=0,this.attrs={},this.paper=c,this.matrix=a.matrix(),this._={transform:[],sx:1,sy:1,dx:0,dy:0,deg:0,dirty:1,dirtyT:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},E=a.el;D.prototype=E,E.constructor=D,E.transform=function(b){if(null==b)
|
||||
return this._.transform;var d,e=this.paper._viewBoxShift,f=e?"s"+[e.scale,e.scale]+"-1-1t"+[e.dx,e.dy]:o;e&&(d=b=c(b).replace(/\.{3}|\u2026/g,this._.transform||o)),a._extractTransform(this,f+b);var g,h=this.matrix.clone(),i=this.skew,j=this.node,k=~c(this.attrs.fill).indexOf("-"),l=!c(this.attrs.fill).indexOf("url(");if(h.translate(1,1),l||k||"image"==this.type)
|
||||
if(i.matrix="1 0 0 1",i.offset="0 0",g=h.split(),k&&g.noRotation||!g.isSimple){j.style.filter=h.toFilter();var m=this.getBBox(),p=this.getBBox(1),q=m.x-p.x,r=m.y-p.y;j.coordorigin=q* -u+n+r* -u,z(this,1,1,q,r,0)}else
|
||||
j.style.filter=o,z(this,g.scalex,g.scaley,g.dx,g.dy,g.rotate);else
|
||||
j.style.filter=o,i.matrix=c(h),i.offset=h.offset();return null!==d&&(this._.transform=d,a._extractTransform(this,d)),this},E.rotate=function(a,b,e){if(this.removed)
|
||||
return this;if(null!=a){if(a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),null==e&&(b=e),null==b||null==e){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}
|
||||
return this._.dirtyT=1,this.transform(this._.transform.concat([["r",a,b,e]])),this}},E.translate=function(a,b){return this.removed?this:(a=c(a).split(k),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this._.bbox&&(this._.bbox.x+=a,this._.bbox.y+=b),this.transform(this._.transform.concat([["t",a,b]])),this)},E.scale=function(a,b,e,f){if(this.removed)
|
||||
return this;if(a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3]),isNaN(e)&&(e=null),isNaN(f)&&(f=null)),a=d(a[0]),null==b&&(b=a),null==f&&(e=f),null==e||null==f)
|
||||
var g=this.getBBox(1);return e=null==e?g.x+g.width/2:e,f=null==f?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]])),this._.dirtyT=1,this},E.hide=function(){return!this.removed&&(this.node.style.display="none"),this},E.show=function(){return!this.removed&&(this.node.style.display=o),this},E.auxGetBBox=a.el.getBBox,E.getBBox=function(){var a=this.auxGetBBox();if(this.paper&&this.paper._viewBoxShift){var b={},c=1/this.paper._viewBoxShift.scale;return b.x=a.x-this.paper._viewBoxShift.dx,b.x*=c,b.y=a.y-this.paper._viewBoxShift.dy,b.y*=c,b.width=a.width*c,b.height=a.height*c,b.x2=b.x+b.width,b.y2=b.y+b.height,b}
|
||||
return a},E._getBBox=function(){return this.removed?{}:{x:this.X+(this.bbx||0)-this.W/2,y:this.Y-this.H,width:this.W,height:this.H}},E.remove=function(){if(!this.removed&&this.node.parentNode){this.paper.__set__&&this.paper.__set__.exclude(this),a.eve.unbind("raphael.*.*."+this.id),a._tear(this,this.paper),this.node.parentNode.removeChild(this.node),this.shape&&this.shape.parentNode.removeChild(this.shape);for(var b in this)
|
||||
this[b]="function"==typeof this[b]?a._removedFactory(b):null;this.removed=!0}},E.attr=function(c,d){if(this.removed)
|
||||
return this;if(null==c){var e={};for(var f in this.attrs)
|
||||
this.attrs[b](f)&&(e[f]=this.attrs[f]);return e.gradient&&"none"==e.fill&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform,e}
|
||||
if(null==d&&a.is(c,"string")){if(c==j&&"none"==this.attrs.fill&&this.attrs.gradient)
|
||||
return this.attrs.gradient;for(var g=c.split(k),h={},i=0,m=g.length;m>i;i++)
|
||||
c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return m-1?h:h[g[0]]}
|
||||
if(this.attrs&&null==d&&a.is(c,"array")){for(h={},i=0,m=c.length;m>i;i++)
|
||||
h[c[i]]=this.attr(c[i]);return h}
|
||||
var n;null!=d&&(n={},n[c]=d),null==d&&a.is(c,"object")&&(n=c);for(var o in n)
|
||||
l("raphael.attr."+o+"."+this.id,this,n[o]);if(n){for(o in this.paper.customAttributes)
|
||||
if(this.paper.customAttributes[b](o)&&n[b](o)&&a.is(this.paper.customAttributes[o],"function")){var p=this.paper.customAttributes[o].apply(this,[].concat(n[o]));this.attrs[o]=n[o];for(var q in p)
|
||||
p[b](q)&&(n[q]=p[q])}
|
||||
n.text&&"text"==this.type&&(this.textpath.string=n.text),B(this,n)}
|
||||
return this},E.toFront=function(){return!this.removed&&this.node.parentNode.appendChild(this.node),this.paper&&this.paper.top!=this&&a._tofront(this,this.paper),this},E.toBack=function(){return this.removed?this:(this.node.parentNode.firstChild!=this.node&&(this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild),a._toback(this,this.paper)),this)},E.insertAfter=function(b){return this.removed?this:(b.constructor==a.st.constructor&&(b=b[b.length-1]),b.node.nextSibling?b.node.parentNode.insertBefore(this.node,b.node.nextSibling):b.node.parentNode.appendChild(this.node),a._insertafter(this,b,this.paper),this)},E.insertBefore=function(b){return this.removed?this:(b.constructor==a.st.constructor&&(b=b[0]),b.node.parentNode.insertBefore(this.node,b.node),a._insertbefore(this,b,this.paper),this)},E.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;return d=d.replace(r,o),0!==+b?(this.attrs.blur=b,c.filter=d+n+m+".Blur(pixelradius="+(+b||1.5)+")",c.margin=a.format("-{0}px 0 0 -{0}px",f(+b||1.5))):(c.filter=d,c.margin=0,delete this.attrs.blur),this},a._engine.path=function(a,b){var c=F("shape");c.style.cssText=t,c.coordsize=u+n+u,c.coordorigin=b.coordorigin;var d=new D(c,b),e={fill:"none",stroke:"#000"};a&&(e.path=a),d.type="path",d.path=[],d.Path=o,B(d,e),b.canvas.appendChild(c);var f=F("skew");return f.on=!0,c.appendChild(f),d.skew=f,d.transform(o),d},a._engine.rect=function(b,c,d,e,f,g){var h=a._rectPath(c,d,e,f,g),i=b.path(h),j=i.attrs;return i.X=j.x=c,i.Y=j.y=d,i.W=j.width=e,i.H=j.height=f,j.r=g,j.path=h,i.type="rect",i},a._engine.ellipse=function(a,b,c,d,e){{var f=a.path();f.attrs}
|
||||
return f.X=b-d,f.Y=c-e,f.W=2*d,f.H=2*e,f.type="ellipse",B(f,{cx:b,cy:c,rx:d,ry:e}),f},a._engine.circle=function(a,b,c,d){{var e=a.path();e.attrs}
|
||||
return e.X=b-d,e.Y=c-d,e.W=e.H=2*d,e.type="circle",B(e,{cx:b,cy:c,r:d}),e},a._engine.image=function(b,c,d,e,f,g){var h=a._rectPath(d,e,f,g),i=b.path(h).attr({stroke:"none"}),k=i.attrs,l=i.node,m=l.getElementsByTagName(j)[0];return k.src=c,i.X=k.x=d,i.Y=k.y=e,i.W=k.width=f,i.H=k.height=g,k.path=h,i.type="image",m.parentNode==l&&l.removeChild(m),m.rotate=!0,m.src=c,m.type="tile",i._.fillpos=[d,e],i._.fillsize=[f,g],l.appendChild(m),z(i,1,1,0,0,0),i},a._engine.text=function(b,d,e,g){var h=F("shape"),i=F("path"),j=F("textpath");d=d||0,e=e||0,g=g||"",i.v=a.format("m{0},{1}l{2},{1}",f(d*u),f(e*u),f(d*u)+1),i.textpathok=!0,j.string=c(g),j.on=!0,h.style.cssText=t,h.coordsize=u+n+u,h.coordorigin="0 0";var k=new D(h,b),l={fill:"#000",stroke:"none",font:a._availableAttrs.font,text:g};k.shape=h,k.path=i,k.textpath=j,k.type="text",k.attrs.text=c(g),k.attrs.x=d,k.attrs.y=e,k.attrs.w=1,k.attrs.h=1,B(k,l),h.appendChild(j),h.appendChild(i),b.canvas.appendChild(h);var m=F("skew");return m.on=!0,h.appendChild(m),k.skew=m,k.transform(o),k},a._engine.setSize=function(b,c){var d=this.canvas.style;return this.width=b,this.height=c,b==+b&&(b+="px"),c==+c&&(c+="px"),d.width=b,d.height=c,d.clip="rect(0 "+b+" "+c+" 0)",this._viewBox&&a._engine.setViewBox.apply(this,this._viewBox),this},a._engine.setViewBox=function(b,c,d,e,f){a.eve("raphael.setViewBox",this,this._viewBox,[b,c,d,e,f]);var g,h,i=this.getSize(),j=i.width,k=i.height;return f&&(g=k/e,h=j/d,j>d*g&&(b-=(j-d*g)/2/g),k>e*h&&(c-=(k-e*h)/2/h)),this._viewBox=[b,c,d,e,!!f],this._viewBoxShift={dx:-b,dy:-c,scale:i},this.forEach(function(a){a.transform("...")}),this};var F;a._engine.initWin=function(a){var b=a.document;b.styleSheets.length<31?b.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)"):b.styleSheets[0].addRule(".rvml","behavior:url(#default#VML)");try{!b.namespaces.rvml&&b.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),F=function(a){return b.createElement("<rvml:"+a+' class="rvml">')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e=b.width,f=b.x,g=b.y;if(!c)
|
||||
throw new Error("VML container not found.");var h=new a._Paper,i=h.canvas=a._g.doc.createElement("div"),j=i.style;return f=f||0,g=g||0,e=e||512,d=d||342,h.width=e,h.height=d,e==+e&&(e+="px"),d==+d&&(d+="px"),h.coordsize=1e3*u+n+1e3*u,h.coordorigin="0 0",h.span=a._g.doc.createElement("span"),h.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",i.appendChild(h.span),j.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",e,d),1==c?(a._g.doc.body.appendChild(i),j.left=f+"px",j.top=g+"px",j.position="absolute"):c.firstChild?c.insertBefore(i,c.firstChild):c.appendChild(i),h.renderfix=function(){},h},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)
|
||||
this[b]="function"==typeof this[b]?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)
|
||||
E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}
|
||||
(H))}});JustGage=function(config){var obj=this;if(config===null||config===undefined){console.log('* justgage: Make sure to pass options to the constructor!');return false;}
|
||||
var node;if(config.id!==null&&config.id!==undefined){node=document.getElementById(config.id);if(!node){console.log('* justgage: No element with id : %s found',config.id);return false;}}else if(config.parentNode!==null&&config.parentNode!==undefined){node=config.parentNode;}else{console.log('* justgage: Make sure to pass the existing element id or parentNode to the constructor.');return false;}
|
||||
var dataset=node.dataset?node.dataset:{};var defaults=(config.defaults!==null&&config.defaults!==undefined)?config.defaults:false;if(defaults!==false){config=extend({},config,defaults);delete config.defaults;}
|
||||
obj.config={id:config.id,value:kvLookup('value',config,dataset,0,'float'),defaults:kvLookup('defaults',config,dataset,0,false),parentNode:kvLookup('parentNode',config,dataset,null),width:kvLookup('width',config,dataset,null),height:kvLookup('height',config,dataset,null),title:kvLookup('title',config,dataset,""),titleFontColor:kvLookup('titleFontColor',config,dataset,"#999999"),titleFontFamily:kvLookup('titleFontFamily',config,dataset,"sans-serif"),titlePosition:kvLookup('titlePosition',config,dataset,"above"),valueFontColor:kvLookup('valueFontColor',config,dataset,"#010101"),valueFontFamily:kvLookup('valueFontFamily',config,dataset,"Arial"),symbol:kvLookup('symbol',config,dataset,''),min:kvLookup('min',config,dataset,0,'float'),max:kvLookup('max',config,dataset,100,'float'),reverse:kvLookup('reverse',config,dataset,false),humanFriendlyDecimal:kvLookup('humanFriendlyDecimal',config,dataset,0),textRenderer:kvLookup('textRenderer',config,dataset,null),gaugeWidthScale:kvLookup('gaugeWidthScale',config,dataset,1.0),gaugeColor:kvLookup('gaugeColor',config,dataset,"#edebeb"),label:kvLookup('label',config,dataset,''),labelFontColor:kvLookup('labelFontColor',config,dataset,"#b3b3b3"),shadowOpacity:kvLookup('shadowOpacity',config,dataset,0.2),shadowSize:kvLookup('shadowSize',config,dataset,5),shadowVerticalOffset:kvLookup('shadowVerticalOffset',config,dataset,3),levelColors:kvLookup('levelColors',config,dataset,["#a9d70b","#f9c802","#ff0000"],'array',','),startAnimationTime:kvLookup('startAnimationTime',config,dataset,700),startAnimationType:kvLookup('startAnimationType',config,dataset,'>'),refreshAnimationTime:kvLookup('refreshAnimationTime',config,dataset,700),refreshAnimationType:kvLookup('refreshAnimationType',config,dataset,'>'),donutStartAngle:kvLookup('donutStartAngle',config,dataset,90),valueMinFontSize:kvLookup('valueMinFontSize',config,dataset,16),titleMinFontSize:kvLookup('titleMinFontSize',config,dataset,10),labelMinFontSize:kvLookup('labelMinFontSize',config,dataset,10),minLabelMinFontSize:kvLookup('minLabelMinFontSize',config,dataset,10),maxLabelMinFontSize:kvLookup('maxLabelMinFontSize',config,dataset,10),hideValue:kvLookup('hideValue',config,dataset,false),hideMinMax:kvLookup('hideMinMax',config,dataset,false),hideInnerShadow:kvLookup('hideInnerShadow',config,dataset,false),humanFriendly:kvLookup('humanFriendly',config,dataset,false),noGradient:kvLookup('noGradient',config,dataset,false),donut:kvLookup('donut',config,dataset,false),relativeGaugeSize:kvLookup('relativeGaugeSize',config,dataset,false),counter:kvLookup('counter',config,dataset,false),decimals:kvLookup('decimals',config,dataset,0),customSectors:kvLookup('customSectors',config,dataset,[]),formatNumber:kvLookup('formatNumber',config,dataset,false),pointer:kvLookup('pointer',config,dataset,false),pointerOptions:kvLookup('pointerOptions',config,dataset,[])};var
|
||||
canvasW,canvasH,widgetW,widgetH,aspect,dx,dy,titleFontSize,titleX,titleY,valueFontSize,valueX,valueY,labelFontSize,labelX,labelY,minFontSize,minX,minY,maxFontSize,maxX,maxY;if(obj.config.value>obj.config.max)
|
||||
obj.config.value=obj.config.max;if(obj.config.value<obj.config.min)
|
||||
obj.config.value=obj.config.min;obj.originalValue=kvLookup('value',config,dataset,-1,'float');if(obj.config.id!==null&&(document.getElementById(obj.config.id))!==null){obj.canvas=Raphael(obj.config.id,"100%","100%");}else if(obj.config.parentNode!==null){obj.canvas=Raphael(obj.config.parentNode,"100%","100%");}
|
||||
if(obj.config.relativeGaugeSize===true){obj.canvas.setViewBox(0,0,200,150,true);}
|
||||
if(obj.config.relativeGaugeSize===true){canvasW=200;canvasH=150;}else if(obj.config.width!==null&&obj.config.height!==null){canvasW=obj.config.width;canvasH=obj.config.height;}else if(obj.config.parentNode!==null){obj.canvas.setViewBox(0,0,200,150,true);canvasW=200;canvasH=150;}else{canvasW=getStyle(document.getElementById(obj.config.id),"width").slice(0,-2)*1;canvasH=getStyle(document.getElementById(obj.config.id),"height").slice(0,-2)*1;}
|
||||
if(obj.config.donut===true){if(canvasW>canvasH){widgetH=canvasH;widgetW=widgetH;}else if(canvasW<canvasH){widgetW=canvasW;widgetH=widgetW;if(widgetH>canvasH){aspect=widgetH/canvasH;widgetH=widgetH/aspect;widgetW=widgetH/aspect;}}else{widgetW=canvasW;widgetH=widgetW;}
|
||||
dx=(canvasW-widgetW)/2;dy=(canvasH-widgetH)/2;titleFontSize=((widgetH/8)>10)?(widgetH/10):10;titleX=dx+widgetW/2;titleY=dy+widgetH/11;valueFontSize=((widgetH/6.4)>16)?(widgetH/5.4):18;valueX=dx+widgetW/2;if(obj.config.label!==''){valueY=dy+widgetH/1.85;}else{valueY=dy+widgetH/1.7;}
|
||||
labelFontSize=((widgetH/16)>10)?(widgetH/16):10;labelX=dx+widgetW/2;labelY=valueY+labelFontSize;minFontSize=((widgetH/16)>10)?(widgetH/16):10;minX=dx+(widgetW/10)+(widgetW/6.666666666666667*obj.config.gaugeWidthScale)/2;minY=labelY;maxFontSize=((widgetH/16)>10)?(widgetH/16):10;maxX=dx+widgetW-(widgetW/10)-(widgetW/6.666666666666667*obj.config.gaugeWidthScale)/2;maxY=labelY;}else{if(canvasW>canvasH){widgetH=canvasH;widgetW=widgetH*1.25;if(widgetW>canvasW){aspect=widgetW/canvasW;widgetW=widgetW/aspect;widgetH=widgetH/aspect;}}else if(canvasW<canvasH){widgetW=canvasW;widgetH=widgetW/1.25;if(widgetH>canvasH){aspect=widgetH/canvasH;widgetH=widgetH/aspect;widgetW=widgetH/aspect;}}else{widgetW=canvasW;widgetH=widgetW*0.75;}
|
||||
dx=(canvasW-widgetW)/2;dy=(canvasH-widgetH)/2;if(obj.config.titlePosition==='below'){dy-=(widgetH/6.4);}
|
||||
titleFontSize=((widgetH/8)>obj.config.titleMinFontSize)?(widgetH/10):obj.config.titleMinFontSize;titleX=dx+widgetW/2;titleY=dy+(obj.config.titlePosition==='below'?(widgetH*1.07):(widgetH/6.4));valueFontSize=((widgetH/6.5)>obj.config.valueMinFontSize)?(widgetH/6.5):obj.config.valueMinFontSize;valueX=dx+widgetW/2;valueY=dy+widgetH/1.275;labelFontSize=((widgetH/16)>obj.config.labelMinFontSize)?(widgetH/16):obj.config.labelMinFontSize;labelX=dx+widgetW/2;labelY=valueY+valueFontSize/2+5;minFontSize=((widgetH/16)>obj.config.minLabelMinFontSize)?(widgetH/16):obj.config.minLabelMinFontSize;minX=dx+(widgetW/10)+(widgetW/6.666666666666667*obj.config.gaugeWidthScale)/2;minY=labelY;maxFontSize=((widgetH/16)>obj.config.maxLabelMinFontSize)?(widgetH/16):obj.config.maxLabelMinFontSize;maxX=dx+widgetW-(widgetW/10)-(widgetW/6.666666666666667*obj.config.gaugeWidthScale)/2;maxY=labelY;}
|
||||
obj.params={canvasW:canvasW,canvasH:canvasH,widgetW:widgetW,widgetH:widgetH,dx:dx,dy:dy,titleFontSize:titleFontSize,titleX:titleX,titleY:titleY,valueFontSize:valueFontSize,valueX:valueX,valueY:valueY,labelFontSize:labelFontSize,labelX:labelX,labelY:labelY,minFontSize:minFontSize,minX:minX,minY:minY,maxFontSize:maxFontSize,maxX:maxX,maxY:maxY};canvasW,canvasH,widgetW,widgetH,aspect,dx,dy,titleFontSize,titleX,titleY,valueFontSize,valueX,valueY,labelFontSize,labelX,labelY,minFontSize,minX,minY,maxFontSize,maxX,maxY=null;obj.canvas.customAttributes.pki=function(value,min,max,w,h,dx,dy,gws,donut,reverse){var alpha,Ro,Ri,Cx,Cy,Xo,Yo,Xi,Yi,path;if(donut){alpha=(1-2*(value-min)/(max-min))*Math.PI;Ro=w/2-w/7;Ri=Ro-w/6.666666666666667*gws;Cx=w/2+dx;Cy=h/1.95+dy;Xo=w/2+dx+Ro*Math.cos(alpha);Yo=h-(h-Cy)-Ro*Math.sin(alpha);Xi=w/2+dx+Ri*Math.cos(alpha);Yi=h-(h-Cy)-Ri*Math.sin(alpha);path="M"+(Cx-Ri)+","+Cy+" ";path+="L"+(Cx-Ro)+","+Cy+" ";if(value>((max-min)/2)){path+="A"+Ro+","+Ro+" 0 0 1 "+(Cx+Ro)+","+Cy+" ";}
|
||||
path+="A"+Ro+","+Ro+" 0 0 1 "+Xo+","+Yo+" ";path+="L"+Xi+","+Yi+" ";if(value>((max-min)/2)){path+="A"+Ri+","+Ri+" 0 0 0 "+(Cx+Ri)+","+Cy+" ";}
|
||||
path+="A"+Ri+","+Ri+" 0 0 0 "+(Cx-Ri)+","+Cy+" ";path+="Z ";return{path:path};}else{alpha=(1-(value-min)/(max-min))*Math.PI;Ro=w/2-w/10;Ri=Ro-w/6.666666666666667*gws;Cx=w/2+dx;Cy=h/1.25+dy;Xo=w/2+dx+Ro*Math.cos(alpha);Yo=h-(h-Cy)-Ro*Math.sin(alpha);Xi=w/2+dx+Ri*Math.cos(alpha);Yi=h-(h-Cy)-Ri*Math.sin(alpha);path="M"+(Cx-Ri)+","+Cy+" ";path+="L"+(Cx-Ro)+","+Cy+" ";path+="A"+Ro+","+Ro+" 0 0 1 "+Xo+","+Yo+" ";path+="L"+Xi+","+Yi+" ";path+="A"+Ri+","+Ri+" 0 0 0 "+(Cx-Ri)+","+Cy+" ";path+="Z ";return{path:path};}
|
||||
alpha,Ro,Ri,Cx,Cy,Xo,Yo,Xi,Yi,path=null;};obj.canvas.customAttributes.ndl=function(value,min,max,w,h,dx,dy,gws,donut){var dlt=w*3.5/100;var dlb=w/15;var dw=w/100;if(obj.config.pointerOptions.toplength!=null&&obj.config.pointerOptions.toplength!=undefined)
|
||||
dlt=obj.config.pointerOptions.toplength;if(obj.config.pointerOptions.bottomlength!=null&&obj.config.pointerOptions.bottomlength!=undefined)
|
||||
dlb=obj.config.pointerOptions.bottomlength;if(obj.config.pointerOptions.bottomwidth!=null&&obj.config.pointerOptions.bottomwidth!=undefined)
|
||||
dw=obj.config.pointerOptions.bottomwidth;var alpha,Ro,Ri,Cx,Cy,Xo,Yo,Xi,Yi,Xc,Yc,Xz,Yz,Xa,Ya,Xb,Yb,path;if(donut){alpha=(1-2*(value-min)/(max-min))*Math.PI;Ro=w/2-w/7;Ri=Ro-w/6.666666666666667*gws;Cx=w/2+dx;Cy=h/1.95+dy;Xo=w/2+dx+Ro*Math.cos(alpha);Yo=h-(h-Cy)-Ro*Math.sin(alpha);Xi=w/2+dx+Ri*Math.cos(alpha);Yi=h-(h-Cy)-Ri*Math.sin(alpha);Xc=Xo+dlt*Math.cos(alpha);Yc=Yo-dlt*Math.sin(alpha);Xz=Xi-dlb*Math.cos(alpha);Yz=Yi+dlb*Math.sin(alpha);Xa=Xz+dw*Math.sin(alpha);Ya=Yz+dw*Math.cos(alpha);Xb=Xz-dw*Math.sin(alpha);Yb=Yz-dw*Math.cos(alpha);path='M'+Xa+','+Ya+' ';path+='L'+Xb+','+Yb+' ';path+='L'+Xc+','+Yc+' ';path+='Z ';return{path:path};}else{alpha=(1-(value-min)/(max-min))*Math.PI;Ro=w/2-w/10;Ri=Ro-w/6.666666666666667*gws;Cx=w/2+dx;Cy=h/1.25+dy;Xo=w/2+dx+Ro*Math.cos(alpha);Yo=h-(h-Cy)-Ro*Math.sin(alpha);Xi=w/2+dx+Ri*Math.cos(alpha);Yi=h-(h-Cy)-Ri*Math.sin(alpha);Xc=Xo+dlt*Math.cos(alpha);Yc=Yo-dlt*Math.sin(alpha);Xz=Xi-dlb*Math.cos(alpha);Yz=Yi+dlb*Math.sin(alpha);Xa=Xz+dw*Math.sin(alpha);Ya=Yz+dw*Math.cos(alpha);Xb=Xz-dw*Math.sin(alpha);Yb=Yz-dw*Math.cos(alpha);path='M'+Xa+','+Ya+' ';path+='L'+Xb+','+Yb+' ';path+='L'+Xc+','+Yc+' ';path+='Z ';return{path:path};}
|
||||
alpha,Ro,Ri,Cx,Cy,Xo,Yo,Xi,Yi,Xc,Yc,Xz,Yz,Xa,Ya,Xb,Yb,path=null;};obj.gauge=obj.canvas.path().attr({"stroke":"none","fill":obj.config.gaugeColor,pki:[obj.config.max,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut,obj.config.reverse]});obj.level=obj.canvas.path().attr({"stroke":"none","fill":getColor(obj.config.value,(obj.config.value-obj.config.min)/(obj.config.max-obj.config.min),obj.config.levelColors,obj.config.noGradient,obj.config.customSectors),pki:[obj.config.min,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut,obj.config.reverse]});if(obj.config.donut){obj.level.transform("r"+obj.config.donutStartAngle+", "+(obj.params.widgetW/2+obj.params.dx)+", "+(obj.params.widgetH/1.95+obj.params.dy));}
|
||||
if(obj.config.pointer){obj.needle=obj.canvas.path().attr({"stroke":(obj.config.pointerOptions.stroke!==null&&obj.config.pointerOptions.stroke!==undefined)?obj.config.pointerOptions.stroke:"none","stroke-width":(obj.config.pointerOptions.stroke_width!==null&&obj.config.pointerOptions.stroke_width!==undefined)?obj.config.pointerOptions.stroke_width:0,"stroke-linecap":(obj.config.pointerOptions.stroke_linecap!==null&&obj.config.pointerOptions.stroke_linecap!==undefined)?obj.config.pointerOptions.stroke_linecap:"square","fill":(obj.config.pointerOptions.color!==null&&obj.config.pointerOptions.color!==undefined)?obj.config.pointerOptions.color:"#000000",ndl:[obj.config.min,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut]});if(obj.config.donut){obj.needle.transform("r"+obj.config.donutStartAngle+", "+(obj.params.widgetW/2+obj.params.dx)+", "+(obj.params.widgetH/1.95+obj.params.dy));}}
|
||||
obj.txtTitle=obj.canvas.text(obj.params.titleX,obj.params.titleY,obj.config.title);obj.txtTitle.attr({"font-size":obj.params.titleFontSize,"font-weight":"bold","font-family":obj.config.titleFontFamily,"fill":obj.config.titleFontColor,"fill-opacity":"1"});setDy(obj.txtTitle,obj.params.titleFontSize,obj.params.titleY);obj.txtValue=obj.canvas.text(obj.params.valueX,obj.params.valueY,0);obj.txtValue.attr({"font-size":obj.params.valueFontSize,"font-weight":"bold","font-family":obj.config.valueFontFamily,"fill":obj.config.valueFontColor,"fill-opacity":"0"});setDy(obj.txtValue,obj.params.valueFontSize,obj.params.valueY);obj.txtLabel=obj.canvas.text(obj.params.labelX,obj.params.labelY,obj.config.label);obj.txtLabel.attr({"font-size":obj.params.labelFontSize,"font-weight":"normal","font-family":"Arial","fill":obj.config.labelFontColor,"fill-opacity":"0"});setDy(obj.txtLabel,obj.params.labelFontSize,obj.params.labelY);var min=obj.config.min;if(obj.config.reverse){min=obj.config.max;}
|
||||
obj.txtMinimum=min;if(obj.config.humanFriendly){obj.txtMinimum=humanFriendlyNumber(min,obj.config.humanFriendlyDecimal);}else if(obj.config.formatNumber){obj.txtMinimum=formatNumber(min);}
|
||||
obj.txtMin=obj.canvas.text(obj.params.minX,obj.params.minY,obj.txtMinimum);obj.txtMin.attr({"font-size":obj.params.minFontSize,"font-weight":"normal","font-family":"Arial","fill":obj.config.labelFontColor,"fill-opacity":(obj.config.hideMinMax||obj.config.donut)?"0":"1"});setDy(obj.txtMin,obj.params.minFontSize,obj.params.minY);var max=obj.config.max;if(obj.config.reverse){max=obj.config.min;}
|
||||
obj.txtMaximum=max;if(obj.config.humanFriendly){obj.txtMaximum=humanFriendlyNumber(max,obj.config.humanFriendlyDecimal);}else if(obj.config.formatNumber){obj.txtMaximum=formatNumber(max);}
|
||||
obj.txtMax=obj.canvas.text(obj.params.maxX,obj.params.maxY,obj.txtMaximum);obj.txtMax.attr({"font-size":obj.params.maxFontSize,"font-weight":"normal","font-family":"Arial","fill":obj.config.labelFontColor,"fill-opacity":(obj.config.hideMinMax||obj.config.donut)?"0":"1"});setDy(obj.txtMax,obj.params.maxFontSize,obj.params.maxY);var defs=obj.canvas.canvas.childNodes[1];var svg="http://www.w3.org/2000/svg";if(ie!=='undefined'&&ie<9){}
|
||||
else if(ie!=='undefined'){onCreateElementNsReady(function(){obj.generateShadow(svg,defs);});}else{obj.generateShadow(svg,defs);}
|
||||
defs,svg=null;if(obj.config.textRenderer){obj.originalValue=obj.config.textRenderer(obj.originalValue);}else if(obj.config.humanFriendly){obj.originalValue=humanFriendlyNumber(obj.originalValue,obj.config.humanFriendlyDecimal)+obj.config.symbol;}else if(obj.config.formatNumber){obj.originalValue=formatNumber(obj.originalValue)+obj.config.symbol;}else{obj.originalValue=(obj.originalValue*1).toFixed(obj.config.decimals)+obj.config.symbol;}
|
||||
if(obj.config.counter===true){eve.on("raphael.anim.frame."+(obj.level.id),function(){var currentValue=obj.level.attr("pki")[0];if(obj.config.reverse){currentValue=(obj.config.max*1)+(obj.config.min*1)-(obj.level.attr("pki")[0]*1);}
|
||||
if(obj.config.textRenderer){obj.txtValue.attr("text",obj.config.textRenderer(Math.floor(currentValue)));}else if(obj.config.humanFriendly){obj.txtValue.attr("text",humanFriendlyNumber(Math.floor(currentValue),obj.config.humanFriendlyDecimal)+obj.config.symbol);}else if(obj.config.formatNumber){obj.txtValue.attr("text",formatNumber(Math.floor(currentValue))+obj.config.symbol);}else{obj.txtValue.attr("text",(currentValue*1).toFixed(obj.config.decimals)+obj.config.symbol);}
|
||||
setDy(obj.txtValue,obj.params.valueFontSize,obj.params.valueY);currentValue=null;});eve.on("raphael.anim.finish."+(obj.level.id),function(){obj.txtValue.attr({"text":obj.originalValue});setDy(obj.txtValue,obj.params.valueFontSize,obj.params.valueY);});}else{eve.on("raphael.anim.start."+(obj.level.id),function(){obj.txtValue.attr({"text":obj.originalValue});setDy(obj.txtValue,obj.params.valueFontSize,obj.params.valueY);});}
|
||||
var rvl=obj.config.value;if(obj.config.reverse){rvl=(obj.config.max*1)+(obj.config.min*1)-(obj.config.value*1);}
|
||||
obj.level.animate({pki:[rvl,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut,obj.config.reverse]},obj.config.startAnimationTime,obj.config.startAnimationType);if(obj.config.pointer){obj.needle.animate({ndl:[rvl,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut]},obj.config.startAnimationTime,obj.config.startAnimationType);}
|
||||
obj.txtValue.animate({"fill-opacity":(obj.config.hideValue)?"0":"1"},obj.config.startAnimationTime,obj.config.startAnimationType);obj.txtLabel.animate({"fill-opacity":"1"},obj.config.startAnimationTime,obj.config.startAnimationType);};JustGage.prototype.refresh=function(val,max){var obj=this;var displayVal,color,max=max||null;if(max!==null){obj.config.max=max;obj.txtMaximum=obj.config.max;if(obj.config.humanFriendly){obj.txtMaximum=humanFriendlyNumber(obj.config.max,obj.config.humanFriendlyDecimal);}else if(obj.config.formatNumber){obj.txtMaximum=formatNumber(obj.config.max);}
|
||||
if(!obj.config.reverse){obj.txtMax.attr({"text":obj.txtMaximum});setDy(obj.txtMax,obj.params.maxFontSize,obj.params.maxY);}else{obj.txtMin.attr({"text":obj.txtMaximum});setDy(obj.txtMin,obj.params.minFontSize,obj.params.minY);}}
|
||||
displayVal=val;if((val*1)>(obj.config.max*1)){val=(obj.config.max*1);}
|
||||
if((val*1)<(obj.config.min*1)){val=(obj.config.min*1);}
|
||||
color=getColor(val,(val-obj.config.min)/(obj.config.max-obj.config.min),obj.config.levelColors,obj.config.noGradient,obj.config.customSectors);if(obj.config.textRenderer){displayVal=obj.config.textRenderer(displayVal);}else if(obj.config.humanFriendly){displayVal=humanFriendlyNumber(displayVal,obj.config.humanFriendlyDecimal)+obj.config.symbol;}else if(obj.config.formatNumber){displayVal=formatNumber((displayVal*1).toFixed(obj.config.decimals))+obj.config.symbol;}else{displayVal=(displayVal*1).toFixed(obj.config.decimals)+obj.config.symbol;}
|
||||
obj.originalValue=displayVal;obj.config.value=val*1;if(!obj.config.counter){obj.txtValue.attr({"text":displayVal});setDy(obj.txtValue,obj.params.valueFontSize,obj.params.valueY);}
|
||||
var rvl=obj.config.value;if(obj.config.reverse){rvl=(obj.config.max*1)+(obj.config.min*1)-(obj.config.value*1);}
|
||||
obj.level.animate({pki:[rvl,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut,obj.config.reverse],"fill":color},obj.config.refreshAnimationTime,obj.config.refreshAnimationType);if(obj.config.pointer){obj.needle.animate({ndl:[rvl,obj.config.min,obj.config.max,obj.params.widgetW,obj.params.widgetH,obj.params.dx,obj.params.dy,obj.config.gaugeWidthScale,obj.config.donut]},obj.config.refreshAnimationTime,obj.config.refreshAnimationType);}
|
||||
obj,displayVal,color,max=null;};JustGage.prototype.generateShadow=function(svg,defs){var obj=this;var sid="inner-shadow-"+obj.config.id;var gaussFilter,feOffset,feGaussianBlur,feComposite1,feFlood,feComposite2,feComposite3;gaussFilter=document.createElementNS(svg,"filter");gaussFilter.setAttribute("id",sid);defs.appendChild(gaussFilter);feOffset=document.createElementNS(svg,"feOffset");feOffset.setAttribute("dx",0);feOffset.setAttribute("dy",obj.config.shadowVerticalOffset);gaussFilter.appendChild(feOffset);feGaussianBlur=document.createElementNS(svg,"feGaussianBlur");feGaussianBlur.setAttribute("result","offset-blur");feGaussianBlur.setAttribute("stdDeviation",obj.config.shadowSize);gaussFilter.appendChild(feGaussianBlur);feComposite1=document.createElementNS(svg,"feComposite");feComposite1.setAttribute("operator","out");feComposite1.setAttribute("in","SourceGraphic");feComposite1.setAttribute("in2","offset-blur");feComposite1.setAttribute("result","inverse");gaussFilter.appendChild(feComposite1);feFlood=document.createElementNS(svg,"feFlood");feFlood.setAttribute("flood-color","black");feFlood.setAttribute("flood-opacity",obj.config.shadowOpacity);feFlood.setAttribute("result","color");gaussFilter.appendChild(feFlood);feComposite2=document.createElementNS(svg,"feComposite");feComposite2.setAttribute("operator","in");feComposite2.setAttribute("in","color");feComposite2.setAttribute("in2","inverse");feComposite2.setAttribute("result","shadow");gaussFilter.appendChild(feComposite2);feComposite3=document.createElementNS(svg,"feComposite");feComposite3.setAttribute("operator","over");feComposite3.setAttribute("in","shadow");feComposite3.setAttribute("in2","SourceGraphic");gaussFilter.appendChild(feComposite3);if(!obj.config.hideInnerShadow){obj.canvas.canvas.childNodes[2].setAttribute("filter","url(#"+sid+")");obj.canvas.canvas.childNodes[3].setAttribute("filter","url(#"+sid+")");}
|
||||
gaussFilter,feOffset,feGaussianBlur,feComposite1,feFlood,feComposite2,feComposite3=null;};function kvLookup(key,tablea,tableb,defval,datatype,delimiter){var val=defval;var canConvert=false;if(!(key===null||key===undefined)){if(tableb!==null&&tableb!==undefined&&typeof tableb==="object"&&key in tableb){val=tableb[key];canConvert=true;}else if(tablea!==null&&tablea!==undefined&&typeof tablea==="object"&&key in tablea){val=tablea[key];canConvert=true;}else{val=defval;}
|
||||
if(canConvert===true){if(datatype!==null&&datatype!==undefined){switch(datatype){case'int':val=parseInt(val,10);break;case'float':val=parseFloat(val);break;default:break;}}}}
|
||||
return val;};function getColor(val,pct,col,noGradient,custSec){var no,inc,colors,percentage,rval,gval,bval,lower,upper,range,rangePct,pctLower,pctUpper,color;var noGradient=noGradient||custSec.length>0;if(custSec.length>0){for(var i=0;i<custSec.length;i++){if(val>custSec[i].lo&&val<=custSec[i].hi){return custSec[i].color;}}}
|
||||
no=col.length;if(no===1)
|
||||
return col[0];inc=(noGradient)?(1/no):(1/(no-1));colors=[];for(i=0;i<col.length;i++){percentage=(noGradient)?(inc*(i+1)):(inc*i);rval=parseInt((cutHex(col[i])).substring(0,2),16);gval=parseInt((cutHex(col[i])).substring(2,4),16);bval=parseInt((cutHex(col[i])).substring(4,6),16);colors[i]={pct:percentage,color:{r:rval,g:gval,b:bval}};}
|
||||
if(pct===0){return'rgb('+[colors[0].color.r,colors[0].color.g,colors[0].color.b].join(',')+')';}
|
||||
for(var j=0;j<colors.length;j++){if(pct<=colors[j].pct){if(noGradient){return'rgb('+[colors[j].color.r,colors[j].color.g,colors[j].color.b].join(',')+')';}else{lower=colors[j-1];upper=colors[j];range=upper.pct-lower.pct;rangePct=(pct-lower.pct)/range;pctLower=1-rangePct;pctUpper=rangePct;color={r:Math.floor(lower.color.r*pctLower+upper.color.r*pctUpper),g:Math.floor(lower.color.g*pctLower+upper.color.g*pctUpper),b:Math.floor(lower.color.b*pctLower+upper.color.b*pctUpper)};return'rgb('+[color.r,color.g,color.b].join(',')+')';}}}}
|
||||
function setDy(elem,fontSize,txtYpos){if((!ie||ie>9)&&elem.node.firstChild.attributes.dy){elem.node.firstChild.attributes.dy.value=0;}}
|
||||
function getRandomInt(min,max){return Math.floor(Math.random()*(max-min+1))+min;}
|
||||
function cutHex(str){return(str.charAt(0)=="#")?str.substring(1,7):str;}
|
||||
function humanFriendlyNumber(n,d){var p,d2,i,s;p=Math.pow;d2=p(10,d);i=7;while(i){s=p(10,i-- *3);if(s<=n){n=Math.round(n*d2/s)/d2+"KMGTPE"[i];}}
|
||||
return n;}
|
||||
function formatNumber(x){var parts=x.toString().split(".");parts[0]=parts[0].replace(/\B(?=(\d{3})+(?!\d))/g,",");return parts.join(".");}
|
||||
function getStyle(oElm,strCssRule){var strValue="";if(document.defaultView&&document.defaultView.getComputedStyle){strValue=document.defaultView.getComputedStyle(oElm,"").getPropertyValue(strCssRule);}else if(oElm.currentStyle){strCssRule=strCssRule.replace(/\-(\w)/g,function(strMatch,p1){return p1.toUpperCase();});strValue=oElm.currentStyle[strCssRule];}
|
||||
return strValue;}
|
||||
function onCreateElementNsReady(func){if(document.createElementNS!==undefined){func();}else{setTimeout(function(){onCreateElementNsReady(func);},100);}}
|
||||
var ie=(function(){var undef,v=3,div=document.createElement('div'),all=div.getElementsByTagName('i');while(div.innerHTML='<!--[if gt IE '+(++v)+']><i></i><![endif]-->',all[0]);return v>4?v:undef;}
|
||||
());function extend(out){out=out||{};for(var i=1;i<arguments.length;i++){if(!arguments[i])
|
||||
continue;for(var key in arguments[i]){if(arguments[i].hasOwnProperty(key))
|
||||
out[key]=arguments[i][key];}}
|
||||
return out;};
|
BIN
examples/SmartSwitch/data_src/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
599
examples/SmartSwitch/data_src/index.htm
Normal file
@ -0,0 +1,599 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>Smart Switch</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="apple-touch-icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="stylesheet" href="app.css" type="text/css" />
|
||||
<style>
|
||||
body {
|
||||
font-family: arial;
|
||||
color: #999
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 1%;
|
||||
padding-inline: 2%
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 70%;
|
||||
text-align: center;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-bottom: 1px solid #2196f3;
|
||||
font-size: 22px;
|
||||
color: #2196f3;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
input[type=button] {
|
||||
background-color: #2196f3;
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 10px 10px;
|
||||
width: 62px;
|
||||
text-decoration: none;
|
||||
margin: 4px 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 34px;
|
||||
font-family: arial;
|
||||
font-weight: 700;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: #fff;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s
|
||||
}
|
||||
|
||||
input:checked+.slider {
|
||||
background-color: #2196f3
|
||||
}
|
||||
|
||||
input:focus+.slider {
|
||||
box-shadow: 0 0 1px #2196f3
|
||||
}
|
||||
|
||||
input:checked+.slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px)
|
||||
}
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.clk {
|
||||
font-size: 54px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.clear:after,
|
||||
.clear:before {
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
|
||||
.clear:after {
|
||||
clear: both
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
bottom: 0px;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
max-width: 500px;
|
||||
border: 0px solid #ccc
|
||||
}
|
||||
|
||||
.gauge {
|
||||
display: block;
|
||||
float: left
|
||||
}
|
||||
|
||||
#g1 {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
#g2 {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
.son {
|
||||
color: green;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.soff {
|
||||
color: red;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.blinking {
|
||||
animation: blinkingText 1.2s infinite;
|
||||
-webkit-animation: blinkingText 1.2s infinite;
|
||||
}
|
||||
|
||||
@keyframes blinkingText {
|
||||
0% {
|
||||
color: #ec0b0b;
|
||||
}
|
||||
49% {
|
||||
color: #ea7272;
|
||||
}
|
||||
60% {
|
||||
color: transparent;
|
||||
}
|
||||
99% {
|
||||
color: transparent;
|
||||
}
|
||||
100% {
|
||||
color: #ff0404;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
padding-top: 4px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none
|
||||
}
|
||||
|
||||
.container input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.container:hover input ~ .checkmark {
|
||||
background-color: #ccc
|
||||
}
|
||||
|
||||
.container input:checked ~ .checkmark {
|
||||
background-color: #2196F3
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none
|
||||
}
|
||||
|
||||
.container input:checked ~ .checkmark:after {
|
||||
display: block
|
||||
}
|
||||
|
||||
.container .checkmark:after {
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: white
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<table align="center">
|
||||
<tr align="center">
|
||||
<td colspan=3>
|
||||
<form name="sched">
|
||||
<label class="container" id="LZ0">Auto
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z0"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ1">M-F
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z1"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ2">Sat
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z2"><span class="checkmark"></span></label>
|
||||
<label class="container" id="LZ3">Sun
|
||||
<input type="radio" name="radio" onclick="handleClick(this);" value="Z3"><span class="checkmark"></span></label>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td onclick="ent1Click();">
|
||||
<label for="input-temperature">Temp °C</label>
|
||||
<input type="text" readonly="readonly" id="input-temperature" />
|
||||
</td>
|
||||
<td onclick="ent2Click();">
|
||||
<label for="input-popup-start">Time On</label>
|
||||
<input type="text" readonly="readonly" id="input-popup-start" />
|
||||
</td>
|
||||
<td onclick="ent2Click();">
|
||||
<label for="input-popup-stop">Time Off</label>
|
||||
<input type="text" readonly="readonly" id="input-popup-stop" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<input type="button" id="W" value="Temp" onclick="button2Click(this);" />
|
||||
</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input id="cbStyle" type="checkbox" onclick="checkboxClick(this);"><span class="slider round"></span></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" id="T" value="Timer" onclick="buttonClick(this);" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<div id="reconnect">
|
||||
<input type="button" id="N" value="WSoff" onclick="statusWs();" />
|
||||
</div>
|
||||
</td>
|
||||
<td><span id="sid"></span></td>
|
||||
<td>
|
||||
<div id="hw-reset">
|
||||
<input type="button" id="R" value="HWrst" onclick="loadDoc('hw-reset', 1);" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<div id="hbut">
|
||||
<input type="button" id="H" value="Heap" onclick="loadDoc('free-ram', 0);" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="free-ram"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="ebut">
|
||||
<input type="button" id="E" value="WEdit" onclick="buttonEClick();" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="wrapper clear">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<div id="g2" class="gauge"></div>
|
||||
<center>
|
||||
<label id="C" class="clk"></label>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<script src="app.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
const MYCORS = '192.168.0.12';
|
||||
var g1, g2;
|
||||
var Analog0 = new Array();
|
||||
var auto = true;
|
||||
|
||||
const successNotification = window.createNotification({
|
||||
positionClass: 'nfc-bottom-right',
|
||||
theme: 'info',
|
||||
showDuration: 3000
|
||||
});
|
||||
|
||||
const warningNotification = window.createNotification({
|
||||
positionClass: 'nfc-bottom-right',
|
||||
theme: 'warning',
|
||||
showDuration: 6000
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
console.log("DOM fully loaded and parsed");
|
||||
g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: -5.5,
|
||||
min: -40,
|
||||
max: 50,
|
||||
decimals: 1,
|
||||
title: "Temperature",
|
||||
titlePosition: "below",
|
||||
label: "°C",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
customSectors: [{
|
||||
color: "#328da8",
|
||||
lo: -40,
|
||||
hi: 0
|
||||
}, {
|
||||
color: "#32a852",
|
||||
lo: 0,
|
||||
hi: 25
|
||||
}, {
|
||||
color: "#ff4d4d",
|
||||
lo: 25,
|
||||
hi: 50
|
||||
}],
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: 40.8,
|
||||
min: 0,
|
||||
max: 100,
|
||||
decimals: 1,
|
||||
title: "Humidity",
|
||||
titlePosition: "below",
|
||||
label: "%",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
customSectors: [{
|
||||
color: "#ffc926",
|
||||
lo: 0,
|
||||
hi: 45
|
||||
}, {
|
||||
color: "#32a852",
|
||||
lo: 45,
|
||||
hi: 55
|
||||
}, {
|
||||
color: "#328da8",
|
||||
lo: 55,
|
||||
hi: 100
|
||||
}],
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var servurl = document.location.host;
|
||||
if (servurl.length < 5) servurl = MYCORS;
|
||||
var connection = new WebSocket('ws://' + servurl + '/ws', ['arduino']);
|
||||
|
||||
connection.onopen = function() {
|
||||
//connection.send('get_something');
|
||||
document.getElementById("sid").className = "son";
|
||||
document.getElementById('sid').innerHTML = "Smart Switch";
|
||||
console.log("connection opened");
|
||||
};
|
||||
|
||||
connection.onclose = function() {
|
||||
document.getElementById("sid").className = "blinking";
|
||||
document.getElementById('sid').innerHTML = "Detached";
|
||||
document.getElementById("N").value = "WSon";
|
||||
console.log("connection closed");
|
||||
};
|
||||
|
||||
connection.onerror = function(error) {
|
||||
document.getElementById("sid").className = "soff";
|
||||
document.getElementById('sid').innerHTML = "Detached";
|
||||
document.getElementById("N").value = "WSon";
|
||||
console.log('WebSocket Error ', error);
|
||||
};
|
||||
|
||||
connection.onmessage = function(evt) {
|
||||
// handle websocket message. update attributes or values of elements that match the name on incoming message
|
||||
console.log("msg rec", evt.data);
|
||||
var msgArray = evt.data.split(","); // split message by delimiter into a string array
|
||||
console.log("msgArray", msgArray[0]);
|
||||
console.log("msgArray", msgArray[1]);
|
||||
console.log("msgArray", msgArray[2]);
|
||||
console.log("msgArray", msgArray[3]);
|
||||
console.log("msgArray", msgArray[4]);
|
||||
var indicator = msgArray[1]; // the first element in the message array is the ID of the object to update
|
||||
console.log("indiactor", indicator);
|
||||
var a = document.getElementById('cbStyle');
|
||||
if (indicator) // if an object by the name of the message exists, update its value or its attributes
|
||||
{
|
||||
switch (msgArray[1]) {
|
||||
case "Arduino":
|
||||
console.log("Got Temp / Humidity");
|
||||
g1.refresh(msgArray[2], null);
|
||||
g2.refresh(msgArray[3], null);
|
||||
if (msgArray[4] == "1") document.getElementById("T").style.backgroundColor = "#32a852";
|
||||
else document.getElementById("T").style.backgroundColor = "#2196f3";
|
||||
var delta = (parseFloat(document.getElementById('input-temperature').value).toFixed(1) - parseFloat(msgArray[2]).toFixed(1));
|
||||
if (delta > 0.5) document.getElementById("W").style.backgroundColor = "red";
|
||||
else if (delta < -0.5) document.getElementById("W").style.backgroundColor = "#2196f3";
|
||||
break;
|
||||
case "Clock":
|
||||
var dn = parseInt(msgArray[3]);
|
||||
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
var dayName = days[dn] || '*'; // 0...6
|
||||
var shedtype = 0;
|
||||
if (dn == 0) shedtype = 3;
|
||||
else if (dn == 6) shedtype = 2;
|
||||
else if ((dn > 0) && (dn < 6)) shedtype = 1;
|
||||
document.getElementById('C').innerHTML = msgArray[2] + ' ' + dayName;
|
||||
if (auto) document.getElementById('LZ' + shedtype).classList.add('son');
|
||||
else document.getElementById('LZ' + shedtype).classList.remove('son');
|
||||
break;
|
||||
case "Setting":
|
||||
document.getElementById('input-popup-start').value = msgArray[2];
|
||||
document.getElementById('input-popup-stop').value = msgArray[3];
|
||||
document.getElementById('input-temperature').value = parseFloat(msgArray[4]).toFixed(1);
|
||||
|
||||
document.getElementById('input-popup-start').className = "";
|
||||
document.getElementById('input-popup-stop').className = "";
|
||||
document.getElementById('input-temperature').className = "";
|
||||
|
||||
tpick.attach("input-popup-start");
|
||||
tpick.attach("input-popup-stop");
|
||||
fpick.attach("input-temperature");
|
||||
warningNotification({
|
||||
message: 'Client UPD'
|
||||
});
|
||||
break;
|
||||
case "ledon":
|
||||
a.checked = true;
|
||||
break;
|
||||
case "ledoff":
|
||||
a.checked = false;
|
||||
break;
|
||||
case "remoff":
|
||||
successNotification({
|
||||
message: 'Client quit'
|
||||
});
|
||||
break;
|
||||
case "OTA":
|
||||
warningNotification({
|
||||
message: 'OTA begin'
|
||||
});
|
||||
statusWs();
|
||||
break;
|
||||
case "sched":
|
||||
document.sched.radio[msgArray[2]].checked = true;
|
||||
break;
|
||||
default:
|
||||
// unrecognized message type. do nothing
|
||||
break;
|
||||
} // switch
|
||||
} // if (indicator)
|
||||
}; // connection.onmessage
|
||||
|
||||
function buttonClick(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.send(e.id + document.getElementById("input-popup-start").value + "|" + document.getElementById("input-popup-stop").value + "|");
|
||||
successNotification({
|
||||
message: 'Timer REQ'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function button2Click(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.send(e.id + parseFloat(document.getElementById("input-temperature").value).toFixed(1) + "|");
|
||||
successNotification({
|
||||
message: 'Temp. REQ'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function buttonEClick() {
|
||||
var murl = '/edit';
|
||||
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/edit'; //CORS
|
||||
window.open(murl, '_blank')
|
||||
warningNotification({
|
||||
message: 'Editor'
|
||||
});
|
||||
};
|
||||
|
||||
function checkboxClick(e) {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
if (e.checked) connection.send('L1');
|
||||
else connection.send('L0');
|
||||
}
|
||||
};
|
||||
|
||||
function ent1Click() {
|
||||
document.getElementById("input-temperature").className = "blinking";
|
||||
};
|
||||
|
||||
function ent2Click() {
|
||||
document.getElementById("input-popup-stop").className = "blinking";
|
||||
document.getElementById("input-popup-start").className = "blinking";
|
||||
};
|
||||
|
||||
function handleClick(e) {
|
||||
if (e.value == 'Z0' ) auto = true;
|
||||
else auto = false;
|
||||
document.getElementById('L' + e.value).classList.remove('son');
|
||||
if (connection.readyState === WebSocket.OPEN) connection.send(e.value + "|");
|
||||
}
|
||||
|
||||
// XMLHttpRequest /non WebSocket/ command. same as command' div' id to get response to
|
||||
function loadDoc(cmd, r) {
|
||||
var murl = '/' + cmd;
|
||||
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/' + cmd; //CORS
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
document.getElementById(cmd).innerHTML = this.responseText;
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", murl, true);
|
||||
xhttp.send();
|
||||
warningNotification({
|
||||
message: 'Cmd: ' + cmd
|
||||
});
|
||||
if (r) { //restart?
|
||||
connection.close();
|
||||
document.getElementById("N").value = "WSon";
|
||||
}
|
||||
};
|
||||
|
||||
function statusWs() {
|
||||
if (connection.readyState === WebSocket.OPEN) {
|
||||
connection.close();
|
||||
document.getElementById("N").value = "WSon";
|
||||
warningNotification({
|
||||
message: 'WS Closed'
|
||||
});
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
2
examples/SmartSwitch/data_src/js_css_src/.exclude.files
Normal file
@ -0,0 +1,2 @@
|
||||
/*.js.gz
|
||||
/.exclude.files
|
14
examples/SmartSwitch/data_src/js_css_src/ace-drafts/ace.js
Normal file
14
examples/SmartSwitch/data_src/js_css_src/ace.js
Normal file
5787
examples/SmartSwitch/data_src/js_css_src/app.js
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Inline Timepicker Example</title>
|
||||
<!-- [1 - LOAD THE LIBRARIES] -->
|
||||
<link href="tpick-light.css" rel="stylesheet">
|
||||
<script src="tpick.js"></script>
|
||||
<style>
|
||||
html, body {font-family: arial;}
|
||||
.myrow{display:block;float:left}
|
||||
#pick-inline-on{width:45%}#t0{width:10%}#pick-inline-off{width:45%}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Inline Time Picker</h1>
|
||||
<!-- [2 - DEFINE YOUR TEXT INPUT + CONTAINER] -->
|
||||
<div id="pick-inline-on" class="myrow"><input type="text" id="input-inline-on"/> TIME ON</div>
|
||||
<div id="t0" class="myrow"></div>
|
||||
<div id="pick-inline-off" class="myrow"><input type="text" id="input-inline-off"/> TIME OFF</div>
|
||||
|
||||
<!-- [3 - ATTACH DATE PICKER ON LOAD] -->
|
||||
<script>
|
||||
window.addEventListener("load", function(){
|
||||
// container + target input
|
||||
tpick.attach("pick-inline-on", "input-inline-on");
|
||||
tpick.attach("pick-inline-off", "input-inline-off");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Popup Timeicker Example</title>
|
||||
<!-- [1 - LOAD LIBRARIES] -->
|
||||
<link href="tpick-light-pop.css" rel="stylesheet">
|
||||
<script src="tpick-pop.js"></script>
|
||||
<style>
|
||||
html, body { font-family: arial; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Popup Time Picker</h1>
|
||||
|
||||
<!-- [2 - DEFINE YOUR TEXT INPUT + CONTAINER] -->
|
||||
<input type="text" id="input-popup-start"/> ON/OFF <input type="text" id="input-popup-stop"/> <button id="set" type="button" onclick="buttonClick(this);">SET</button>
|
||||
<p>Line</p>
|
||||
|
||||
<!-- [3 - ATTACH DATE PICKER] -->
|
||||
<script>
|
||||
window.addEventListener("load", function(){
|
||||
tpick.attach("input-popup-start");
|
||||
tpick.attach("input-popup-stop");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,29 @@
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
LICENSE
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
|
||||
Copyright 2018 by Code Boxx
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
MORE
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
Please visit https://code-boxx.com/ for more!
|
@ -0,0 +1,65 @@
|
||||
/* [CONTAINER] */
|
||||
.tpop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
transition: all 0.5s;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
.tpop.show {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
.tpicker {
|
||||
background: #f2f2f2;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
white-space: nowrap;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
.tpop .tpicker {
|
||||
margin: 20px auto 0 auto;
|
||||
}
|
||||
|
||||
/* [HR + MIN + AM/PM] */
|
||||
.tpicker-h, .tpicker-m, .tpicker-ap {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
}
|
||||
.tpicker input[type=text] {
|
||||
box-sizing: border-box;
|
||||
width: 70%;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
.tpicker-up, .tpicker-down {
|
||||
text-align: center;
|
||||
color: #ff853f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* [CANCEL + OK BUTTON] */
|
||||
.tpicker-btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.tpicker-btn input[type=button] {
|
||||
width: 50%;
|
||||
padding: 10px 0;
|
||||
border: 0;
|
||||
background: #a83a0b;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* [CONTAINER] */
|
||||
.tpicker {
|
||||
background: #f2f2f2;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
white-space: nowrap;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* [HR + MIN + AM/PM] */
|
||||
.tpicker-h, .tpicker-m, .tpicker-ap {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
}
|
||||
.tpicker input[type=text] {
|
||||
box-sizing: border-box;
|
||||
width: 70%;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
.tpicker-up, .tpicker-down {
|
||||
text-align: center;
|
||||
color: #ff853f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* [CANCEL + OK BUTTON] */
|
||||
.tpicker-btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.tpicker-btn input[type=button] {
|
||||
width: 50%;
|
||||
padding: 10px 0;
|
||||
border: 0;
|
||||
background: #a83a0b;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
var tpick = {
|
||||
attach : function (target) {
|
||||
// attach() : attach time picker to target
|
||||
|
||||
// Generate a unique random ID for the time picker
|
||||
var uniqueID = 0;
|
||||
while (document.getElementById("tpick-" + uniqueID) != null) {
|
||||
uniqueID = Math.floor(Math.random() * (100 - 2)) + 1;
|
||||
}
|
||||
|
||||
// Create wrapper
|
||||
var tw = document.createElement("div");
|
||||
tw.id = "tpick-" + uniqueID;
|
||||
tw.classList.add("tpop");
|
||||
tw.dataset.target = target;
|
||||
tw.addEventListener("click", function (evt) {
|
||||
if (evt.target.classList.contains("tpop")) {
|
||||
this.classList.remove("show");
|
||||
}
|
||||
});
|
||||
|
||||
// Create new time picker
|
||||
var tp = document.createElement("div");
|
||||
tp.classList.add("tpicker");
|
||||
|
||||
// Create hour picker
|
||||
tp.appendChild(this.draw("h"));
|
||||
tp.appendChild(this.draw("m"));
|
||||
tp.appendChild(this.draw("ap"));
|
||||
|
||||
// OK button
|
||||
var bottom = document.createElement("div"),
|
||||
ok = document.createElement("input");
|
||||
ok.setAttribute("type", "button");
|
||||
ok.value = "OK";
|
||||
ok.addEventListener("click", function(){ tpick.set(this); });
|
||||
bottom.classList.add("tpicker-btn");
|
||||
bottom.appendChild(ok);
|
||||
tp.appendChild(bottom);
|
||||
|
||||
// Attach time picker to body
|
||||
tw.appendChild(tp);
|
||||
document.body.appendChild(tw);
|
||||
|
||||
// Attach on focus event
|
||||
var target = document.getElementById(target);
|
||||
target.dataset.dp = uniqueID;
|
||||
target.onfocus = function () {
|
||||
document.getElementById("tpick-" + this.dataset.dp).classList.add("show");
|
||||
};
|
||||
},
|
||||
|
||||
draw : function (type) {
|
||||
// draw() : support function to create the hr, min, am/pm selector
|
||||
|
||||
// Create the controls
|
||||
var docket = document.createElement("div"),
|
||||
up = document.createElement("div"),
|
||||
down = document.createElement("div"),
|
||||
text = document.createElement("input");
|
||||
docket.classList.add("tpicker-" + type);
|
||||
up.classList.add("tpicker-up");
|
||||
down.classList.add("tpicker-down");
|
||||
up.innerHTML = "︿";
|
||||
down.innerHTML = "﹀";
|
||||
text.readOnly = true;
|
||||
text.setAttribute("type", "text");
|
||||
|
||||
// Default values + click event
|
||||
// You can do your own modifications here
|
||||
if (type=="h") {
|
||||
text.value = "12";
|
||||
up.addEventListener("click", function(){ tpick.spin("h", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("h", 0, this); });
|
||||
} else if (type=="m") {
|
||||
text.value = "10";
|
||||
up.addEventListener("click", function(){ tpick.spin("m", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("m", 0, this); });
|
||||
} else {
|
||||
text.value = "AM";
|
||||
up.addEventListener("click", function(){ tpick.spin("ap", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("ap", 0, this); });
|
||||
}
|
||||
|
||||
// Complete + return the docket
|
||||
docket.appendChild(up);
|
||||
docket.appendChild(text);
|
||||
docket.appendChild(down);
|
||||
return docket;
|
||||
},
|
||||
|
||||
spin : function (type, direction, el) {
|
||||
// spin() : when the up/down button is pressed
|
||||
|
||||
// Get current field + value
|
||||
var parent = el.parentElement,
|
||||
field = parent.getElementsByTagName("input")[0],
|
||||
value = field.value;
|
||||
|
||||
// Spin it
|
||||
if (type=="h") {
|
||||
value = parseInt(value);
|
||||
if (direction) { value++; } else { value--; }
|
||||
if (value==0) { value = 12; }
|
||||
else if (value>12) { value = 1; }
|
||||
} else if (type=="m") {
|
||||
value = parseInt(value);
|
||||
if (direction) { value+=5; } else { value-=5; }
|
||||
if (value<0) { value = 55; }
|
||||
else if (value>60) { value = 0; }
|
||||
if (value<10) { value = "0" + value; }
|
||||
}
|
||||
else {
|
||||
value = value=="PM" ? "AM" : "PM";
|
||||
}
|
||||
field.value = value;
|
||||
},
|
||||
|
||||
set : function (el) {
|
||||
// set() : set the selected time on the target
|
||||
|
||||
// Get the parent container
|
||||
var parent = el.parentElement;
|
||||
while (parent.classList.contains("tpop") == false) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
|
||||
// Formulate + set selected time
|
||||
var input = parent.querySelectorAll("input[type=text]");
|
||||
var time = input[0].value + ":" + input[1].value + " " + input[2].value;
|
||||
document.getElementById(parent.dataset.target).value = time;
|
||||
|
||||
// Close popup
|
||||
parent.classList.remove("show");
|
||||
}
|
||||
};
|
116
examples/SmartSwitch/data_src/js_css_src/js-time-picker/tpick.js
Normal file
@ -0,0 +1,116 @@
|
||||
var tpick = {
|
||||
attach : function (container, target) {
|
||||
// attach() : attach time picker to target
|
||||
|
||||
// Generate a unique random ID for the time picker
|
||||
var uniqueID = 0;
|
||||
while (document.getElementById("tpick-" + uniqueID) != null) {
|
||||
uniqueID = Math.floor(Math.random() * (100 - 2)) + 1;
|
||||
}
|
||||
|
||||
// Create new time picker
|
||||
var tp = document.createElement("div");
|
||||
tp.id = "tpick-" + uniqueID;
|
||||
tp.dataset.target = target;
|
||||
tp.classList.add("tpicker");
|
||||
|
||||
// Create hour picker
|
||||
tp.appendChild(this.draw("h"));
|
||||
tp.appendChild(this.draw("m"));
|
||||
tp.appendChild(this.draw("ap"));
|
||||
|
||||
// OK button
|
||||
var bottom = document.createElement("div"),
|
||||
ok = document.createElement("input");
|
||||
ok.setAttribute("type", "button");
|
||||
ok.value = "OK";
|
||||
ok.addEventListener("click", function(){ tpick.set(this); });
|
||||
bottom.classList.add("tpicker-btn");
|
||||
bottom.appendChild(ok);
|
||||
tp.appendChild(bottom);
|
||||
|
||||
// Attach time picker to target container
|
||||
document.getElementById(container).appendChild(tp);
|
||||
},
|
||||
|
||||
draw : function (type) {
|
||||
// draw() : support function to create the hr, min, am/pm selector
|
||||
|
||||
// Create the controls
|
||||
var docket = document.createElement("div"),
|
||||
up = document.createElement("div"),
|
||||
down = document.createElement("div"),
|
||||
text = document.createElement("input");
|
||||
docket.classList.add("tpicker-" + type);
|
||||
up.classList.add("tpicker-up");
|
||||
down.classList.add("tpicker-down");
|
||||
up.innerHTML = "︿";
|
||||
down.innerHTML = "﹀";
|
||||
text.readOnly = true;
|
||||
text.setAttribute("type", "text");
|
||||
|
||||
// Default values + click event
|
||||
// You can do your own modifications here
|
||||
if (type=="h") {
|
||||
text.value = "12";
|
||||
up.addEventListener("click", function(){ tpick.spin("h", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("h", 0, this); });
|
||||
} else if (type=="m") {
|
||||
text.value = "10";
|
||||
up.addEventListener("click", function(){ tpick.spin("m", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("m", 0, this); });
|
||||
} else {
|
||||
text.value = "AM";
|
||||
up.addEventListener("click", function(){ tpick.spin("ap", 1, this); });
|
||||
down.addEventListener("click", function(){ tpick.spin("ap", 0, this); });
|
||||
}
|
||||
|
||||
// Complete + return the docket
|
||||
docket.appendChild(up);
|
||||
docket.appendChild(text);
|
||||
docket.appendChild(down);
|
||||
return docket;
|
||||
},
|
||||
|
||||
spin : function (type, direction, el) {
|
||||
// spin() : when the up/down button is pressed
|
||||
|
||||
// Get current field + value
|
||||
var parent = el.parentElement,
|
||||
field = parent.getElementsByTagName("input")[0],
|
||||
value = field.value;
|
||||
|
||||
// Spin it
|
||||
if (type=="h") {
|
||||
value = parseInt(value);
|
||||
if (direction) { value++; } else { value--; }
|
||||
if (value==0) { value = 12; }
|
||||
else if (value>12) { value = 1; }
|
||||
} else if (type=="m") {
|
||||
value = parseInt(value);
|
||||
if (direction) { value+=5; } else { value-=5; }
|
||||
if (value<0) { value = 55; }
|
||||
else if (value>60) { value = 0; }
|
||||
if (value<10) { value = "0" + value; }
|
||||
}
|
||||
else {
|
||||
value = value=="PM" ? "AM" : "PM";
|
||||
}
|
||||
field.value = value;
|
||||
},
|
||||
|
||||
set : function (el) {
|
||||
// set() : set the selected time on the target
|
||||
|
||||
// Get the parent container
|
||||
var parent = el.parentElement;
|
||||
while (parent.classList.contains("tpicker") == false) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
|
||||
// Formulate + set selected time
|
||||
var input = parent.querySelectorAll("input[type=text]");
|
||||
var time = input[0].value + ":" + input[1].value + " " + input[2].value;
|
||||
document.getElementById(parent.dataset.target).value = time;
|
||||
}
|
||||
};
|
@ -0,0 +1,94 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Auto-adjust</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#g1 {
|
||||
width:400px; height:320px;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#g2, #g3, #g4 {
|
||||
width:100px; height:80px;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
width: 450px;
|
||||
margin: 2em auto;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="g1"></div>
|
||||
<div id="g2"></div>
|
||||
<div id="g3"></div>
|
||||
<div id="g4"></div>
|
||||
<p>
|
||||
JustGage auto-adjusts to the size of containing element. And to the screen zoom level. And screen density. Nice. This means you’ll get clean, sharp and nice looking gauge at all times. Try zooming the page to see the results.
|
||||
</p>
|
||||
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
var g1, g2, g3, g4;
|
||||
|
||||
window.onload = function(){
|
||||
var g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Big Fella",
|
||||
label: "pounds"
|
||||
});
|
||||
|
||||
var g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Small Buddy",
|
||||
label: "oz"
|
||||
});
|
||||
|
||||
var g3 = new JustGage({
|
||||
id: "g3",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Tiny Lad",
|
||||
label: "oz"
|
||||
});
|
||||
|
||||
var g4 = new JustGage({
|
||||
id: "g4",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Little Pal",
|
||||
label: "oz"
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
g1.refresh(getRandomInt(50, 100));
|
||||
g2.refresh(getRandomInt(50, 100));
|
||||
g3.refresh(getRandomInt(0, 50));
|
||||
g4.refresh(getRandomInt(0, 50));
|
||||
}, 2500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,58 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Counter</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<a href="#" id="g1_refresh">Random Refresh</a>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
var g1;
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: 72,
|
||||
min: 0,
|
||||
max: 100,
|
||||
donut: true,
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true,
|
||||
hideInnerShadow: true
|
||||
});
|
||||
|
||||
document.getElementById('g1_refresh').addEventListener('click', function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,82 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Custom interval</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#g1,
|
||||
#g2,
|
||||
#g3 {
|
||||
width: 200px;
|
||||
height: 160px;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
width: 450px;
|
||||
margin: 2em auto;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="g1"></div>
|
||||
<div id="g2"></div>
|
||||
<div id="g3"></div>
|
||||
<p>
|
||||
You need to measure, say, between 350 and 980? No problem, just tell it to justGage. Displayed value and color are calculated as a percentage in defined range, with optional min and max labels shown.
|
||||
</p>
|
||||
<p>
|
||||
Also, if displayed value is out of range, relax and kick your feet up - justGage will take care of it for you.
|
||||
</p>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
var g1, g2, g3;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: getRandomInt(350, 980),
|
||||
min: 350,
|
||||
max: 980,
|
||||
title: "Lone Ranger",
|
||||
label: "miles traveled"
|
||||
});
|
||||
|
||||
g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: 32,
|
||||
min: 50,
|
||||
max: 100,
|
||||
title: "Empty Tank",
|
||||
label: ""
|
||||
});
|
||||
|
||||
g3 = new JustGage({
|
||||
id: "g3",
|
||||
value: 120,
|
||||
min: 50,
|
||||
max: 100,
|
||||
title: "Meltdown",
|
||||
label: ""
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
g1.refresh(getRandomInt(350, 980));
|
||||
g2.refresh(getRandomInt(0, 49));
|
||||
g3.refresh(getRandomInt(101, 200));
|
||||
}, 2500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,130 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Custom Node</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
button {
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 850px;
|
||||
margin: 20px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 20px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cont" class="container">
|
||||
<div id="gauge2" class="gauge"></div>
|
||||
<div id="gauge3" class="gauge" data-title="#3" data-value="75" data-counter="true"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<button type="button" id="g1_show">Show G1</button>
|
||||
<button type="button" id="g4_show">Show G4</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<button type="button" id="g1_refresh">Refresh G1</button>
|
||||
<button type="button" id="g2_refresh">Refresh G2</button>
|
||||
<button type="button" id="g3_refresh">Refresh G3</button>
|
||||
<button type="button" id="g4_refresh">Refresh G4</button>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gNode1 = document.createElement('div');
|
||||
gNode1.setAttribute("class", "gauge");
|
||||
|
||||
var gNode4 = document.createElement('div');
|
||||
gNode4.setAttribute("class", "gauge");
|
||||
gNode4.setAttribute("data-title", "#4");
|
||||
gNode4.setAttribute("data-value", "100");
|
||||
gNode4.setAttribute("data-counter", "true");
|
||||
|
||||
var gauge1 = new JustGage({
|
||||
parentNode: gNode1,
|
||||
width: 150,
|
||||
height: 150,
|
||||
title: "#1",
|
||||
value: 25,
|
||||
min: 0,
|
||||
max: 100,
|
||||
decimals: 0,
|
||||
counter: true
|
||||
});
|
||||
|
||||
var gauge2 = new JustGage({
|
||||
id: "gauge2",
|
||||
title: "#2",
|
||||
value: 50,
|
||||
min: 0,
|
||||
max: 100,
|
||||
humanFriendly: false,
|
||||
decimals: 0,
|
||||
counter: true
|
||||
});
|
||||
|
||||
var gauge3 = new JustGage({
|
||||
id: "gauge3"
|
||||
});
|
||||
|
||||
var gauge4 = new JustGage({
|
||||
parentNode: gNode4,
|
||||
width: 150,
|
||||
height: 150
|
||||
});
|
||||
|
||||
document.getElementById('g1_refresh').addEventListener('click', function() {
|
||||
gauge1.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('g2_refresh').addEventListener('click', function() {
|
||||
gauge2.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('g3_refresh').addEventListener('click', function() {
|
||||
gauge3.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('g4_refresh').addEventListener('click', function() {
|
||||
gauge4.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('g1_show').addEventListener('click', function() {
|
||||
var container = document.getElementById("cont");
|
||||
container.insertBefore(gNode1, container.childNodes[0]);
|
||||
container = null;
|
||||
});
|
||||
|
||||
document.getElementById('g4_show').addEventListener('click', function() {
|
||||
var container = document.getElementById("cont");
|
||||
container.appendChild(gNode4, container.childNodes[0]);
|
||||
container = null;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,88 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Custom Sectors</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<style>
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div id="gg1" class="gauge"></div>
|
||||
<p id="gg1_text">0-50 is green, 51-100 is red</p>
|
||||
<a href="#" id="gg1_refresh" class="button grey">Random Refresh</a>
|
||||
<a href="#" id="gg1_update" class="button grey">Update Sectors</a>
|
||||
</div>
|
||||
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gg1 = new JustGage({
|
||||
id: "gg1",
|
||||
value : 72.15,
|
||||
min: 0,
|
||||
max: 100,
|
||||
decimals: 2,
|
||||
gaugeWidthScale: 0.6,
|
||||
customSectors: [{
|
||||
color : "#00ff00",
|
||||
lo : 0,
|
||||
hi : 50
|
||||
},{
|
||||
color : "#ff0000",
|
||||
lo : 50,
|
||||
hi : 100
|
||||
}],
|
||||
counter: true
|
||||
});
|
||||
|
||||
document.getElementById('gg1_refresh').addEventListener('click', function() {
|
||||
gg1.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('gg1_update').addEventListener('click', function() {
|
||||
gg1.update({
|
||||
value: getRandomInt(0, 100),
|
||||
customSectors: [{
|
||||
color : "#00ff00",
|
||||
lo : 0,
|
||||
hi : 25
|
||||
},{
|
||||
color : "#ff0000",
|
||||
lo : 25,
|
||||
hi : 100
|
||||
}]
|
||||
});
|
||||
document.getElementById('gg1_text').innerHTML = "<b>UPDATE</b>: 0-25 is green, 26-100 is red"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,116 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Custom Render Function</title>
|
||||
<style>
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge_container {
|
||||
text-align: left;
|
||||
height: 450px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
margin: 40px 5px 0 5px;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.controls {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 10px 0 0 0;
|
||||
}
|
||||
|
||||
li.refresh {
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: Arial;
|
||||
display: inline-block;
|
||||
width: 65px;
|
||||
margin: 0 0 5px 0;
|
||||
text-align: right;
|
||||
padding: 5px;
|
||||
color: #919191;
|
||||
}
|
||||
|
||||
input {
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
input[disabled=disabled] {
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
padding: 0 0 0 10px;
|
||||
margin: 0px;
|
||||
color: #777777;
|
||||
border-color: transparent;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 0px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="gg1" style="width: 200px; height: 150px;"></div>
|
||||
<a href="#" id="gg1_refresh" class="button grey">Random Refresh</a>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gg1 = new JustGage({
|
||||
id: "gg1",
|
||||
value: 50,
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Target",
|
||||
label: "temperature",
|
||||
textRenderer: customValue
|
||||
});
|
||||
|
||||
document.getElementById('gg1_refresh').addEventListener('click', function() {
|
||||
gg1.refresh(getRandomInt(0, 100));
|
||||
return false;
|
||||
});
|
||||
|
||||
function customValue(val) {
|
||||
if (val < 50) {
|
||||
return 'low';
|
||||
} else if (val > 50) {
|
||||
return 'high';
|
||||
} else if (val === 50) {
|
||||
return 'ideal';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,137 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Customize style</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#g1, #g2, #g3, #g4, #g5, #g6 {
|
||||
width:200px; height:160px;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
width: 450px;
|
||||
margin: 2em auto;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="g1"></div>
|
||||
<div id="g2"></div>
|
||||
<div id="g3"></div>
|
||||
<div id="g4"></div>
|
||||
<div id="g5"></div>
|
||||
<div id="g6"></div>
|
||||
<p>
|
||||
Not digging default style? Then mock your own, Picasso! JustGage features bunch of styling options including gauge width, gauge color and shadow, gauge level colors, colors for title, value, min & max etc.
|
||||
</p>
|
||||
<p>
|
||||
Check non-minified version of justgage.js for list of all setup parameters.
|
||||
</p>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<!--<script src="../justgage-1.0.1.js"></script>-->
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var g1, g2, g3, g4, g5, g6;
|
||||
|
||||
var g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Custom Width",
|
||||
label: "",
|
||||
gaugeWidthScale: 0.2
|
||||
});
|
||||
|
||||
var g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Custom Shadow",
|
||||
label: "",
|
||||
shadowOpacity: 1,
|
||||
shadowSize: 5,
|
||||
shadowVerticalOffset: 10
|
||||
});
|
||||
|
||||
var g3 = new JustGage({
|
||||
id: "g3",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Custom Colors",
|
||||
label: "",
|
||||
levelColors: [
|
||||
"#00fff6",
|
||||
"#ff00fc",
|
||||
"#1200ff"
|
||||
]
|
||||
});
|
||||
|
||||
var g4 = new JustGage({
|
||||
id: "g4",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Hide Labels",
|
||||
hideMinMax: true
|
||||
});
|
||||
|
||||
|
||||
var g5 = new JustGage({
|
||||
id: "g5",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Animation Type",
|
||||
label: "",
|
||||
startAnimationTime: 2000,
|
||||
startAnimationType: ">",
|
||||
refreshAnimationTime: 1000,
|
||||
refreshAnimationType: "bounce"
|
||||
});
|
||||
|
||||
var g6 = new JustGage({
|
||||
id: "g6",
|
||||
value: getRandomInt(0, 100),
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: "Minimal",
|
||||
label: "",
|
||||
hideMinMax: true,
|
||||
gaugeColor: "#fff",
|
||||
levelColors: ["#000"],
|
||||
hideInnerShadow: true,
|
||||
startAnimationTime: 1,
|
||||
startAnimationType: "linear",
|
||||
refreshAnimationTime: 1,
|
||||
refreshAnimationType: "linear"
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
g2.refresh(getRandomInt(0, 100));
|
||||
g3.refresh(getRandomInt(0, 100));
|
||||
g4.refresh(getRandomInt(0, 100));
|
||||
g5.refresh(getRandomInt(0, 100));
|
||||
g6.refresh(getRandomInt(0, 100));
|
||||
}, 2500);
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,60 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Defaults</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.container {
|
||||
width: 600px;
|
||||
margin: 100px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="gg1" class="gauge"></div>
|
||||
<div id="gg2" class="gauge" data-value="25"></div>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var dflt = {
|
||||
min: 0,
|
||||
max: 200,
|
||||
donut: true,
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true,
|
||||
hideInnerShadow: true
|
||||
}
|
||||
|
||||
var gg1 = new JustGage({
|
||||
id: 'gg1',
|
||||
value: 125,
|
||||
title: 'javascript call',
|
||||
defaults: dflt
|
||||
});
|
||||
|
||||
var gg2 = new JustGage({
|
||||
id: 'gg2',
|
||||
title: 'data-attributes',
|
||||
defaults: dflt
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,62 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Counter</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<a href="#" id="g1_refresh">Random Refresh</a>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
var g1;
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
g1 = new JustGage({
|
||||
id: "g1",
|
||||
title: "Font Options",
|
||||
value: 72,
|
||||
min: 0,
|
||||
max: 100,
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true,
|
||||
titleFontColor: "red",
|
||||
titleFontFamily: "Georgia",
|
||||
titlePosition: "below",
|
||||
valueFontColor: "blue",
|
||||
valueFontFamily: "Georgia"
|
||||
});
|
||||
|
||||
document.getElementById('g1_refresh').addEventListener('click', function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,56 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Counter</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="gg1" class="gauge"></div>
|
||||
<a href="#" id="gg1_refresh">Random Refresh</a>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gg1 = new JustGage({
|
||||
id: "gg1",
|
||||
value: 40960,
|
||||
min: 1024,
|
||||
max: 1000000,
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true,
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
document.getElementById('gg1_refresh').addEventListener('click', function() {
|
||||
gg1.refresh(getRandomInt(1024, 1000000));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>html5 data-attribute setup</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<style>
|
||||
.container {
|
||||
width: 320px;
|
||||
margin: 30px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div id="gg1" class="gauge" data-value="1200" data-min="0" data-max="1000000" data-gaugeWidthScale="0.6"></div>
|
||||
<input type="button" id="gg1_refresh" class="btn" value="Random Refresh" />
|
||||
</div>
|
||||
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gg1 = new JustGage({
|
||||
id: "gg1",
|
||||
formatNumber: true,
|
||||
counter: true
|
||||
});
|
||||
|
||||
document.getElementById('gg1_refresh').addEventListener('click', function() {
|
||||
gg1.refresh(getRandomInt(0, 1000000));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,109 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Guage</title>
|
||||
<style>
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge_container {
|
||||
text-align: left;
|
||||
height: 450px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
margin: 40px 5px 0 5px;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.controls {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 10px 0 0 0;
|
||||
}
|
||||
|
||||
li.refresh {
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: Arial;
|
||||
display: inline-block;
|
||||
width: 65px;
|
||||
margin: 0 0 5px 0;
|
||||
text-align: right;
|
||||
padding: 5px;
|
||||
color: #919191;
|
||||
}
|
||||
|
||||
input {
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
input[disabled=disabled] {
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
padding: 0 0 0 10px;
|
||||
margin: 0px;
|
||||
color: #777777;
|
||||
border-color: transparent;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 0px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="gg1" style="width: 200px; height: 150px;"></div>
|
||||
<a href="#" id="gg1_refresh" class="button grey">Random Refresh</a>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var gg1 = new JustGage({
|
||||
id: "gg1",
|
||||
value: 10.1,
|
||||
min: -40,
|
||||
max: 50,
|
||||
title: "Target",
|
||||
label: "",
|
||||
humanFriendly: true,
|
||||
startAnimationTime: 10000,
|
||||
refreshAnimationTime: 10000
|
||||
});
|
||||
|
||||
document.getElementById('gg1_refresh').addEventListener('click', function() {
|
||||
gg1.refresh(getRandomInt(0, 1000000));
|
||||
console.log(gg1);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,168 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Pointer</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.wrapper {
|
||||
position: relative;
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
margin: 50px auto 0 auto;
|
||||
padding-bottom: 30px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.box {
|
||||
float: left;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 320px;
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 16px 40px;
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
background: #34aadc;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="box">
|
||||
<div id="g1" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g2" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g3" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g4" class="gauge"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<button type="button" id="gauge_refresh">Refresh Gauges</button>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var g1 = new JustGage({
|
||||
id: 'g1',
|
||||
value: 65,
|
||||
min: 0,
|
||||
max: 100,
|
||||
symbol: '%',
|
||||
pointer: true,
|
||||
gaugeWidthScale: 0.6,
|
||||
customSectors: [{
|
||||
color: '#ff0000',
|
||||
lo: 50,
|
||||
hi: 100
|
||||
}, {
|
||||
color: '#00ff00',
|
||||
lo: 0,
|
||||
hi: 50
|
||||
}],
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g2 = new JustGage({
|
||||
id: 'g2',
|
||||
value: 45,
|
||||
min: 0,
|
||||
max: 100,
|
||||
symbol: '%',
|
||||
pointer: true,
|
||||
pointerOptions: {
|
||||
toplength: -15,
|
||||
bottomlength: 10,
|
||||
bottomwidth: 12,
|
||||
color: '#8e8e93',
|
||||
stroke: '#ffffff',
|
||||
stroke_width: 3,
|
||||
stroke_linecap: 'round'
|
||||
},
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g3 = new JustGage({
|
||||
id: 'g3',
|
||||
value: 40,
|
||||
min: 0,
|
||||
max: 100,
|
||||
symbol: '%',
|
||||
donut: true,
|
||||
pointer: true,
|
||||
gaugeWidthScale: 0.4,
|
||||
pointerOptions: {
|
||||
toplength: 10,
|
||||
bottomlength: 10,
|
||||
bottomwidth: 8,
|
||||
color: '#000'
|
||||
},
|
||||
customSectors: [{
|
||||
color: "#ff0000",
|
||||
lo: 50,
|
||||
hi: 100
|
||||
}, {
|
||||
color: "#00ff00",
|
||||
lo: 0,
|
||||
hi: 50
|
||||
}],
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g4 = new JustGage({
|
||||
id: 'g4',
|
||||
value: 70,
|
||||
min: 0,
|
||||
max: 100,
|
||||
symbol: '%',
|
||||
pointer: true,
|
||||
pointerOptions: {
|
||||
toplength: 8,
|
||||
bottomlength: -20,
|
||||
bottomwidth: 6,
|
||||
color: '#8e8e93'
|
||||
},
|
||||
gaugeWidthScale: 0.1,
|
||||
counter: true
|
||||
});
|
||||
|
||||
document.getElementById('gauge_refresh').addEventListener('click', function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
g2.refresh(getRandomInt(0, 100));
|
||||
g3.refresh(getRandomInt(0, 100));
|
||||
g4.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,91 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Donuts, baby!</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 50px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
a:link.button,
|
||||
a:active.button,
|
||||
a:visited.button,
|
||||
a:hover.button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 7px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<a href="#" id="g1_refresh" class="button grey">Random Refresh</a>
|
||||
<br />
|
||||
<a href="#" id="g1_setmax100" class="button grey">Set Max 100</a>
|
||||
<a href="#" id="g1_setmax200" class="button grey">Set Max 200</a>
|
||||
<a href="#" id="g1_setmax400" class="button grey">Set Max 400</a>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var g1 = new JustGage({
|
||||
id: "g1",
|
||||
title: "Max is 100.",
|
||||
value: -27.1,
|
||||
min: -40.0,
|
||||
max: 50,
|
||||
decimals: 1,
|
||||
gaugeWidthScale: 0.6,
|
||||
textRenderer: customValue
|
||||
});
|
||||
|
||||
document.getElementById('g1_refresh').addEventListener('click', function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
|
||||
document.getElementById('g1_setmax100').addEventListener('click', function() {
|
||||
g1.refresh(g1.originalValue, 100);
|
||||
g1.txtTitle.attr({
|
||||
"text": "Max is 100."
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('g1_setmax200').addEventListener('click', function() {
|
||||
g1.refresh(g1.originalValue, 200);
|
||||
g1.txtTitle.attr({
|
||||
"text": "Whoops, max jumped to 200."
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
document.getElementById('g1_setmax400').addEventListener('click', function() {
|
||||
g1.refresh(g1.originalValue, 400);
|
||||
g1.txtTitle.attr({
|
||||
"text": "Blimey, max blasted to 400!"
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
function customValue(val) {
|
||||
return val + "°C";
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,71 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Dynamic Resize</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style>
|
||||
body{text-align:center;padding:0;margin:0}
|
||||
.clear:after,.clear:before{content:"";display:table}
|
||||
.clear:after{clear:both}
|
||||
.wrapper{position:absolute;top:0;right:0;bottom:0;left:0;margin:30px;border:1px solid #ccc}
|
||||
.gauge{display:block;float:left}
|
||||
#g1{width:33%}#g2{width:33%}#g3{width:33%}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper clear">
|
||||
<div id="g1" class="gauge"></div>
|
||||
<div id="g2" class="gauge"></div>
|
||||
<div id="g3" class="gauge"></div>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var g1, g2, g3;
|
||||
|
||||
var g1 = new JustGage({
|
||||
id: "g1",
|
||||
value: -5.5,
|
||||
min: -40.1,
|
||||
max: 50.1,
|
||||
title: "Temperature",
|
||||
titlePosition: "below",
|
||||
label: "°C",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
var g2 = new JustGage({
|
||||
id: "g2",
|
||||
value: 40.8,
|
||||
min: 0,
|
||||
max: 99.9,
|
||||
title: "Humidity",
|
||||
titlePosition: "below",
|
||||
label: "%",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
formatNumber: true
|
||||
});
|
||||
|
||||
var g3 = new JustGage({
|
||||
id: "g3",
|
||||
value: 980.7,
|
||||
min: 800.1,
|
||||
max: 1300.1,
|
||||
title: "Pressure",
|
||||
titlePosition: "below",
|
||||
label: "hPa",
|
||||
relativeGaugeSize: true,
|
||||
pointer: true,
|
||||
formatNumber: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,144 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Reverse</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
.wrapper {
|
||||
position: relative;
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
margin: 50px auto 0 auto;
|
||||
padding-bottom: 30px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.box {
|
||||
float: left;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge {
|
||||
width: 320px;
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 30px 5px 0 2px;
|
||||
padding: 16px 40px;
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
background: #34aadc;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="box">
|
||||
<div id="g1" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g2" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g3" class="gauge"></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div id="g4" class="gauge"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<button type="button" id="gauge_refresh">Refresh Gauges</button>
|
||||
</div>
|
||||
<script src="../raphael-2.1.4.min.js"></script>
|
||||
<script src="../justgage.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
|
||||
var g1 = new JustGage({
|
||||
id: 'g1',
|
||||
value: 65,
|
||||
min: 0,
|
||||
max: 100,
|
||||
reverse: true,
|
||||
gaugeWidthScale: 0.6,
|
||||
customSectors: [{
|
||||
color: '#ff0000',
|
||||
lo: 50,
|
||||
hi: 100
|
||||
}, {
|
||||
color: '#00ff00',
|
||||
lo: 0,
|
||||
hi: 50
|
||||
}],
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g2 = new JustGage({
|
||||
id: 'g2',
|
||||
value: 45,
|
||||
min: 0,
|
||||
max: 500,
|
||||
reverse: true,
|
||||
gaugeWidthScale: 0.6,
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g3 = new JustGage({
|
||||
id: 'g3',
|
||||
value: 25000,
|
||||
min: 0,
|
||||
max: 100000,
|
||||
humanFriendly : true,
|
||||
reverse: true,
|
||||
gaugeWidthScale: 1.3,
|
||||
customSectors: [{
|
||||
color: "#ff0000",
|
||||
lo: 50000,
|
||||
hi: 100000
|
||||
}, {
|
||||
color: "#00ff00",
|
||||
lo: 0,
|
||||
hi: 50000
|
||||
}],
|
||||
counter: true
|
||||
});
|
||||
|
||||
var g4 = new JustGage({
|
||||
id: 'g4',
|
||||
value: 90,
|
||||
min: 0,
|
||||
max: 100,
|
||||
symbol: '%',
|
||||
reverse: true,
|
||||
gaugeWidthScale: 0.1,
|
||||
counter: true
|
||||
});
|
||||
|
||||
document.getElementById('gauge_refresh').addEventListener('click', function() {
|
||||
g1.refresh(getRandomInt(0, 100));
|
||||
g2.refresh(getRandomInt(0, 100));
|
||||
g3.refresh(getRandomInt(0, 100000));
|
||||
g4.refresh(getRandomInt(0, 100));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
1220
examples/SmartSwitch/data_src/js_css_src/justgage-1.2.2/justgage.js
Normal file
12
examples/SmartSwitch/data_src/js_css_src/justgage-1.2.2/raphael-2.1.4.min.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["es2015"]
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"windows"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-console": 0,
|
||||
"no-undef": 0
|
||||
}
|
||||
};
|
30
examples/SmartSwitch/data_src/js_css_src/styled-notifications/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# IDE files
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
# Build directories
|
||||
build/
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Lock files
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "7"
|
@ -0,0 +1,7 @@
|
||||
# Notifications license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,104 @@
|
||||
const { partial, append, isString, createElement, createParagraph } = require('../src/helpers');
|
||||
|
||||
const addNumbers = (x, y) => x + y;
|
||||
|
||||
const sum = (...numbers) => numbers.reduce((total, current) => total + current, 0);
|
||||
|
||||
describe('Helpers', () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('Partial', () => {
|
||||
it('should return a partially applied function', () => {
|
||||
expect(typeof partial(addNumbers, 10)).toEqual('function');
|
||||
});
|
||||
|
||||
it('should execute function when partially applied function is called', () => {
|
||||
expect(partial(addNumbers, 20)(10)).toEqual(30);
|
||||
});
|
||||
|
||||
it('should gather argument', () => {
|
||||
expect(partial(sum, 5, 10)(15, 20, 25)).toEqual(75);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Append', () => {
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
const elementToAppend = document.createElement('h1');
|
||||
elementToAppend.classList.add('heading');
|
||||
elementToAppend.innerText = 'working';
|
||||
|
||||
append(container, elementToAppend);
|
||||
|
||||
const element = document.querySelector('.heading');
|
||||
expect(element);
|
||||
|
||||
expect(element.innerText).toEqual('working');
|
||||
});
|
||||
|
||||
describe('Is string', () => {
|
||||
expect(isString(1)).toEqual(false);
|
||||
expect(isString(null)).toEqual(false);
|
||||
expect(isString(undefined)).toEqual(false);
|
||||
expect(isString({})).toEqual(false);
|
||||
|
||||
expect(isString('')).toEqual(true);
|
||||
expect(isString('a')).toEqual(true);
|
||||
expect(isString('1')).toEqual(true);
|
||||
expect(isString('some string')).toEqual(true);
|
||||
});
|
||||
|
||||
describe('Create element', () => {
|
||||
it('should create an element', () => {
|
||||
expect(createElement('p')).toEqual(document.createElement('p'));
|
||||
expect(createElement('h1')).toEqual(document.createElement('h1'));
|
||||
expect(createElement('ul')).toEqual(document.createElement('ul'));
|
||||
expect(createElement('li')).toEqual(document.createElement('li'));
|
||||
expect(createElement('div')).toEqual(document.createElement('div'));
|
||||
expect(createElement('span')).toEqual(document.createElement('span'));
|
||||
});
|
||||
|
||||
it('should add class names', () => {
|
||||
expect(createElement('div', 'someclass1', 'someclass2').classList.contains('someclass2'));
|
||||
expect(createElement('p', 'para', 'test').classList.contains('para'));
|
||||
|
||||
const mockUl = document.createElement('ul');
|
||||
mockUl.classList.add('nav');
|
||||
mockUl.classList.add('foo');
|
||||
|
||||
expect(createElement('ul', 'nav', 'foo').classList).toEqual(mockUl.classList);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create paragraph', () => {
|
||||
it('should create a paragraph', () => {
|
||||
const p = document.createElement('p');
|
||||
p.innerText = 'Some text';
|
||||
expect(createParagraph()('Some text')).toEqual(p);
|
||||
});
|
||||
|
||||
it('should add class names', () => {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('body-text');
|
||||
p.classList.add('para');
|
||||
|
||||
expect(createParagraph('body-text', 'para')('')).toEqual(p);
|
||||
});
|
||||
|
||||
it('should set inner text', () => {
|
||||
const p = document.createElement('p');
|
||||
p.innerText = 'Hello world!';
|
||||
p.classList.add('text');
|
||||
|
||||
expect(createParagraph('text')('Hello world!')).toEqual(p);
|
||||
});
|
||||
|
||||
it('should append to DOM', () => {
|
||||
append(document.body, createParagraph('text')('hello'));
|
||||
expect(document.querySelector('.text').innerText).toEqual('hello');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,144 @@
|
||||
require('../src/index');
|
||||
|
||||
describe('Notifications', () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('should display a console warning if no title or message is passed', () => {
|
||||
jest.spyOn(global.console, 'warn');
|
||||
window.createNotification()();
|
||||
expect(console.warn).toBeCalled();
|
||||
});
|
||||
|
||||
it('should render a default notification', () => {
|
||||
const notification = window.createNotification();
|
||||
|
||||
const title = 'I am a title';
|
||||
|
||||
// Should initially not contain any notifications
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(0);
|
||||
|
||||
// Create a notification instance with a title
|
||||
notification({ title });
|
||||
|
||||
// Should be one notification with the title passed in
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
expect(document.querySelector('.ncf-title').innerText).toEqual(title);
|
||||
|
||||
// Create a second instance so there should now be two instances
|
||||
notification({ title });
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should close on click if the option is enabled', () => {
|
||||
const notification = window.createNotification({
|
||||
closeOnClick: true
|
||||
});
|
||||
|
||||
// Create a notification with a generic body
|
||||
notification({ message: 'some text' });
|
||||
|
||||
// Should be one notification instance
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
|
||||
// Click the notification
|
||||
document.querySelector('.ncf').click();
|
||||
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should not close on click if the option is disabled', () => {
|
||||
const notification = window.createNotification({
|
||||
closeOnClick: false
|
||||
});
|
||||
|
||||
// Create a notification with a generic body
|
||||
notification({ message: 'some text' });
|
||||
|
||||
// Should be one notification instance
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
|
||||
// Click the notification
|
||||
document.querySelector('.ncf').click();
|
||||
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set position class if valid', () => {
|
||||
const validPositions = [
|
||||
'nfc-top-left',
|
||||
'nfc-top-right',
|
||||
'nfc-bottom-left',
|
||||
'nfc-bottom-right'
|
||||
];
|
||||
|
||||
validPositions.forEach(position => {
|
||||
const notification = window.createNotification({
|
||||
positionClass: position
|
||||
});
|
||||
|
||||
notification({ title: 'title here' });
|
||||
|
||||
const className = `.${position}`;
|
||||
|
||||
expect(document.querySelectorAll(className).length).toEqual(1);
|
||||
|
||||
const container = document.querySelector(className);
|
||||
expect(container.querySelectorAll('.ncf').length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should revert to default to default position and warn if class is invalid', () => {
|
||||
const notification = window.createNotification({
|
||||
positionClass: 'invalid-name'
|
||||
});
|
||||
|
||||
jest.spyOn(global.console, 'warn');
|
||||
|
||||
notification({ message: 'test' });
|
||||
|
||||
expect(console.warn).toBeCalled();
|
||||
|
||||
expect(document.querySelectorAll('.nfc-top-right').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should allow a custom onclick callback', () => {
|
||||
let a = 'not clicked';
|
||||
|
||||
const notification = window.createNotification({
|
||||
onclick: () => {
|
||||
a = 'clicked';
|
||||
}
|
||||
});
|
||||
|
||||
notification({ message: 'click test' });
|
||||
|
||||
expect(a).toEqual('not clicked');
|
||||
|
||||
// Click the notification
|
||||
document.querySelector('.ncf').click();
|
||||
|
||||
expect(a).toEqual('clicked');
|
||||
});
|
||||
|
||||
it('should show for correct duration', () => {
|
||||
const notification = window.createNotification({
|
||||
showDuration: 500
|
||||
});
|
||||
|
||||
notification({ message: 'test' });
|
||||
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
|
||||
// Should exist after 400ms
|
||||
setTimeout(() => {
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(1);
|
||||
}, 400);
|
||||
|
||||
// Should delete after 500ms
|
||||
setTimeout(() => {
|
||||
expect(document.querySelectorAll('.ncf').length).toEqual(0);
|
||||
});
|
||||
}, 501);
|
||||
});
|
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
// Written using ES5 JS for browser support
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
var form = document.querySelector('form');
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Form elements
|
||||
var title = form.querySelector('#title').value;
|
||||
var message = form.querySelector('#message').value;
|
||||
var position = form.querySelector('#position').value;
|
||||
var duration = form.querySelector('#duration').value;
|
||||
var theme = form.querySelector('#theme').value;
|
||||
var closeOnClick = form.querySelector('#close').checked;
|
||||
var displayClose = form.querySelector('#closeButton').checked;
|
||||
|
||||
if(!message) {
|
||||
message = 'You did not enter a message...';
|
||||
}
|
||||
|
||||
window.createNotification({
|
||||
closeOnClick: closeOnClick,
|
||||
displayCloseButton: displayClose,
|
||||
positionClass: position,
|
||||
showDuration: duration,
|
||||
theme: theme
|
||||
})({
|
||||
title: title,
|
||||
message: message
|
||||
});
|
||||
});
|
||||
});
|