added sarah
166
works/Sarah Grant/otastaImg/DNSServer.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include "./DNSServer.h"
|
||||
#include <lwip/def.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#define DEBUG
|
||||
#define DEBUG_OUTPUT Serial
|
||||
|
||||
DNSServer::DNSServer()
|
||||
{
|
||||
_ttl = htonl(60);
|
||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
||||
}
|
||||
|
||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
||||
const IPAddress &resolvedIP)
|
||||
{
|
||||
_port = port;
|
||||
_domainName = domainName;
|
||||
_resolvedIP[0] = resolvedIP[0];
|
||||
_resolvedIP[1] = resolvedIP[1];
|
||||
_resolvedIP[2] = resolvedIP[2];
|
||||
_resolvedIP[3] = resolvedIP[3];
|
||||
downcaseAndRemoveWwwPrefix(_domainName);
|
||||
return _udp.begin(_port) == 1;
|
||||
}
|
||||
|
||||
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
|
||||
{
|
||||
_errorReplyCode = replyCode;
|
||||
}
|
||||
|
||||
void DNSServer::setTTL(const uint32_t &ttl)
|
||||
{
|
||||
_ttl = htonl(ttl);
|
||||
}
|
||||
|
||||
void DNSServer::stop()
|
||||
{
|
||||
_udp.stop();
|
||||
}
|
||||
|
||||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
|
||||
{
|
||||
domainName.toLowerCase();
|
||||
domainName.replace("www.", "");
|
||||
domainName.replace("https://", "");
|
||||
}
|
||||
|
||||
void DNSServer::processNextRequest()
|
||||
{
|
||||
_currentPacketSize = _udp.parsePacket();
|
||||
if (_currentPacketSize)
|
||||
{
|
||||
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
|
||||
_udp.read(_buffer, _currentPacketSize);
|
||||
_dnsHeader = (DNSHeader*) _buffer;
|
||||
|
||||
if (_dnsHeader->QR == DNS_QR_QUERY &&
|
||||
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
|
||||
requestIncludesOnlyOneQuestion() &&
|
||||
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
|
||||
)
|
||||
|
||||
{
|
||||
replyWithIP();
|
||||
}
|
||||
else if (_dnsHeader->QR == DNS_QR_QUERY)
|
||||
{
|
||||
replyWithCustomCode();
|
||||
}
|
||||
|
||||
free(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool DNSServer::requestIncludesOnlyOneQuestion()
|
||||
{
|
||||
return ntohs(_dnsHeader->QDCount) == 1 &&
|
||||
_dnsHeader->ANCount == 0 &&
|
||||
_dnsHeader->NSCount == 0 &&
|
||||
_dnsHeader->ARCount == 0;
|
||||
}
|
||||
|
||||
String DNSServer::getDomainNameWithoutWwwPrefix()
|
||||
{
|
||||
String parsedDomainName = "";
|
||||
unsigned char *start = _buffer + 12;
|
||||
if (*start == 0)
|
||||
{
|
||||
return parsedDomainName;
|
||||
}
|
||||
int pos = 0;
|
||||
while(true)
|
||||
{
|
||||
unsigned char labelLength = *(start + pos);
|
||||
for(int i = 0; i < labelLength; i++)
|
||||
{
|
||||
pos++;
|
||||
parsedDomainName += (char)*(start + pos);
|
||||
}
|
||||
pos++;
|
||||
if (*(start + pos) == 0)
|
||||
{
|
||||
downcaseAndRemoveWwwPrefix(parsedDomainName);
|
||||
return parsedDomainName;
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedDomainName += ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DNSServer::replyWithIP()
|
||||
{
|
||||
_dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
_dnsHeader->ANCount = _dnsHeader->QDCount;
|
||||
_dnsHeader->QDCount = _dnsHeader->QDCount;
|
||||
//_dnsHeader->RA = 1;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write(_buffer, _currentPacketSize);
|
||||
|
||||
_udp.write((uint8_t)192); // answer name is a pointer
|
||||
_udp.write((uint8_t)12); // pointer to offset at 0x00c
|
||||
|
||||
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address)
|
||||
_udp.write((uint8_t)1);
|
||||
|
||||
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address)
|
||||
_udp.write((uint8_t)1);
|
||||
|
||||
_udp.write((unsigned char*)&_ttl, 4);
|
||||
|
||||
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
|
||||
_udp.write((uint8_t)0);
|
||||
_udp.write((uint8_t)4);
|
||||
_udp.write(_resolvedIP, sizeof(_resolvedIP));
|
||||
_udp.endPacket();
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
DEBUG_OUTPUT.print("DNS responds: ");
|
||||
DEBUG_OUTPUT.print(_resolvedIP[0]);
|
||||
DEBUG_OUTPUT.print(".");
|
||||
DEBUG_OUTPUT.print(_resolvedIP[1]);
|
||||
DEBUG_OUTPUT.print(".");
|
||||
DEBUG_OUTPUT.print(_resolvedIP[2]);
|
||||
DEBUG_OUTPUT.print(".");
|
||||
DEBUG_OUTPUT.print(_resolvedIP[3]);
|
||||
DEBUG_OUTPUT.print(" for ");
|
||||
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix());
|
||||
#endif
|
||||
}
|
||||
|
||||
void DNSServer::replyWithCustomCode()
|
||||
{
|
||||
_dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
_dnsHeader->RCode = (unsigned char)_errorReplyCode;
|
||||
_dnsHeader->QDCount = 0;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write(_buffer, sizeof(DNSHeader));
|
||||
_udp.endPacket();
|
||||
}
|
72
works/Sarah Grant/otastaImg/DNSServer.h
Executable file
@ -0,0 +1,72 @@
|
||||
#ifndef DNSServer_h
|
||||
#define DNSServer_h
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#define DNS_QR_QUERY 0
|
||||
#define DNS_QR_RESPONSE 1
|
||||
#define DNS_OPCODE_QUERY 0
|
||||
|
||||
enum class DNSReplyCode
|
||||
{
|
||||
NoError = 0,
|
||||
FormError = 1,
|
||||
ServerFailure = 2,
|
||||
NonExistentDomain = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
YXDomain = 6,
|
||||
YXRRSet = 7,
|
||||
NXRRSet = 8
|
||||
};
|
||||
|
||||
struct DNSHeader
|
||||
{
|
||||
uint16_t ID; // identification number
|
||||
unsigned char RD : 1; // recursion desired
|
||||
unsigned char TC : 1; // truncated message
|
||||
unsigned char AA : 1; // authoritive answer
|
||||
unsigned char OPCode : 4; // message_type
|
||||
unsigned char QR : 1; // query/response flag
|
||||
unsigned char RCode : 4; // response code
|
||||
unsigned char Z : 3; // its z! reserved
|
||||
unsigned char RA : 1; // recursion available
|
||||
uint16_t QDCount; // number of question entries
|
||||
uint16_t ANCount; // number of answer entries
|
||||
uint16_t NSCount; // number of authority entries
|
||||
uint16_t ARCount; // number of resource entries
|
||||
};
|
||||
|
||||
class DNSServer
|
||||
{
|
||||
public:
|
||||
DNSServer();
|
||||
void processNextRequest();
|
||||
void setErrorReplyCode(const DNSReplyCode &replyCode);
|
||||
void setTTL(const uint32_t &ttl);
|
||||
|
||||
// Returns true if successful, false if there are no sockets available
|
||||
bool start(const uint16_t &port,
|
||||
const String &domainName,
|
||||
const IPAddress &resolvedIP);
|
||||
// stops the DNS server
|
||||
void stop();
|
||||
|
||||
private:
|
||||
WiFiUDP _udp;
|
||||
uint16_t _port;
|
||||
String _domainName;
|
||||
unsigned char _resolvedIP[4];
|
||||
int _currentPacketSize;
|
||||
unsigned char* _buffer;
|
||||
DNSHeader* _dnsHeader;
|
||||
uint32_t _ttl;
|
||||
DNSReplyCode _errorReplyCode;
|
||||
|
||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
||||
String getDomainNameWithoutWwwPrefix();
|
||||
bool requestIncludesOnlyOneQuestion();
|
||||
void replyWithIP();
|
||||
void replyWithCustomCode();
|
||||
};
|
||||
#endif
|
||||
|
BIN
works/Sarah Grant/otastaImg/data/.DS_Store
vendored
Normal file
BIN
works/Sarah Grant/otastaImg/data/img/Berlin_TV_Tower.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/ES400-1.png
Normal file
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 60 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/berghain.gif
Normal file
After Width: | Height: | Size: 331 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/brandenberg-gate.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/gradient250x250.jpg
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/mask.gif
Normal file
After Width: | Height: | Size: 357 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/source.gif
Normal file
After Width: | Height: | Size: 143 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/thumbnail.gif
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
works/Sarah Grant/otastaImg/data/img/tvtower.png
Normal file
After Width: | Height: | Size: 51 KiB |
399
works/Sarah Grant/otastaImg/data/index.html
Normal file
@ -0,0 +1,399 @@
|
||||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
/*position: absolute;*/
|
||||
}
|
||||
.rel {
|
||||
position: relative;
|
||||
}
|
||||
#capture {
|
||||
height: 100vh;
|
||||
}
|
||||
.text {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
z-index: -1;
|
||||
}
|
||||
.text > div {
|
||||
/*font-size: 20px;*/
|
||||
font-family: monospace;
|
||||
color: #695858;
|
||||
letter-spacing: .1em;
|
||||
padding: 13px;
|
||||
transform: rotate(355deg);
|
||||
}
|
||||
#button-wrapper {
|
||||
position: fixed;
|
||||
top: 300px;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
animation: translate 20s infinite linear;
|
||||
z-index: 9999;
|
||||
}
|
||||
#imgButton {
|
||||
background: linear-gradient(to right, #CB3066, #16BFFD);
|
||||
background-size: 300% 300%;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
padding: 10px 30px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
box-shadow: 0 5px 10px 0 rgba(205, 209, 215, 0.8);
|
||||
animation: gradient 2.5s ease infinite, inmotion 1.5s alternate infinite ease-in-out;
|
||||
}
|
||||
@keyframes translate {
|
||||
to { transform: translateX(200%); }
|
||||
}
|
||||
@keyframes inmotion {
|
||||
to { transform: translateY(100px); }
|
||||
}
|
||||
@keyframes gradient {
|
||||
0% { background-position: 0% 50% }
|
||||
50% { background-position: 100% 50% }
|
||||
100% { background-position: 0% 50% }
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<body id="capture">
|
||||
<div id="button-wrapper">
|
||||
<button id="imgButton">CLICK TO SAVE YOUR KATAMARI</button>
|
||||
</div>
|
||||
<div class="text">
|
||||
<div>Bouchéstraße</div>
|
||||
<div>Alt-Treptow</div>
|
||||
<div>Berlin</div>
|
||||
<div>Kiefholzstraße</div>
|
||||
<div>Elsenstraße</div>
|
||||
<div>Harzer Straße</div>
|
||||
<div>Wildenbruchstraße</div>
|
||||
<div>Heidelberger Straße</div>
|
||||
<div>Karl-Kunger-Straße</div>
|
||||
<div>Sülzhayner Straße</div>
|
||||
<div>Schmollerstraße</div>
|
||||
<div>Mengerzeile</div>
|
||||
<div>Am Treptower Park</div>
|
||||
<div>Puschkinallee</div>
|
||||
<div>Martin-Hoffman-Straße</div>
|
||||
<div>Eichenstraße</div>
|
||||
<div>Fanny-Zobel-Straße</div>
|
||||
<div>Matthesstraße</div>
|
||||
<div>Beermannstraße</div>
|
||||
<div>Jordanstraße</div>
|
||||
<div>Lohmühlenstraße</div>
|
||||
<div>Am Flutgraben</div>
|
||||
<div>Schleusenufer</div>
|
||||
<div>Landwehr Canal</div>
|
||||
<div>Spree</div>
|
||||
<div>Treptower Park</div>
|
||||
<div>Rummelsburger See</div>
|
||||
<div>Schlesisches Tor</div>
|
||||
<div>Skalitzer Straße</div>
|
||||
<div>Köpenicker Straße</div>
|
||||
<div>Brückenstraße</div>
|
||||
<div>Rungestraße</div>
|
||||
<div>Ohmstraße</div>
|
||||
<div>Michaelkirchstraße</div>
|
||||
<div>Michaelkirchplatz</div>
|
||||
<div>Heinrich-Heine-Straße</div>
|
||||
<div>Jannowitzbrücke</div>
|
||||
<div>Schlesisches Straße</div>
|
||||
<div>Cuvrystraße</div>
|
||||
<div>Wrangelstraße</div>
|
||||
<div>Taborstraße</div>
|
||||
<div>Heckmannufer</div>
|
||||
<div>Görlitzer Ufer</div>
|
||||
<div>Görlitzer Park</div>
|
||||
<div>Falckenstein Straße</div>
|
||||
<div>Oppelner Straße</div>
|
||||
<div>Sorauer Straße</div>
|
||||
<div>Lübbener Straße</div>
|
||||
<div>Plesser Straße</div>
|
||||
<div>Krüllsstraße</div>
|
||||
<div>Kiehlufer</div>
|
||||
<div>Treptower Straße</div>
|
||||
<div>Hüttenroder Weg</div>
|
||||
<div>Onckenstraße</div>
|
||||
<div>Lexisstraße</div>
|
||||
<div>Grabowstraße</div>
|
||||
<div>Wiener Straße</div>
|
||||
<div>Reichenberger Straße</div>
|
||||
<div>Paul-Lincke-Ufer</div>
|
||||
<div>Glogauer Straße</div>
|
||||
<div>Liegnitzer Straße</div>
|
||||
<div>Forster Straße</div>
|
||||
<div>Ohlauer Straße</div>
|
||||
<div>Lausitzer Straße</div>
|
||||
<div>Manteuffelstraße</div>
|
||||
<div>Mariannenstraße</div>
|
||||
<div>Muskauer Straße</div>
|
||||
<div>Warschauer Straße</div>
|
||||
<div>Revaler Straße</div>
|
||||
<div>Simon-Dach-Straße</div>
|
||||
<div>Boxhagener Platz</div>
|
||||
<div>Kreuzberg</div>
|
||||
<div>Neukölln</div>
|
||||
<div>Bouchéstraße</div>
|
||||
<div>Alt-Treptow</div>
|
||||
<div>Berlin</div>
|
||||
<div>Kiefholzstraße</div>
|
||||
<div>Elsenstraße</div>
|
||||
<div>Harzer Straße</div>
|
||||
<div>Wildenbruchstraße</div>
|
||||
<div>Heidelberger Straße</div>
|
||||
<div>Karl-Kunger-Straße</div>
|
||||
<div>Sülzhayner Straße</div>
|
||||
<div>Schmollerstraße</div>
|
||||
<div>Mengerzeile</div>
|
||||
<div>Am Treptower Park</div>
|
||||
<div>Puschkinallee</div>
|
||||
<div>Martin-Hoffman-Straße</div>
|
||||
<div>Eichenstraße</div>
|
||||
<div>Fanny-Zobel-Straße</div>
|
||||
<div>Matthesstraße</div>
|
||||
<div>Beermannstraße</div>
|
||||
<div>Jordanstraße</div>
|
||||
<div>Lohmühlenstraße</div>
|
||||
<div>Am Flutgraben</div>
|
||||
<div>Schleusenufer</div>
|
||||
<div>Landwehr Canal</div>
|
||||
<div>Spree</div>
|
||||
<div>Treptower Park</div>
|
||||
<div>Rummelsburger See</div>
|
||||
<div>Schlesisches Tor</div>
|
||||
<div>Skalitzer Straße</div>
|
||||
<div>Köpenicker Straße</div>
|
||||
<div>Brückenstraße</div>
|
||||
<div>Rungestraße</div>
|
||||
<div>Ohmstraße</div>
|
||||
<div>Michaelkirchstraße</div>
|
||||
<div>Michaelkirchplatz</div>
|
||||
<div>Heinrich-Heine-Straße</div>
|
||||
<div>Jannowitzbrücke</div>
|
||||
<div>Schlesisches Straße</div>
|
||||
<div>Cuvrystraße</div>
|
||||
<div>Wrangelstraße</div>
|
||||
<div>Taborstraße</div>
|
||||
<div>Heckmannufer</div>
|
||||
<div>Görlitzer Ufer</div>
|
||||
<div>Görlitzer Park</div>
|
||||
<div>Falckenstein Straße</div>
|
||||
<div>Oppelner Straße</div>
|
||||
<div>Sorauer Straße</div>
|
||||
<div>Lübbener Straße</div>
|
||||
<div>Plesser Straße</div>
|
||||
<div>Krüllsstraße</div>
|
||||
<div>Kiehlufer</div>
|
||||
<div>Treptower Straße</div>
|
||||
<div>Hüttenroder Weg</div>
|
||||
<div>Onckenstraße</div>
|
||||
<div>Lexisstraße</div>
|
||||
<div>Grabowstraße</div>
|
||||
<div>Wiener Straße</div>
|
||||
<div>Reichenberger Straße</div>
|
||||
<div>Paul-Lincke-Ufer</div>
|
||||
<div>Glogauer Straße</div>
|
||||
<div>Liegnitzer Straße</div>
|
||||
<div>Forster Straße</div>
|
||||
<div>Ohlauer Straße</div>
|
||||
<div>Lausitzer Straße</div>
|
||||
<div>Manteuffelstraße</div>
|
||||
<div>Mariannenstraße</div>
|
||||
<div>Muskauer Straße</div>
|
||||
<div>Warschauer Straße</div>
|
||||
<div>Revaler Straße</div>
|
||||
<div>Simon-Dach-Straße</div>
|
||||
<div>Boxhagener Platz</div>
|
||||
<div>Kreuzberg</div>
|
||||
<div>Neukölln</div>
|
||||
<div>Bouchéstraße</div>
|
||||
<div>Alt-Treptow</div>
|
||||
<div>Berlin</div>
|
||||
<div>Kiefholzstraße</div>
|
||||
<div>Elsenstraße</div>
|
||||
<div>Harzer Straße</div>
|
||||
<div>Wildenbruchstraße</div>
|
||||
<div>Heidelberger Straße</div>
|
||||
<div>Karl-Kunger-Straße</div>
|
||||
<div>Sülzhayner Straße</div>
|
||||
<div>Schmollerstraße</div>
|
||||
<div>Mengerzeile</div>
|
||||
<div>Am Treptower Park</div>
|
||||
<div>Puschkinallee</div>
|
||||
<div>Martin-Hoffman-Straße</div>
|
||||
<div>Eichenstraße</div>
|
||||
<div>Fanny-Zobel-Straße</div>
|
||||
<div>Matthesstraße</div>
|
||||
<div>Beermannstraße</div>
|
||||
<div>Jordanstraße</div>
|
||||
<div>Lohmühlenstraße</div>
|
||||
<div>Am Flutgraben</div>
|
||||
<div>Schleusenufer</div>
|
||||
<div>Landwehr Canal</div>
|
||||
<div>Spree</div>
|
||||
<div>Treptower Park</div>
|
||||
<div>Rummelsburger See</div>
|
||||
<div>Schlesisches Tor</div>
|
||||
<div>Skalitzer Straße</div>
|
||||
<div>Köpenicker Straße</div>
|
||||
<div>Brückenstraße</div>
|
||||
<div>Rungestraße</div>
|
||||
<div>Ohmstraße</div>
|
||||
<div>Michaelkirchstraße</div>
|
||||
<div>Michaelkirchplatz</div>
|
||||
<div>Heinrich-Heine-Straße</div>
|
||||
<div>Jannowitzbrücke</div>
|
||||
<div>Schlesisches Straße</div>
|
||||
<div>Cuvrystraße</div>
|
||||
<div>Wrangelstraße</div>
|
||||
<div>Taborstraße</div>
|
||||
<div>Heckmannufer</div>
|
||||
<div>Görlitzer Ufer</div>
|
||||
<div>Görlitzer Park</div>
|
||||
<div>Falckenstein Straße</div>
|
||||
<div>Oppelner Straße</div>
|
||||
<div>Sorauer Straße</div>
|
||||
<div>Lübbener Straße</div>
|
||||
<div>Plesser Straße</div>
|
||||
<div>Krüllsstraße</div>
|
||||
<div>Kiehlufer</div>
|
||||
<div>Treptower Straße</div>
|
||||
<div>Hüttenroder Weg</div>
|
||||
<div>Onckenstraße</div>
|
||||
<div>Lexisstraße</div>
|
||||
<div>Grabowstraße</div>
|
||||
<div>Wiener Straße</div>
|
||||
<div>Reichenberger Straße</div>
|
||||
<div>Paul-Lincke-Ufer</div>
|
||||
<div>Glogauer Straße</div>
|
||||
<div>Liegnitzer Straße</div>
|
||||
<div>Forster Straße</div>
|
||||
<div>Ohlauer Straße</div>
|
||||
<div>Lausitzer Straße</div>
|
||||
<div>Manteuffelstraße</div>
|
||||
<div>Mariannenstraße</div>
|
||||
<div>Muskauer Straße</div>
|
||||
<div>Warschauer Straße</div>
|
||||
<div>Revaler Straße</div>
|
||||
<div>Simon-Dach-Straße</div>
|
||||
<div>Boxhagener Platz</div>
|
||||
<div>Kreuzberg</div>
|
||||
<div>Neukölln</div>
|
||||
<div>Bouchéstraße</div>
|
||||
<div>Alt-Treptow</div>
|
||||
<div>Berlin</div>
|
||||
<div>Kiefholzstraße</div>
|
||||
<div>Elsenstraße</div>
|
||||
<div>Harzer Straße</div>
|
||||
<div>Wildenbruchstraße</div>
|
||||
<div>Heidelberger Straße</div>
|
||||
<div>Karl-Kunger-Straße</div>
|
||||
<div>Sülzhayner Straße</div>
|
||||
<div>Schmollerstraße</div>
|
||||
<div>Mengerzeile</div>
|
||||
<div>Am Treptower Park</div>
|
||||
<div>Puschkinallee</div>
|
||||
<div>Martin-Hoffman-Straße</div>
|
||||
<div>Eichenstraße</div>
|
||||
<div>Fanny-Zobel-Straße</div>
|
||||
<div>Matthesstraße</div>
|
||||
<div>Beermannstraße</div>
|
||||
<div>Jordanstraße</div>
|
||||
<div>Lohmühlenstraße</div>
|
||||
<div>Am Flutgraben</div>
|
||||
<div>Schleusenufer</div>
|
||||
<div>Landwehr Canal</div>
|
||||
<div>Spree</div>
|
||||
<div>Treptower Park</div>
|
||||
<div>Rummelsburger See</div>
|
||||
<div>Schlesisches Tor</div>
|
||||
<div>Skalitzer Straße</div>
|
||||
<div>Köpenicker Straße</div>
|
||||
<div>Brückenstraße</div>
|
||||
<div>Rungestraße</div>
|
||||
<div>Ohmstraße</div>
|
||||
<div>Michaelkirchstraße</div>
|
||||
<div>Michaelkirchplatz</div>
|
||||
<div>Heinrich-Heine-Straße</div>
|
||||
<div>Jannowitzbrücke</div>
|
||||
<div>Schlesisches Straße</div>
|
||||
<div>Cuvrystraße</div>
|
||||
<div>Wrangelstraße</div>
|
||||
<div>Taborstraße</div>
|
||||
<div>Heckmannufer</div>
|
||||
<div>Görlitzer Ufer</div>
|
||||
<div>Görlitzer Park</div>
|
||||
<div>Falckenstein Straße</div>
|
||||
<div>Oppelner Straße</div>
|
||||
<div>Sorauer Straße</div>
|
||||
<div>Lübbener Straße</div>
|
||||
<div>Plesser Straße</div>
|
||||
<div>Krüllsstraße</div>
|
||||
<div>Kiehlufer</div>
|
||||
<div>Treptower Straße</div>
|
||||
<div>Hüttenroder Weg</div>
|
||||
<div>Onckenstraße</div>
|
||||
<div>Lexisstraße</div>
|
||||
<div>Grabowstraße</div>
|
||||
<div>Wiener Straße</div>
|
||||
<div>Reichenberger Straße</div>
|
||||
<div>Paul-Lincke-Ufer</div>
|
||||
<div>Glogauer Straße</div>
|
||||
<div>Liegnitzer Straße</div>
|
||||
<div>Forster Straße</div>
|
||||
<div>Ohlauer Straße</div>
|
||||
<div>Lausitzer Straße</div>
|
||||
<div>Manteuffelstraße</div>
|
||||
<div>Mariannenstraße</div>
|
||||
<div>Muskauer Straße</div>
|
||||
<div>Warschauer Straße</div>
|
||||
<div>Revaler Straße</div>
|
||||
<div>Simon-Dach-Straße</div>
|
||||
<div>Boxhagener Platz</div>
|
||||
<div>Kreuzberg</div>
|
||||
<div>Neukölln</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<marquee><i><h1>PLAGUE RAVE 2020 <progress></progress></h1></i></marquee>
|
||||
|
||||
<div class="rel">
|
||||
<img src="img/mask.gif" />
|
||||
<br/>
|
||||
<img src="img/berghain.gif" />
|
||||
</br>
|
||||
<img src="img/TIER-scooter-final-1024x1024.png" />
|
||||
</div>
|
||||
<marquee><i><h1><progress></progress> PLAGUE RAVE 2020</h1></i></marquee>
|
||||
|
||||
<div class="rel">
|
||||
<img src="img/source.gif" />
|
||||
<img class="left" src="img/thumbnail.gif" />
|
||||
<img src="img/Berlin_TV_Tower.png" />
|
||||
</div>
|
||||
|
||||
<marquee><i><h1><progress></progress> PLAGUE RAVE 2020</h1></i></marquee>
|
||||
</div>
|
||||
|
||||
<script type='text/javascript' src='js/jquery-3.5.1.slim.min.js'></script>
|
||||
<script type='text/javascript' src='js/howler.min.js'></script>
|
||||
<script type='text/javascript' src='js/html2canvas.min.js'></script>
|
||||
<script type='text/javascript' src='js/kh.js'></script>
|
||||
</body>
|
||||
</html>
|
4
works/Sarah Grant/otastaImg/data/js/howler.min.js
vendored
Normal file
20
works/Sarah Grant/otastaImg/data/js/html2canvas.min.js
vendored
Normal file
2
works/Sarah Grant/otastaImg/data/js/jquery-3.5.1.slim.min.js
vendored
Normal file
801
works/Sarah Grant/otastaImg/data/js/kh.js
Normal file
@ -0,0 +1,801 @@
|
||||
/* PLAGUE RAVE 2020 by chootka for e-ridin`dirty / OPENCOIL 2020 * Dennis de Bel * Anton ???
|
||||
|
||||
Adapted from kathack.com, Alex Leone, David Nufer, David Truong, © 2011-03-11 */
|
||||
|
||||
var CSS_TRANSFORM = null,
|
||||
CSS_TRANSFORM_ORIGIN = null,
|
||||
POSSIBLE_TRANSFORM_PREFIXES = ['-webkit-', '-moz-', '-o-', '-ms-', ''],
|
||||
khFirst = false,
|
||||
vibrateInterval = null;
|
||||
|
||||
/* When running twice on one page, update pick-uppable nodes instead of
|
||||
* creating more.
|
||||
*/
|
||||
if (!window.khNodes) {
|
||||
khFirst = true;
|
||||
window.khNodes = new StickyNodes();
|
||||
}
|
||||
|
||||
function getCssTransform() {
|
||||
var i, d = document.createElement('div'), pre;
|
||||
for (i = 0; i < POSSIBLE_TRANSFORM_PREFIXES.length; i++) {
|
||||
pre = POSSIBLE_TRANSFORM_PREFIXES[i];
|
||||
d.style.setProperty(pre + 'transform', 'rotate(1rad) scaleX(2)', null);
|
||||
if (d.style.getPropertyValue(pre + 'transform')) {
|
||||
CSS_TRANSFORM = pre + 'transform';
|
||||
CSS_TRANSFORM_ORIGIN = pre + 'transform-origin';
|
||||
return;
|
||||
}
|
||||
}
|
||||
alert("Your browser doesn't support CSS tranforms!");
|
||||
throw "Your browser doesn't support CSS tranforms!";
|
||||
}
|
||||
getCssTransform();
|
||||
|
||||
/**
|
||||
* Vibration support
|
||||
*/
|
||||
|
||||
// Starts vibration at passed in level
|
||||
function startVibrate(duration) {
|
||||
navigator.vibrate(duration);
|
||||
}
|
||||
|
||||
// Stops vibration
|
||||
function stopVibrate() {
|
||||
// Clear interval and stop persistent vibrating
|
||||
if(vibrateInterval) clearInterval(vibrateInterval);
|
||||
navigator.vibrate(0);
|
||||
}
|
||||
|
||||
// Start persistent vibration at given duration and interval
|
||||
// Assumes a number value is given
|
||||
function startPeristentVibrate(duration, interval) {
|
||||
vibrateInterval = setInterval(function() {
|
||||
startVibrate(duration);
|
||||
}, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the circle intersects the element rectangle.
|
||||
* 0 | 1 | 2
|
||||
* ------------------
|
||||
* 3 | 4 | 5
|
||||
* ------------------
|
||||
* 6 | 7 | 9
|
||||
*/
|
||||
function circleGridObjInt(cx, cy, cr, cr2, go) {
|
||||
var dx, dy;
|
||||
if (cx < go.left) {
|
||||
dx = go.left - cx;
|
||||
if (cy < go.top) { /* zone 0. */
|
||||
dy = go.top - cy;
|
||||
return ((dx * dx + dy * dy) <= cr2);
|
||||
} else if (cy <= go.bottom) { /* zone 3. */
|
||||
return (dx <= cr);
|
||||
} else { /* zone 6. */
|
||||
dy = cy - go.bottom;
|
||||
return ((dx * dx + dy * dy) <= cr2);
|
||||
}
|
||||
} else if (cx <= go.right) {
|
||||
if (cy < go.top) { /* zone 1. */
|
||||
return ((go.top - cy) <= cr);
|
||||
} else if (cy <= go.bottom) { /* zone 4. */
|
||||
return true;
|
||||
} else { /* zone 7. */
|
||||
return ((cy - go.bottom) <= cr);
|
||||
}
|
||||
} else {
|
||||
dx = cx - go.right;
|
||||
if (cy < go.top) { /* zone 2. */
|
||||
dy = go.top - cy;
|
||||
return ((dx * dx + dy * dy) <= cr2);
|
||||
} else if (cy <= go.bottom) { /* zone 5. */
|
||||
return (dx <= cr);
|
||||
} else { /* zone 9. */
|
||||
dy = cy - go.bottom;
|
||||
return ((dx * dx + dy * dy) <= cr2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [x,y] where the rectangle is closest to (cx, cy).
|
||||
* 0 | 1 | 2
|
||||
* ------------------
|
||||
* 3 | 4 | 5
|
||||
* ------------------
|
||||
* 6 | 7 | 9
|
||||
*/
|
||||
function getClosestPoint(cx, cy, go) {
|
||||
var dx, dy;
|
||||
if (cx < go.left) {
|
||||
dx = go.left - cx;
|
||||
if (cy < go.top) { /* zone 0. */
|
||||
return [go.left, go.top];
|
||||
} else if (cy <= go.bottom) { /* zone 3. */
|
||||
return [go.left, cy];
|
||||
} else { /* zone 6. */
|
||||
return [go.left, go.bottom];
|
||||
}
|
||||
} else if (cx <= go.right) {
|
||||
if (cy < go.top) { /* zone 1. */
|
||||
return [cx, go.top];
|
||||
} else if (cy <= go.bottom) { /* zone 4. */
|
||||
return [cx, cy];
|
||||
} else { /* zone 7. */
|
||||
return [cx, go.bottom];
|
||||
}
|
||||
} else {
|
||||
dx = cx - go.right;
|
||||
if (cy < go.top) { /* zone 2. */
|
||||
return [go.right, go.top];
|
||||
} else if (cy <= go.bottom) { /* zone 5. */
|
||||
return [go.right, cy];
|
||||
} else { /* zone 9. */
|
||||
return [go.right, go.bottom];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "volume" of the grid object.
|
||||
*/
|
||||
function gridObjVol(go) {
|
||||
return go.w * go.h * Math.min(go.w, go.h);
|
||||
}
|
||||
|
||||
function StickyNodes() {
|
||||
var domNodes = [],
|
||||
grid = [],
|
||||
GRIDX = 100,
|
||||
GRIDY = 100,
|
||||
REPLACE_WORDS_IN = {
|
||||
a: 1, b: 1, big: 1, body: 1, cite:1, code: 1, dd: 1, div: 1,
|
||||
dt: 1, em: 1, font: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1,
|
||||
i: 1, label: 1, legend: 1, li: 1, p: 1, pre: 1, small: 1,
|
||||
span: 1, strong: 1, sub: 1, sup: 1, td: 1, th: 1, tt: 1
|
||||
};
|
||||
|
||||
function addDomNode(el) {
|
||||
if (el !== undefined && el !== null) {
|
||||
el.khIgnore = true;
|
||||
domNodes.push(el);
|
||||
}
|
||||
}
|
||||
this.addDomNode = addDomNode;
|
||||
|
||||
this.addWords = function (el) {
|
||||
var textEls = [];
|
||||
|
||||
function shouldAddChildren(el) {
|
||||
return el.tagName && REPLACE_WORDS_IN[el.tagName.toLowerCase()];
|
||||
}
|
||||
|
||||
function buildTextEls(el, shouldAdd) {
|
||||
var i, len;
|
||||
if (shouldAdd && el.nodeType === Node.TEXT_NODE &&
|
||||
el.nodeValue.trim().length > 0) {
|
||||
textEls.push(el);
|
||||
return;
|
||||
}
|
||||
if (!el.childNodes || el.khIgnore) {
|
||||
return;
|
||||
}
|
||||
shouldAdd = shouldAddChildren(el);
|
||||
for (i = 0, len = el.childNodes.length; i < len; i++) {
|
||||
buildTextEls(el.childNodes[i], shouldAdd);
|
||||
}
|
||||
}
|
||||
|
||||
function wordsToSpans(textEl) {
|
||||
var p = textEl.parentNode,
|
||||
words = textEl.nodeValue.split(/\s+/),
|
||||
ws = textEl.nodeValue.split(/\S+/),
|
||||
i, n, len = Math.max(words.length, ws.length);
|
||||
/* preserve whitespace for pre tags. */
|
||||
if (ws.length > 0 && ws[0].length === 0) {
|
||||
ws.shift();
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i < words.length && words[i].length > 0) {
|
||||
n = document.createElement('span');
|
||||
n.innerHTML = words[i];
|
||||
p.insertBefore(n, textEl);
|
||||
addDomNode(n);
|
||||
}
|
||||
if (i < ws.length && ws[i].length > 0) {
|
||||
n = document.createTextNode(ws[i]);
|
||||
p.insertBefore(n, textEl);
|
||||
}
|
||||
}
|
||||
p.removeChild(textEl);
|
||||
}
|
||||
|
||||
buildTextEls(el, shouldAddChildren(el));
|
||||
textEls.map(wordsToSpans);
|
||||
};
|
||||
|
||||
/* includes el. */
|
||||
this.addTagNames = function (el, tagNames) {
|
||||
var tname = el.tagName && el.tagName.toLowerCase(),
|
||||
i, j, els, len;
|
||||
if (el.khIgnore) {
|
||||
return;
|
||||
}
|
||||
if (tagNames.indexOf(tname) !== -1) {
|
||||
addDomNode(el);
|
||||
}
|
||||
if (!el.getElementsByTagName) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < tagNames.length; i++) {
|
||||
els = el.getElementsByTagName(tagNames[i]);
|
||||
for (j = 0, len = els.length; j < len; j++) {
|
||||
if (!els[j].khIgnore) {
|
||||
addDomNode(els[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.finalize = function (docW, docH) {
|
||||
var xi, yi, i, len, startXI, startYI, el, go, off, w, h,
|
||||
endXI = Math.floor(docW / GRIDX) + 1,
|
||||
endYI = Math.floor(docH / GRIDY) + 1;
|
||||
|
||||
/* initialize grid. */
|
||||
grid = new Array(endXI);
|
||||
for (xi = 0; xi < endXI; xi++) {
|
||||
grid[xi] = new Array(endYI);
|
||||
}
|
||||
|
||||
/* add nodes into grid. */
|
||||
for (i = 0, len = domNodes.length; i < len; i++) {
|
||||
el = domNodes[i];
|
||||
if (el.khPicked) {
|
||||
continue;
|
||||
}
|
||||
off = jQuery(el).offset();
|
||||
w = jQuery(el).width();
|
||||
h = jQuery(el).height();
|
||||
go = {
|
||||
el: domNodes[i], /* dom element. */
|
||||
left: off.left,
|
||||
right: off.left + w,
|
||||
top: off.top,
|
||||
bottom: off.top + h,
|
||||
w: w,
|
||||
h: h,
|
||||
x: off.left + (w / 2), /* center x. */
|
||||
y: off.top + (h / 2), /* center y. */
|
||||
diag: Math.sqrt(((w * w) + (h * h)) / 4), /* center to corner */
|
||||
|
||||
/* these are for removing ourselves from the grid. */
|
||||
arrs: [], /* which arrays we're in (grid[x][y]). */
|
||||
idxs: [] /* what indexes. */
|
||||
};
|
||||
startXI = Math.floor(go.left / GRIDX);
|
||||
startYI = Math.floor(go.top / GRIDY);
|
||||
endXI = Math.floor((go.left + go.w) / GRIDX) + 1;
|
||||
endYI = Math.floor((go.top + go.h) / GRIDY) + 1;
|
||||
for (xi = startXI; xi < endXI; xi++) {
|
||||
for (yi = startYI; yi < endYI; yi++) {
|
||||
if (grid[xi] === undefined) {
|
||||
grid[xi] = [];
|
||||
}
|
||||
if (grid[xi][yi] === undefined) {
|
||||
grid[xi][yi] = [go];
|
||||
} else {
|
||||
grid[xi][yi].push(go);
|
||||
}
|
||||
go.arrs.push(grid[xi][yi]);
|
||||
go.idxs.push(grid[xi][yi].length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function removeGridObj(go) {
|
||||
var i;
|
||||
for (i = 0; i < go.arrs.length; i++) {
|
||||
go.arrs[i][go.idxs[i]] = undefined;
|
||||
}
|
||||
go.el.style.visibility = "hidden";
|
||||
go.el.khPicked = true;
|
||||
delete go.arrs;
|
||||
delete go.idxs;
|
||||
}
|
||||
|
||||
/**
|
||||
* cb(gridObj) -> boolean true if the object should be removed.
|
||||
*/
|
||||
this.removeIntersecting = function (x, y, r, cb) {
|
||||
var xi, yi, arr, i, r2 = r * r, go,
|
||||
startXI = Math.floor((x - r) / GRIDX),
|
||||
startYI = Math.floor((y - r) / GRIDY),
|
||||
endXI = Math.floor((x + r) / GRIDX) + 1,
|
||||
endYI = Math.floor((y + r) / GRIDY) + 1;
|
||||
for (xi = startXI; xi < endXI; xi++) {
|
||||
if (grid[xi] === undefined) {
|
||||
continue;
|
||||
}
|
||||
for (yi = startYI; yi < endYI; yi++) {
|
||||
arr = grid[xi][yi];
|
||||
if (arr === undefined) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
go = arr[i];
|
||||
if (go !== undefined &&
|
||||
circleGridObjInt(x, y, r, r2, go) &&
|
||||
cb(go)) {
|
||||
removeGridObj(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function PlayerBall(parentNode, stickyNodes, ballOpts) {
|
||||
var x = 300, y = 300,
|
||||
vx = 0, vy = 0,
|
||||
radius = 20,
|
||||
lastR = 0, /**< optimization: only resize when necessary. */
|
||||
docW = 10000, docH = 10000,
|
||||
|
||||
attached = [],
|
||||
attachedDiv, /* div to put attached nodes into. */
|
||||
canvas_el,
|
||||
canvas_ctx,
|
||||
color = ballOpts.color,
|
||||
|
||||
accelTargetX = 0, accelTargetY = 0,
|
||||
accel = false,
|
||||
|
||||
VOL_MULT = ballOpts.VOL_MULT,
|
||||
MAX_ATTACHED_VISIBLE = ballOpts.MAX_ATTACHED_VISIBLE,
|
||||
CHECK_VOLS = ballOpts.CHECK_VOLS,
|
||||
|
||||
/**
|
||||
* which direction the ball is facing in the xy axis, in radians.
|
||||
* th: 0 is facing dead East
|
||||
* th: 1/2 PI is facing dead South
|
||||
* note that this is like regular th on a graph with y inverted.
|
||||
* Same rotation as css transform.
|
||||
*/
|
||||
th = 0,
|
||||
|
||||
/**
|
||||
* Ball angle in the rotation axis / z plane, in radians.
|
||||
* phi: 0 is pointing in the direction the ball is rolling.
|
||||
* phi: 1/2 PI is pointing straight up (out of the page).
|
||||
* note that forward rotation means phi -= 0.1.
|
||||
*/
|
||||
phi = 0;
|
||||
|
||||
this.init = function () {
|
||||
canvas_el = document.createElement('canvas');
|
||||
canvas_el.width = radius * 2;
|
||||
canvas_el.height = radius * 2;
|
||||
canvas_el.style.cssText = 'position: absolute; z-index: 500;';
|
||||
parentNode.appendChild(canvas_el);
|
||||
canvas_ctx = canvas_el.getContext('2d');
|
||||
|
||||
attachedDiv = document.createElement('div');
|
||||
// attachedDiv.setAttribute("id", "capture")
|
||||
parentNode.appendChild(attachedDiv);
|
||||
};
|
||||
|
||||
this.setRadius = function (r) {
|
||||
radius = r;
|
||||
};
|
||||
|
||||
this.getState = function () {
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
vx: vx,
|
||||
vy: vy,
|
||||
radius: radius,
|
||||
th: th,
|
||||
phi: phi,
|
||||
};
|
||||
};
|
||||
|
||||
this.setState = function (s) {
|
||||
x = s.x;
|
||||
y = s.y;
|
||||
vx = s.vx;
|
||||
vy = s.vy;
|
||||
radius = s.radius;
|
||||
th = s.th;
|
||||
phi = s.phi;
|
||||
};
|
||||
|
||||
this.setXY = function (sx, sy) {
|
||||
x = sx;
|
||||
y = sy;
|
||||
};
|
||||
|
||||
this.setTh = function (sth) {
|
||||
th = sth;
|
||||
};
|
||||
|
||||
this.setPhi = function (sphi) {
|
||||
phi = sphi;
|
||||
};
|
||||
|
||||
this.setColor = function (c) {
|
||||
color = c;
|
||||
};
|
||||
|
||||
this.setDocSize = function (w, h) {
|
||||
docW = w;
|
||||
docH = h;
|
||||
};
|
||||
|
||||
this.setAccel = function (bool) {
|
||||
accel = bool;
|
||||
};
|
||||
|
||||
this.setAccelTarget = function (tx, ty) {
|
||||
accelTargetX = tx;
|
||||
accelTargetY = ty;
|
||||
};
|
||||
|
||||
function getVol() {
|
||||
return (4 * Math.PI * radius * radius * radius / 3);
|
||||
}
|
||||
|
||||
function grow(go) {
|
||||
var newVol = getVol() + gridObjVol(go) * VOL_MULT;
|
||||
radius = Math.pow(newVol * 3 / (4 * Math.PI), 1 / 3);
|
||||
}
|
||||
|
||||
function attachGridObj(go) {
|
||||
var attXY = getClosestPoint(x, y, go),
|
||||
dx = attXY[0] - x,
|
||||
dy = attXY[1] - y,w
|
||||
r = Math.sqrt(dx * dx + dy * dy),
|
||||
attTh = 0 - th,
|
||||
offLeft = attXY[0] - go.left,
|
||||
offTop = attXY[1] - go.top,
|
||||
offTh = Math.atan2(dy, dx) - th,
|
||||
attX = r * Math.cos(offTh),
|
||||
attY = r * Math.sin(offTh),
|
||||
el = go.el.cloneNode(true),
|
||||
go_jel = jQuery(go.el),
|
||||
newAtt = {
|
||||
el: el,
|
||||
attX: attX,
|
||||
attY: attY,
|
||||
attT: 'translate(' + Math.round(attX) + 'px,' +
|
||||
Math.round(attY) + 'px) ' +
|
||||
'rotate(' + attTh + 'rad)',
|
||||
r: r,
|
||||
offTh: offTh,
|
||||
offPhi: 0 - phi,
|
||||
diag: go.diag,
|
||||
removeR: r + go.diag,
|
||||
visible: false,
|
||||
display: go_jel.css('display')
|
||||
};
|
||||
attached.push(newAtt);
|
||||
// grow(go);
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = (-offLeft) + 'px';
|
||||
el.style.top = (-offTop) + 'px';
|
||||
el.style.setProperty(CSS_TRANSFORM_ORIGIN,
|
||||
offLeft + 'px ' + offTop + 'px', null);
|
||||
el.style.display = 'none';
|
||||
/* copy computed styles from old object. */
|
||||
el.style.color = go_jel.css('color');
|
||||
el.style.textDecoration = go_jel.css('text-decoration');
|
||||
el.style.fontSize = go_jel.css('font-size');
|
||||
el.style.fontWeight = go_jel.css('font-weight');
|
||||
el.khIgnore = true;
|
||||
attachedDiv.appendChild(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the object should be removed from stickyNodes.
|
||||
*/
|
||||
function removeIntCb(go) {
|
||||
if (CHECK_VOLS && gridObjVol(go) > getVol()) {
|
||||
return false;
|
||||
}
|
||||
attachGridObj(go);
|
||||
return true;
|
||||
}
|
||||
|
||||
this.updatePhysics = function () {
|
||||
var oldX = x, oldY = y, dx, dy,
|
||||
bounce = false,
|
||||
accelTh;
|
||||
if (accel) {
|
||||
accelTh = Math.atan2(accelTargetY - y, accelTargetX - x);
|
||||
vx += Math.cos(accelTh) * 0.5;
|
||||
vy += Math.sin(accelTh) * 0.5;
|
||||
} else {
|
||||
vx *= 0.95;
|
||||
vy *= 0.95;
|
||||
}
|
||||
x += vx;
|
||||
y += vy;
|
||||
/* bounce ball on edges of document. */
|
||||
if (x - radius < 0) {
|
||||
bounce = true;
|
||||
x = radius + 1;
|
||||
vx = -vx;
|
||||
} else if (x + radius > docW) {
|
||||
bounce = true;
|
||||
x = docW - radius - 1;
|
||||
vx = -vx;
|
||||
}
|
||||
if (y - radius < 0) {
|
||||
bounce = true;
|
||||
y = radius + 1;
|
||||
vy = -vy;
|
||||
} else if (y + radius > docH) {
|
||||
bounce = true;
|
||||
y = docH - radius - 1;
|
||||
vy = -vy;
|
||||
}
|
||||
if (vx !== 0 || vy !== 0) {
|
||||
th = Math.atan2(vy, vx);
|
||||
dx = x - oldX;
|
||||
dy = y - oldY;
|
||||
/* arclen = th * r, so th = arclen / r. */
|
||||
phi -= Math.sqrt(dx * dx + dy * dy) / radius;
|
||||
}
|
||||
stickyNodes.removeIntersecting(x, y, radius, removeIntCb);
|
||||
this.draw();
|
||||
};
|
||||
|
||||
function drawBall() {
|
||||
var sx1, sy1, sx2, sy2, dx, dy, i, pct1, pct2, z1, z2;
|
||||
/* move/resize canvas element. */
|
||||
canvas_el.style.left = (x - radius) + 'px';
|
||||
canvas_el.style.top = (y - radius) + 'px';
|
||||
if (radius != lastR) {
|
||||
canvas_el.width = 2 * radius + 1;
|
||||
canvas_el.height = 2 * radius + 1;
|
||||
lastR = radius;
|
||||
}
|
||||
/* draw white circle. */
|
||||
canvas_ctx.clearRect(0, 0, 2 * radius, 2 * radius);
|
||||
canvas_ctx.fillStyle = "#ff0000";
|
||||
canvas_ctx.beginPath();
|
||||
canvas_ctx.arc(radius, radius, radius - 1, 0, Math.PI * 2, true);
|
||||
canvas_ctx.fill();
|
||||
/* draw outer border. */
|
||||
canvas_ctx.strokeStyle = color;
|
||||
canvas_ctx.beginPath();
|
||||
canvas_ctx.arc(radius, radius, radius - 1, 0, Math.PI * 2, true);
|
||||
canvas_ctx.stroke();
|
||||
/* draw stripes. */
|
||||
canvas_ctx.fillStyle = color;
|
||||
sx1 = radius + radius * Math.cos(th + Math.PI / 16);
|
||||
sy1 = radius + radius * Math.sin(th + Math.PI / 16);
|
||||
sx2 = radius + radius * Math.cos(th - Math.PI / 16);
|
||||
sy2 = radius + radius * Math.sin(th - Math.PI / 16);
|
||||
dx = (radius + radius * Math.cos(th + Math.PI * 15 / 16)) - sx1;
|
||||
dy = (radius + radius * Math.sin(th + Math.PI * 15 / 16)) - sy1;
|
||||
for (i = 0; i < Math.PI * 2; i += Math.PI / 7) {
|
||||
pct1 = (-Math.cos(phi + i) + 1) / 2;
|
||||
pct2 = (-Math.cos(phi + i + Math.PI / 32) + 1) / 2;
|
||||
z1 = Math.sin(phi + i);
|
||||
z2 = Math.sin(phi + i + Math.PI / 32);
|
||||
if (z1 > 0 && z2 > 0) {
|
||||
canvas_ctx.beginPath();
|
||||
canvas_ctx.moveTo(sx1 + pct1 * dx, sy1 + pct1 * dy);
|
||||
canvas_ctx.lineTo(sx1 + pct2 * dx, sy1 + pct2 * dy);
|
||||
canvas_ctx.lineTo(sx2 + pct2 * dx, sy2 + pct2 * dy);
|
||||
canvas_ctx.lineTo(sx2 + pct1 * dx, sy2 + pct1 * dy);
|
||||
canvas_ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the attached object is roughly visible.
|
||||
*/
|
||||
function drawAttached(att) {
|
||||
var oth = th + att.offTh,
|
||||
ophi = phi + att.offPhi,
|
||||
ox = att.r * Math.cos(oth),
|
||||
oy = att.r * Math.sin(oth),
|
||||
dx = (att.r * Math.cos((th - att.offTh) + Math.PI)) - ox,
|
||||
dy = (att.r * Math.sin((th - att.offTh) + Math.PI)) - oy,
|
||||
pct = (-Math.cos(ophi) + 1) / 2,
|
||||
cx = ox + pct * dx,
|
||||
cy = oy + pct * dy,
|
||||
oz = att.r * Math.sin(ophi);
|
||||
if (oz < 0 && Math.sqrt(cx * cx + cy * cy) + att.diag < radius) {
|
||||
/* hidden behind circle. */
|
||||
if (att.visible) {
|
||||
att.visible = false;
|
||||
att.el.style.display = "none";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* attached node is visible. */
|
||||
if (!att.visible) {
|
||||
att.visible = true;
|
||||
att.el.style.display = att.display;
|
||||
}
|
||||
//att.el.style.zIndex = 500 + Math.round(oz);
|
||||
att.el.style.zIndex = (oz > 0)? 501 : 499;
|
||||
att.el.style.setProperty(
|
||||
CSS_TRANSFORM,
|
||||
'translate(' + x + 'px,' + y + 'px) ' +
|
||||
'rotate(' + th + 'rad) ' +
|
||||
'scaleX(' + Math.cos(ophi) + ') ' +
|
||||
att.attT, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
function onAttachedRemoved(att) {
|
||||
attachedDiv.removeChild(att.el);
|
||||
delete att.el;
|
||||
}
|
||||
|
||||
this.draw = function () {
|
||||
var i, att, numAttachedVisible = 0;
|
||||
drawBall();
|
||||
for (i = attached.length; --i >= 0;) {
|
||||
att = attached[i];
|
||||
if (att.removeR < radius) {
|
||||
attached.splice(i, 1).map(onAttachedRemoved);
|
||||
} else if (drawAttached(att)) {
|
||||
if (++numAttachedVisible > MAX_ATTACHED_VISIBLE) {
|
||||
/* remove older items and stop. */
|
||||
attached.splice(0, i).map(onAttachedRemoved);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function preventDefault(event) {
|
||||
event.preventDefault();
|
||||
event.returnValue = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
function Game(gameDiv, stickyNodes, ballOpts) {
|
||||
var stickyNodes, player1, physicsInterval, resizeInterval, listeners = [];
|
||||
|
||||
player1 = new PlayerBall(gameDiv, stickyNodes, ballOpts, false);
|
||||
player1.init();
|
||||
player1.setXY(300, 300);
|
||||
window.scrollTo(0, 200);
|
||||
|
||||
function on_resize() {
|
||||
player1.setDocSize(jQuery(document).width() - 5,
|
||||
jQuery(document).height() - 5);
|
||||
}
|
||||
on_resize();
|
||||
|
||||
player1.setAccel(true)
|
||||
|
||||
// if (ballOpts.MOUSEB === -5) {
|
||||
document.addEventListener('touchstart', function (event) {
|
||||
if (event.touches.length === 1) {
|
||||
player1.setAccel(true);
|
||||
return preventDefault(event);
|
||||
}
|
||||
}, true);
|
||||
document.addEventListener('touchmove', function (event) {
|
||||
player1.setAccelTarget(event.touches[0].pageX,
|
||||
event.touches[0].pageY);
|
||||
}, true);
|
||||
document.addEventListener('touchend', function (event) {
|
||||
if (event.touches.length === 0) {
|
||||
player1.setAccel(false);
|
||||
return preventDefault(event);
|
||||
}
|
||||
}, true);
|
||||
// }
|
||||
|
||||
// else {
|
||||
// /* mouse buttons */
|
||||
// document.addEventListener('mousemove', function (event) {
|
||||
// player1.setAccelTarget(event.pageX, event.pageY);
|
||||
// }, true);
|
||||
// document.addEventListener('mousedown', function (event) {
|
||||
// if (event.button === ballOpts.MOUSEB) {
|
||||
// player1.setAccel(true);
|
||||
// return preventDefault(event);
|
||||
// }
|
||||
// }, true);
|
||||
// document.addEventListener('mouseup', function (event) {
|
||||
// if (event.button === ballOpts.MOUSEB) {
|
||||
// player1.setAccel(false);
|
||||
// return preventDefault(event);
|
||||
// }
|
||||
// }, true);
|
||||
// }
|
||||
|
||||
physicsInterval = setInterval(function () {
|
||||
player1.accelTargetX = Math.random()*window.innerHeight;
|
||||
player1.accelTargetY = Math.random()*window.innerWidth;
|
||||
player1.updatePhysics();
|
||||
}, 25);
|
||||
resizeInterval = setInterval(on_resize, 1000);
|
||||
}
|
||||
|
||||
function whenAllLoaded(gameDiv, stickyNodes) {
|
||||
stickyNodes.finalize(jQuery(document).width(), jQuery(document).height());
|
||||
|
||||
var game, ballOpts;
|
||||
ballOpts = {
|
||||
color: 0x00FF00,
|
||||
VOL_MULT: 1.0,
|
||||
MAX_ATTACHED_VISIBLE: 9000,
|
||||
CHECK_VOLS: false,
|
||||
MOUSEB: 0 // 0 FOR DESKTOP OR -5 FOR MOBILE
|
||||
};
|
||||
game = new Game(gameDiv, stickyNodes, ballOpts);
|
||||
|
||||
// Download image
|
||||
var btn = document.getElementById('imgButton');
|
||||
btn.addEventListener("touchend", function(event) {
|
||||
console.log("capture", document.getElementById("capture"));
|
||||
html2canvas(document.getElementById("capture")).then(function(canvas) {
|
||||
var link = document.createElement("a");
|
||||
document.body.appendChild(link);
|
||||
link.download = "ridindirtyberlin.png";
|
||||
link.href = canvas.toDataURL("image/png");
|
||||
link.target = '_blank';
|
||||
link.click();
|
||||
});
|
||||
})
|
||||
|
||||
startPeristentVibrate(300, 430);
|
||||
}
|
||||
|
||||
function main() {
|
||||
var gameDiv, checkInterval, stickyNodes, dlbtn;
|
||||
|
||||
gameDiv = document.createElement('div');
|
||||
gameDiv.khIgnore = true;
|
||||
dlbtn = document.getElementById("button-wrapper");
|
||||
dlbtn.khIgnore = true;
|
||||
|
||||
document.body.appendChild(gameDiv);
|
||||
|
||||
setTimeout(function () {
|
||||
var i, len, el;
|
||||
window.khNodes.addWords(document.body);
|
||||
for (i = 0, len = document.body.childNodes.length; i < len; i++) {
|
||||
el = document.body.childNodes[i];
|
||||
window.khNodes.addTagNames(el, [
|
||||
'canvas', 'iframe', 'img', 'input', 'select',
|
||||
'textarea', 'video', 'marquee', 'progress'
|
||||
]);
|
||||
}
|
||||
|
||||
checkInterval = setInterval(function () {
|
||||
if (window.jQuery) {
|
||||
clearInterval(checkInterval);
|
||||
whenAllLoaded(gameDiv, window.khNodes);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (!window.noMain) {
|
||||
main();
|
||||
|
||||
var sound = new Howl({
|
||||
src: ['snd/gabba140.mp3'],
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
volume: 1
|
||||
});
|
||||
}
|
BIN
works/Sarah Grant/otastaImg/data/snd/gabba140.mp3
Normal file
98
works/Sarah Grant/otastaImg/otastaImg.ino
Normal file
@ -0,0 +1,98 @@
|
||||
// Captive portal with (arduino) OTA + SPIFFS
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h> // Over-the-Air updates
|
||||
#include <ESP8266WebServer.h>
|
||||
#include "./DNSServer.h" // Dns server
|
||||
#include <FS.h> // SPIFFS
|
||||
|
||||
DNSServer dnsServer;
|
||||
const byte DNS_PORT = 53;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "Plague Rave 2020"
|
||||
//#define STASPSK "mypassword"
|
||||
#endif
|
||||
|
||||
IPAddress apIP(192, 168, 4, 1);
|
||||
const char* ssid = STASSID;
|
||||
//const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP(ssid);
|
||||
dnsServer.start(DNS_PORT, "*", apIP); // redirect dns request to AP ip
|
||||
|
||||
MDNS.begin("opencoil", WiFi.softAPIP());
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
//Over-the-Air updates
|
||||
ArduinoOTA.setHostname("opencoil");
|
||||
//ArduinoOTA.setPassword("change-me"); //disabled to allow data uploads
|
||||
ArduinoOTA.begin();
|
||||
SPIFFS.begin();
|
||||
|
||||
//redirect all traffic to index.html
|
||||
server.onNotFound([]() {
|
||||
if(!handleFileRead(server.uri())){
|
||||
const char *metaRefreshStr = "<head><meta http-equiv=\"refresh\" content=\"0; url=http://192.168.4.1/index.html\" /></head><body><p>redirecting...</p></body>";
|
||||
server.send(200, "text/html", metaRefreshStr);
|
||||
}
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
delay(50);
|
||||
}
|
||||
|
||||
|
||||
String getContentType(String filename){
|
||||
if(server.hasArg("download")) return "application/octet-stream";
|
||||
else if(filename.endsWith(".htm")) return "text/html";
|
||||
else if(filename.endsWith(".html")) return "text/html";
|
||||
else if(filename.endsWith(".css")) return "text/css";
|
||||
else if(filename.endsWith(".js")) return "application/javascript";
|
||||
else if(filename.endsWith(".png")) return "image/png";
|
||||
else if(filename.endsWith(".gif")) return "image/gif";
|
||||
else if(filename.endsWith(".jpg")) return "image/jpeg";
|
||||
else if(filename.endsWith(".ico")) return "image/x-icon";
|
||||
else if(filename.endsWith(".xml")) return "text/xml";
|
||||
else if(filename.endsWith(".mp4")) return "video/mp4";
|
||||
else if(filename.endsWith(".pdf")) return "application/x-pdf";
|
||||
else if(filename.endsWith(".zip")) return "application/x-zip";
|
||||
else if(filename.endsWith(".gz")) return "application/x-gzip";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
//Given a file path, look for it in the SPIFFS file storage. Returns true if found, returns false if not found.
|
||||
bool handleFileRead(String path){
|
||||
if(path.endsWith("/")) path += "index.html";
|
||||
String contentType = getContentType(path);
|
||||
String pathWithGz = path + ".gz";
|
||||
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
|
||||
if(SPIFFS.exists(pathWithGz))
|
||||
path += ".gz";
|
||||
File file = SPIFFS.open(path, "r");
|
||||
size_t sent = server.streamFile(file, contentType);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|