Compare commits

...

5 Commits

131 changed files with 1502 additions and 17825 deletions

209
README.md
View File

@ -1,20 +1,8 @@
# In this fork
- SPIFFSEditor modifications
- Added [extras](https://github.com/lorol/ESPAsyncWebServer/tree/master/extras) folder with (Win) tools for re-packing, editing, updating and compressing html to binary arrays embedded to source
- Added a [SmartSwitch](https://github.com/lorol/ESPAsyncWebServer/tree/master/examples/SmartSwitch) example to test code features
- Applied the memory optimizations from [sascha432](https://github.com/sascha432/ESPAsyncWebServer) fork
- Cookie Authentication including on Websocket part, based on [ayushsharma82](https://github.com/me-no-dev/ESPAsyncWebServer/pull/684) PR, new functions added:
- For Websocket: ```void handleHandshake(AwsHandshakeHandler handler) ```
- For EventSource: ```void authorizeConnect(ArAuthorizeConnectHandler cb)```
- [Additions to this README.md from jendakol' PR](https://github.com/me-no-dev/ESPAsyncWebServer/pull/770)
- [Respond with file content using a callback and extra headers](#respond-with-file-content-using-a-callback-and-extra-headers)
- [Serving static files by custom handling](#serving-static-files-by-custom-handling)
- Added LittleFS choice for both [esp8266](https://github.com/esp8266/Arduino/tree/master/libraries/LittleFS) / [esp32](https://github.com/lorol/LITTLEFS) , and FatFS tests for esp32 see [SmartSwitch](https://github.com/lorol/ESPAsyncWebServer/blob/master/examples/SmartSwitch/SmartSwitch.ino#L16 )
------------------------------------------------------------------------------------------
# ESPAsyncWebServer
# ESPAsyncWebServer
[![Build Status](https://travis-ci.org/me-no-dev/ESPAsyncWebServer.svg?branch=master)](https://travis-ci.org/me-no-dev/ESPAsyncWebServer) ![](https://github.com/me-no-dev/ESPAsyncWebServer/workflows/ESP%20Async%20Web%20Server%20CI/badge.svg) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/395dd42cfc674e6ca2e326af3af80ffc)](https://www.codacy.com/manual/me-no-dev/ESPAsyncWebServer?utm_source=github.com&utm_medium=referral&utm_content=me-no-dev/ESPAsyncWebServer&utm_campaign=Badge_Grade)
A fork of the [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) library by [@me-no-dev](https://github.com/me-no-dev) for [ESPHome](https://esphome.io).
For help and support [![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Async HTTP and WebSocket Server for ESP8266 Arduino
@ -67,7 +55,6 @@ To use this library you might need to have the latest git versions of [ESP32](ht
- [Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates)
- [Respond with content using a callback](#respond-with-content-using-a-callback)
- [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers)
- [Respond with file content using a callback and extra headers](#respond-with-file-content-using-a-callback-and-extra-headers)
- [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates)
- [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers)
- [Chunked Response](#chunked-response)
@ -82,7 +69,6 @@ To use this library you might need to have the latest git versions of [ESP32](ht
- [Specifying Cache-Control header](#specifying-cache-control-header)
- [Specifying Date-Modified header](#specifying-date-modified-header)
- [Specifying Template Processor callback](#specifying-template-processor-callback)
- [Serving static files by custom handling](#serving-static-files-by-custom-handling)
- [Param Rewrite With Matching](#param-rewrite-with-matching)
- [Using filters](#using-filters)
- [Serve different site files in AP mode](#serve-different-site-files-in-ap-mode)
@ -540,56 +526,6 @@ response->addHeader("Server","ESP Async Web Server");
request->send(response);
```
### Respond with content coming from a File
```cpp
//Send index.htm with default content type
request->send(SPIFFS, "/index.htm");
//Send index.htm as text
request->send(SPIFFS, "/index.htm", "text/plain");
//Download index.htm
request->send(SPIFFS, "/index.htm", String(), true);
```
### Respond with content coming from a File and extra headers
```cpp
//Send index.htm with default content type
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm");
//Send index.htm as text
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", "text/plain");
//Download index.htm
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", String(), true);
response->addHeader("Server","ESP Async Web Server");
request->send(response);
```
### Respond with content coming from a File containing templates
Internally uses [Chunked Response](#chunked-response).
Index.htm contents:
```
%HELLO_FROM_TEMPLATE%
```
Somewhere in source files:
```cpp
String processor(const String& var)
{
if(var == "HELLO_FROM_TEMPLATE")
return F("Hello world!");
return String();
}
// ...
//Send index.htm with template processor function
request->send(SPIFFS, "/index.htm", String(), false, processor);
```
### Respond with content using a callback
```cpp
//send 128 bytes as plain text
@ -618,43 +554,6 @@ response->addHeader("Server","ESP Async Web Server");
request->send(response);
```
### Respond with file content using a callback and extra headers
With this code your ESP is able to serve even large (large in terms of ESP, e.g. 100kB) files
without memory problems.
You need to create a file handler in outer function (to have a single one for request) but use
it in a lambda. The catch is that the lambda has it's own lifecycle which may/will cause it's
called after the original function is over thus the original file handle is destroyed. Using the
captured `&file` in the lambda then causes segfault (Hello, Exception 9!) and the whole ESP crashes.
By using this code, you tell the compiler to move the handle into the lambda so it won't be
destroyed when outer function (that one where you call `request->send(response)`) ends.
```cpp
const File file = ... // e.g. SPIFFS.open(path, "r");
const contentType = "application/javascript";
AsyncWebServerResponse *response = request->beginResponse(
contentType,
file.size(),
[file](uint8_t *buffer, size_t maxLen, size_t total) mutable -> size_t {
int bytes = file.read(buffer, maxLen);
// close file at the end
if (bytes + total == file.size()) file.close();
return max(0, bytes); // return 0 even when no bytes were loaded
}
);
if (gzipped) {
response->addHeader(F("Content-Encoding"), F("gzip"));
}
request->send(response);
```
### Respond with content using a callback containing templates
```cpp
String processor(const String& var)
@ -824,41 +723,6 @@ response->setLength();
request->send(response);
```
## Serving static files
In addition to serving files from SPIFFS as described above, the server provide a dedicated handler that optimize the
performance of serving files from SPIFFS - ```AsyncStaticWebHandler```. Use ```server.serveStatic()``` function to
initialize and add a new instance of ```AsyncStaticWebHandler``` to the server.
The Handler will not handle the request if the file does not exists, e.g. the server will continue to look for another
handler that can handle the request.
Notice that you can chain setter functions to setup the handler, or keep a pointer to change it at a later time.
### Serving specific file by name
```cpp
// Serve the file "/www/page.htm" when request url is "/page.htm"
server.serveStatic("/page.htm", SPIFFS, "/www/page.htm");
```
### Serving files in directory
To serve files in a directory, the path to the files should specify a directory in SPIFFS and ends with "/".
```cpp
// Serve files in directory "/www/" when request url starts with "/"
// Request to the root or none existing files will try to server the defualt
// file name "index.htm" if exists
server.serveStatic("/", SPIFFS, "/www/");
// Server with different default file
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html");
```
### Serving static files with authentication
```cpp
server
.serveStatic("/", SPIFFS, "/www/")
.setDefaultFile("default.html")
.setAuthentication("user", "pass");
```
### Specifying Cache-Control header
It is possible to specify Cache-Control header value to reduce the number of calls to the server once the client loaded
the files. For more information on Cache-Control values see [Cache-Control](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9)
@ -911,53 +775,6 @@ String processor(const String& var)
server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor);
```
### Serving static files by custom handling
It may happen your static files are too big and the ESP will crash the request before it sends the whole file.
In that case, you can handle static files with custom file serving through not found handler.
This code below is more-or-less equivalent to this:
```cpp
webServer.serveStatic("/", SPIFFS, STATIC_FILES_PREFIX).setDefaultFile("index.html")
```
First, declare the handling function:
```cpp
bool handleStaticFile(AsyncWebServerRequest *request) {
String path = STATIC_FILES_PREFIX + request->url();
if (path.endsWith("/")) path += F("index.html");
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) {
bool gzipped = false;
if (SPIFFS.exists(pathWithGz)) {
gzipped = true;
path += ".gz";
}
// TODO serve the file
return true;
}
return false;
}
```
And then configure your webserver:
```cpp
webServer.onNotFound([](AsyncWebServerRequest *request) {
if (handleStaticFile(request)) return;
request->send(404);
});
```
You may want to try [Respond with file content using a callback and extra headers](#respond-with-file-content-using-a-callback-and-extra-headers)
For actual serving the file.
## Param Rewrite With Matching
It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter:
Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"
@ -1018,20 +835,6 @@ Two filter callback are provided for convince:
* `ON_STA_FILTER` - return true when requests are made to the STA (station mode) interface.
* `ON_AP_FILTER` - return true when requests are made to the AP (access point) interface.
### Serve different site files in AP mode
```cpp
server.serveStatic("/", SPIFFS, "/www/").setFilter(ON_STA_FILTER);
server.serveStatic("/", SPIFFS, "/ap/").setFilter(ON_AP_FILTER);
```
### Rewrite to different index on AP
```cpp
// Serve the file "/www/index-ap.htm" in AP, and the file "/www/index.htm" on STA
server.rewrite("/", "index.htm");
server.rewrite("/index.htm", "index-ap.htm").setFilter(ON_AP_FILTER);
server.serveStatic("/", SPIFFS, "/www/");
```
### Serving different hosts
```cpp
// Filter callback using request host
@ -1458,9 +1261,6 @@ void setup(){
}
});
// attach filesystem root at URL /fs
server.serveStatic("/fs", SPIFFS, "/");
// Catch-All Handlers
// Any request that can not find a Handler that canHandle it
// ends in the callbacks below.
@ -1543,9 +1343,6 @@ Example of OTA code
```cpp
// OTA callbacks
ArduinoOTA.onStart([]() {
// Clean SPIFFS
SPIFFS.end();
// Disable client connections
ws.enable(false);

View File

@ -1,257 +0,0 @@
// 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();
}

View File

@ -1,3 +0,0 @@
/*.gz
/edit_gz
/.exclude.files

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -1 +0,0 @@
Test

View File

@ -1,131 +0,0 @@
<!--
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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,283 +0,0 @@
/**************************************************************
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>&nbsp;<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

View File

@ -1,56 +0,0 @@
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

View File

@ -1,19 +0,0 @@
<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

View File

@ -1,750 +0,0 @@
/*
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();
}

View File

@ -1,48 +0,0 @@
/*
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;
}

View File

@ -1,20 +0,0 @@
/*
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

View File

@ -1,3 +0,0 @@
/*.gz
/edit_gz
/.exclude.files

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,626 +0,0 @@
<!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>

View File

@ -1,36 +0,0 @@
<!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>

View File

@ -1,2 +0,0 @@
*.gz
.exclude.files

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,533 +0,0 @@
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="&#65087;";down.innerHTML="&#65088;";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="&#65087;";down.innerHTML="&#65088;";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ëls 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ëls 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 doesnt 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,"&#60;").replace(/&/g,"&#38;").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;};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,599 +0,0 @@
<!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&nbsp;&nbsp;&nbsp;
<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>

View File

@ -1,2 +0,0 @@
/*.js.gz
/.exclude.files

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
<!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>

View File

@ -1,27 +0,0 @@
<!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>

View File

@ -1,29 +0,0 @@
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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!

View File

@ -1,65 +0,0 @@
/* [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;
}

View File

@ -1,47 +0,0 @@
/* [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;
}

View File

@ -1,136 +0,0 @@
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 = "&#65087;";
down.innerHTML = "&#65088;";
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");
}
};

View File

@ -1,116 +0,0 @@
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 = "&#65087;";
down.innerHTML = "&#65088;";
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;
}
};

View File

@ -1,94 +0,0 @@
<!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 youll 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>

View File

@ -1,58 +0,0 @@
<!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>

View File

@ -1,82 +0,0 @@
<!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>

View File

@ -1,130 +0,0 @@
<!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>

View File

@ -1,88 +0,0 @@
<!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>

View File

@ -1,116 +0,0 @@
<!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>

View File

@ -1,137 +0,0 @@
<!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 &amp; 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>

View File

@ -1,60 +0,0 @@
<!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>

View File

@ -1,62 +0,0 @@
<!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>

View File

@ -1,56 +0,0 @@
<!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>

View File

@ -1,54 +0,0 @@
<!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>

View File

@ -1,109 +0,0 @@
<!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>

View File

@ -1,168 +0,0 @@
<!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>

View File

@ -1,91 +0,0 @@
<!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>

View File

@ -1,71 +0,0 @@
<!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>

View File

@ -1,144 +0,0 @@
<!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>

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015"]
}

View File

@ -1,31 +0,0 @@
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
}
};

View File

@ -1,30 +0,0 @@
# 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

View File

@ -1,3 +0,0 @@
language: node_js
node_js:
- "7"

View File

@ -1,7 +0,0 @@
# 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.

View File

@ -1,104 +0,0 @@
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');
});
});
});

View File

@ -1,144 +0,0 @@
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);
});

View File

@ -1,34 +0,0 @@
'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
});
});
});

View File

@ -1,101 +0,0 @@
<!DOCTYPE html>
<html lang="en">
</hea>
<meta charset="UTF-8">
<title>Notifications</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background-color: floralwhite;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
text-align: center;
padding: 30px;
}
h1 {
margin-top: 0;
}
form {
text-align: left;
max-width: 600px;
width: 100%;
margin: 0 auto;
display: block;
}
input, select {
width: 100%;
margin-top: 5px;
margin-bottom: 20px;
padding: 5px;
box-sizing: border-box;
}
input[type=checkbox] {
width: auto;
}
input[type=submit] {
background: #2f96b4;
border: 0;
color: #fff;
margin-bottom: 0;
padding: 10px 5px;
}
</style>
<!-- Notifications styling -->
<link rel="stylesheet" href="../dist/notifications.css" type="text/css">
<head>
<body>
<h1>Notifications</h1>
<form>
<label for="title">Title (optional)</label>
<br/>
<input type="text" id="title" placeholder="Enter a title..." value="Notification">
<label for="message">Message</label>
<br/>
<input type="text" id="message" placeholder="Enter a message..." value="I am a default message">
<label for="position">Notification position:</label>
<br/>
<select id="position">
<option value="nfc-top-right">Top Right</option>
<option value="nfc-bottom-right">Bottom Right</option>
<option value="nfc-top-left">Top Left</option>
<option value="nfc-bottom-left">Bottom Left</option>
</select>
<label for="duration">Show Duration (ms)</label>
<br/>
<input id="duration" type="number" value="3000"/>
<label for="theme">Theme</label>
<br/>
<select id="theme">
<option value="success">Success</option>
<option value="info">Information</option>
<option value="warning">Warning</option>
<option value="error">Error</option>
<option value="none">None</option>
</select>
<label for="close">Close on click</label>
<input id="close" type="checkbox" value="Close on click" checked>
<br/>
<label for="closeButton">Display a close button</label>
<input id="closeButton" type="checkbox" value="Display close button">
<input type="submit" value="Display notification">
</form>
<script src="../dist/notifications.js" type="text/javascript"></script>
<script src="./demo.js" type="text/javascript"></script>
</body>
</html>

View File

@ -1,37 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Notifications</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../dist/notifications.css" type="text/css">
<script src="../dist/notifications.js" type="text/javascript"></script>
</head>
<body>
<button id="not" type="button" class="tbu" onclick="button2Click(this);">Notify</button>
<script type="text/javascript">
const successNotification = window.createNotification({
positionClass: 'nfc-bottom-right',
theme: 'info',
showDuration: 2000
});
function button2Click(e) {
// Invoke success notification
successNotification({
message: 'Simple success notification ' + e.id
});
};
</script>
</body>
</html>

View File

@ -1 +0,0 @@
.ncf-container{font-size:14px;box-sizing:border-box;position:fixed;z-index:999999}.ncf-container.nfc-top-left{top:12px;left:12px}.ncf-container.nfc-top-right{top:12px;right:12px}.ncf-container.nfc-bottom-right{bottom:12px;right:12px}.ncf-container.nfc-bottom-left{bottom:12px;left:12px}@media (max-width:767px){.ncf-container{left:0;right:0}}.ncf-container .ncf{background:#fff;transition:.3s ease;position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:30px;width:300px;border-radius:3px 3px 3px 3px;box-shadow:0 0 12px #999;color:#000;opacity:.9;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=90);filter:alpha(opacity=90);background-position:15px!important;background-repeat:no-repeat!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ncf-container .ncf:hover{box-shadow:0 0 12px #000;opacity:1;cursor:pointer}.ncf-container .ncf .ncf-title{font-weight:700;font-size:16px;text-align:left;margin-top:0;margin-bottom:6px;word-wrap:break-word}.ncf-container .ncf .nfc-message{margin:0;text-align:left;word-wrap:break-word}.ncf-container .success{background:#51a351;color:#fff;padding:15px 15px 15px 50px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")}.ncf-container .info{background:#2f96b4;color:#fff;padding:15px 15px 15px 50px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")}.ncf-container .warning{background:#f87400;color:#fff;padding:15px 15px 15px 50px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")}.ncf-container .error{background:#bd362f;color:#fff;padding:15px 15px 15px 50px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}.ncf-container button{position:relative;right:-.3em;top:-.3em;float:right;font-weight:700;color:#fff;text-shadow:0 1px 0 #fff;opacity:.8;line-height:1;font-size:16px;padding:0;cursor:pointer;background:transparent;border:0}.ncf-container button:hover{opacity:1}

View File

@ -1 +0,0 @@
!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){}]);

View File

@ -1,58 +0,0 @@
{
"name": "styled-notifications",
"version": "1.0.1",
"description": "A simple JavaScript notifications library",
"main": "dist/notifications.js",
"scripts": {
"start": "webpack --watch",
"build": "webpack -p",
"test": "jest",
"prepare": "yarn run test && yarn run build"
},
"pre-commit": [
"prepare"
],
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/JamieLivingstone/Notifications.git"
},
"keywords": [
"notification",
"popup",
"alert",
"toast"
],
"author": "Jamie Livingstone",
"contributors": [
{
"name": "Jamie Livingstone (https://github.com/JamieLivingstone)"
},
{
"name": "cavebeavis (https://github.com/cavebeavis)"
}
],
"license": "ISC",
"bugs": {
"url": "https://github.com/JamieLivingstone/Notifications/issues"
},
"homepage": "https://github.com/JamieLivingstone/Notifications#readme",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2015-ie": "^6.7.0",
"css-loader": "^0.28.7",
"eslint": "^4.6.1",
"extract-text-webpack-plugin": "^3.0.0",
"jest": "^21.0.2",
"node-sass": "^4.5.3",
"pre-commit": "^1.2.2",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^3.5.6"
}
}

View File

@ -1,82 +0,0 @@
[![Build Status](https://travis-ci.org/JamieLivingstone/Notifications.svg?branch=master)](https://travis-ci.org/JamieLivingstone/Notifications)
# Notifications
**Notifications** is a Javascript library for notifications heavily inspired by toastr but does not require any dependencies such as jQuery.
Works on browsers: IE9+, Safari, Chrome, FireFox, opera, edge
## npm Installation
Do either
```
npm i styled-notifications
```
or add the following to your `package.json`:
```
"dependencies": {
"styled-notifications": "^1.0.1"
},
```
## Installation
Download files from the dist folder and then:
1. Link to notifications.css `<link href="notifications.css" rel="stylesheet"/>`
2. Link to notifications.js `<script src="notifications.js"></script>`
## Usage
### Custom options
- closeOnClick <bool> - Close the notification dialog when a click is invoked.
- displayCloseButton <bool> - Display a close button in the top right hand corner of the notification.
- positionClass <string> - Set the position of the notification dialog. Accepted positions: ('nfc-top-right', 'nfc-bottom-right', 'nfc-bottom-left', 'nfc-top-left').
- onClick <function(event)> - Call a callback function when a click is invoked on a notification.
- showDuration <integer> - Milliseconds the notification should be visible (0 for a notification that will remain open until clicked)
- theme <string> - Set the position of the notification dialog. Accepted positions: ('success', 'info', 'warning', 'error', 'A custom clasName').
```
const defaultOptions = {
closeOnClick: true,
displayCloseButton: false,
positionClass: 'nfc-top-right',
onclick: false,
showDuration: 3500,
theme: 'success'
};
```
## Example
### Success notification
```
// Create a success notification instance
const successNotification = window.createNotification({
theme: 'success',
showDuration: 5000
});
// Invoke success notification
successNotification({
message: 'Simple success notification'
});
// Use the same instance but pass a title
successNotification({
title: 'Working',
message: 'Simple success notification'
});
```
### Information notification
```
// Only running it once? Invoke immediately like this
window.createNotification({
theme: 'success',
showDuration: 5000
})({
message: 'I have some information for you...'
});
```
### Todo
~~1. Add to NPM~~
2. Improve documentation
3. Further device testing
4. Add contributor instructions

View File

@ -1,24 +0,0 @@
export const partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs);
export const append = (el, ...children) => children.forEach(child => el.appendChild(child));
export const isString = input => typeof input === 'string';
export const createElement = (elementType, ...classNames) => {
const element = document.createElement(elementType);
if(classNames.length) {
classNames.forEach(currentClass => element.classList.add(currentClass));
}
return element;
};
const setInnerText = (element, text) => {
element.innerText = text;
return element;
};
const createTextElement = (elementType, ...classNames) => partial(setInnerText, createElement(elementType, ...classNames));
export const createParagraph = (...classNames) => createTextElement('p', ...classNames);

View File

@ -1,148 +0,0 @@
'use strict';
// Polyfills
import './polyfills/classList';
import {
append,
createElement,
createParagraph,
isString
} from './helpers';
(function Notifications(window) {
// Default notification options
const defaultOptions = {
closeOnClick: true,
displayCloseButton: false,
positionClass: 'nfc-top-right',
onclick: false,
showDuration: 3500,
theme: 'success'
};
function configureOptions(options) {
// Create a copy of options and merge with defaults
options = Object.assign({}, defaultOptions, options);
// Validate position class
function validatePositionClass(className) {
const validPositions = [
'nfc-top-left',
'nfc-top-right',
'nfc-bottom-left',
'nfc-bottom-right'
];
return validPositions.indexOf(className) > -1;
}
// Verify position, if invalid reset to default
if (!validatePositionClass(options.positionClass)) {
console.warn('An invalid notification position class has been specified.');
options.positionClass = defaultOptions.positionClass;
}
// Verify onClick is a function
if (options.onclick && typeof options.onclick !== 'function') {
console.warn('Notification on click must be a function.');
options.onclick = defaultOptions.onclick;
}
// Verify show duration
if(typeof options.showDuration !== 'number') {
options.showDuration = defaultOptions.showDuration;
}
// Verify theme
if(!isString(options.theme) || options.theme.length === 0) {
console.warn('Notification theme must be a string with length');
options.theme = defaultOptions.theme;
}
return options;
}
// Create a new notification instance
function createNotification(options) {
// Validate options and set defaults
options = configureOptions(options);
// Return a notification function
return function notification({ title, message } = {}) {
const container = createNotificationContainer(options.positionClass);
if(!title && !message) {
return console.warn('Notification must contain a title or a message!');
}
// Create the notification wrapper
const notificationEl = createElement('div', 'ncf', options.theme);
// Close on click
if(options.closeOnClick === true) {
notificationEl.addEventListener('click', () => container.removeChild(notificationEl));
}
// Custom click callback
if(options.onclick) {
notificationEl.addEventListener('click', (e) => options.onclick(e));
}
// Display close button
if(options.displayCloseButton) {
const closeButton = createElement('button');
closeButton.innerText = 'X';
// Use the wrappers close on click to avoid useless event listeners
if(options.closeOnClick === false){
closeButton.addEventListener('click', () =>container.removeChild(notificationEl));
}
append(notificationEl, closeButton);
}
// Append title and message
isString(title) && title.length && append(notificationEl, createParagraph('ncf-title')(title));
isString(message) && message.length && append(notificationEl, createParagraph('nfc-message')(message));
// Append to container
append(container, notificationEl);
// Remove element after duration
if(options.showDuration && options.showDuration > 0) {
const timeout = setTimeout(() => {
container.removeChild(notificationEl);
// Remove container if empty
if(container.querySelectorAll('.ncf').length === 0) {
document.body.removeChild(container);
}
}, options.showDuration);
// If close on click is enabled and the user clicks, cancel timeout
if(options.closeOnClick || options.displayCloseButton) {
notificationEl.addEventListener('click', () => clearTimeout(timeout));
}
}
};
}
function createNotificationContainer(position) {
let container = document.querySelector(`.${position}`);
if(!container) {
container = createElement('div', 'ncf-container', position);
append(document.body, container);
}
return container;
}
// Add Notifications to window to make globally accessible
if (window.createNotification) {
console.warn('Window already contains a create notification function. Have you included the script twice?');
} else {
window.createNotification = createNotification;
}
})(window);

View File

@ -1,68 +0,0 @@
(function () {
if (typeof window.Element === 'undefined' || 'classList' in document.documentElement) return;
var prototype = Array.prototype,
push = prototype.push,
splice = prototype.splice,
join = prototype.join;
function DOMTokenList(el) {
this.el = el;
// The className needs to be trimmed and split on whitespace
// to retrieve a list of classes.
var classes = el.className.replace(/^\s+|\s+$/g,'').split(/\s+/);
for (var i = 0; i < classes.length; i++) {
push.call(this, classes[i]);
}
}
DOMTokenList.prototype = {
add: function(token) {
if(this.contains(token)) return;
push.call(this, token);
this.el.className = this.toString();
},
contains: function(token) {
return this.el.className.indexOf(token) != -1;
},
item: function(index) {
return this[index] || null;
},
remove: function(token) {
if (!this.contains(token)) return;
for (var i = 0; i < this.length; i++) {
if (this[i] == token) break;
}
splice.call(this, i, 1);
this.el.className = this.toString();
},
toString: function() {
return join.call(this, ' ');
},
toggle: function(token) {
if (!this.contains(token)) {
this.add(token);
} else {
this.remove(token);
}
return this.contains(token);
}
};
window.DOMTokenList = DOMTokenList;
function defineElementGetter (obj, prop, getter) {
if (Object.defineProperty) {
Object.defineProperty(obj, prop,{
get : getter
});
} else {
obj.__defineGetter__(prop, getter);
}
}
defineElementGetter(Element.prototype, 'classList', function () {
return new DOMTokenList(this);
});
})();

View File

@ -1,134 +0,0 @@
// Base colors
$success: #51A351;
$info: #2F96B4;
$warning: #f87400;
$error: #BD362F;
$grey: #999999;
.ncf-container {
font-size: 14px;
box-sizing: border-box;
position: fixed;
z-index: 999999;
&.nfc-top-left {
top: 12px;
left: 12px;
}
&.nfc-top-right {
top: 12px;
right: 12px;
}
&.nfc-bottom-right {
bottom: 12px;
right: 12px;
}
&.nfc-bottom-left {
bottom: 12px;
left: 12px;
}
@media (max-width: 767px) {
left: 0;
right: 0;
}
.ncf {
background: #ffffff;
transition: .3s ease;
position: relative;
pointer-events: auto;
overflow: hidden;
margin: 0 0 6px;
padding: 30px;
width: 300px;
border-radius: 3px 3px 3px 3px;
box-shadow: 0 0 12px $grey;
color: #000000;
opacity: 0.9;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90);
filter: alpha(opacity=90);
background-position: 15px center !important;
background-repeat: no-repeat !important;
// Prevent annoying text selection
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
&:hover {
box-shadow: 0 0 12px #000000;
opacity: 1;
cursor: pointer;
}
.ncf-title {
font-weight: bold;
font-size: 16px;
text-align: left;
margin-top: 0;
margin-bottom: 6px;
word-wrap: break-word;
}
.nfc-message {
margin: 0;
text-align: left;
word-wrap: break-word;
}
}
// Themes
.success {
background: $success;
color: #ffffff;
padding: 15px 15px 15px 50px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==");
}
.info {
background: $info;
color: #ffffff;
padding: 15px 15px 15px 50px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=");
}
.warning {
background: $warning;
color: #ffffff;
padding: 15px 15px 15px 50px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=");
}
.error {
background: $error;
color: #ffffff;
padding: 15px 15px 15px 50px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
}
button {
position: relative;
right: -0.3em;
top: -0.3em;
float: right;
font-weight: bold;
color: #FFFFFF;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.8;
line-height: 1;
font-size: 16px;
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
&:hover {
opacity: 1;
}
}
}

View File

@ -1,41 +0,0 @@
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractSass = new ExtractTextPlugin({
filename: 'notifications.css',
disable: process.env.NODE_ENV === 'development'
});
module.exports = {
entry: ['./src/index.js', './src/style.scss'],
output: {
path: __dirname + '/dist',
filename: 'notifications.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['babel-preset-es2015', 'es2015-ie']
}
},
{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: 'css-loader'
}, {
loader: 'sass-loader'
}],
// use style-loader in development
fallback: 'style-loader'
})
}
],
},
plugins: [
extractSass
]
};

Some files were not shown because too many files have changed in this diff Show More