First commit
This commit is contained in:
parent
7f4c43662a
commit
0528576418
143
README.md
143
README.md
@ -1,3 +1,142 @@
|
||||
# jamidi
|
||||
|
||||
Midi exchanges over websocket for webpages, midi devices/sofware,...
|
||||
Jamidi v0.1b
|
||||
By llstr, Sam Neurohack
|
||||
|
||||
LICENCE : CC NC
|
||||
|
||||
Midi and more exchanges over LAN/Internet
|
||||
|
||||
Imagine Bob own a TR 808 at home and Alice a nozoid OCS-2. They can control each other devices in a webpage or a midi controller.
|
||||
If John has a mutliple encoders midi controller at home and want to control Alice and Bob devices, all changes made by John will be displayed to everyone and played by devices.
|
||||
|
||||
More you can use also a vcvrack complex patch to drive light fixtures, laser abstract generators, midi instruments, wherever they are...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Jamidi features
|
||||
|
||||
- Jamidi vs rtpmidi ? rtpmidi is transport efficiency, but bound to midi specifications. Jamidi support midi cc and note but is more OSC style : you can add any type of "command".
|
||||
|
||||
- Jamidi is websocket based, so accept anything (webpage, home made client,...).
|
||||
|
||||
- Jamidi doesn't broadcast audio. Use whatever solution you like. We use icecast and VLC to listen.
|
||||
|
||||
- Jamidi is experimental and nowhere safe. You need to understand safety risks with opening a network port, especially over Internet. Ask someone who knows if you don't.
|
||||
|
||||
|
||||
|
||||
|
||||
# How it work : Websocket
|
||||
|
||||
|
||||
Websocket transport a string like :
|
||||
|
||||
/ocs2/cc/2 0
|
||||
|
||||
or
|
||||
|
||||
/tr808/note/1
|
||||
|
||||
cc : is a "command". Currently cc, reset (highly specific to nozoid synthetiser). You can add any tyoe of "command".
|
||||
ocs2 : is a "device", that must be described, follow examples in jamidi.json. Here the Alice OCS2 will reveive broadcasted midi informations.
|
||||
|
||||
/ocs2 and /tr808 changes made by John will be displayed to everyone and played by devices.
|
||||
|
||||
|
||||
Websocket default port is 8081 but one can change.
|
||||
|
||||
|
||||
|
||||
|
||||
# How it work : Server
|
||||
|
||||
Will receive all "commands" from all clients, forward them to local devices and broadcast them too.
|
||||
|
||||
|
||||
Options :
|
||||
|
||||
servername : 'local', 'llstrvpn'. Servers (IP, port,...) must be described in jamidi.json
|
||||
|
||||
--broadcast : Broadcast all incomings commands to all client. Default option.
|
||||
--no-broadcast : Do not broadcast all incomings commands to all client.
|
||||
|
||||
--reset : Send reset values to local device a startup. Default option.
|
||||
--no-reset : Do not send reset values to local device a startup.
|
||||
|
||||
--current : Send all current CC values to all new client. Default option.
|
||||
--no-current : Do not send all current CC values to all new client.
|
||||
|
||||
|
||||
|
||||
# How it work : Clients
|
||||
|
||||
Can be webpages or midi instrument/software. Multiple clients types is supported.
|
||||
|
||||
|
||||
Options :
|
||||
|
||||
servername : Remote server 'local', 'xrkia' ('local' by default). Servers must be described in jamidi.json
|
||||
default : Network <-> default midi device (True or False)
|
||||
|
||||
Some "rules" are available. Say you want all network incoming midi CC 1 channel 0 goes to a specific device on channel 3 CC 48. Rules must be described in rules.json
|
||||
Each rule may happen at all time or only during a "song".
|
||||
|
||||
|
||||
|
||||
|
||||
# Webpages examples
|
||||
|
||||
1/ Run :
|
||||
|
||||
python3 mainwip.py
|
||||
|
||||
2/ In a browser open 2 instances of each of these :
|
||||
|
||||
indexaurora.html is a three rotating encoders example.
|
||||
mmo3.html and ocs2.html demo to control 2 real life nozoids.
|
||||
|
||||
3/ Each aurora pages talk to each other. Each mmo3 to each other and so on..
|
||||
|
||||
|
||||
These pages will work with local jamidi server, but they can't guess your online configuration. You need to modify IP and port in line :
|
||||
|
||||
var LJ = 'ws://127.0.0.1:8081/'
|
||||
|
||||
|
||||
|
||||
# Midi instrument usage
|
||||
|
||||
1/ You need to edit jamidi.json and follow ocs2 example.
|
||||
|
||||
2/ Create a client/webpage that will send /ocs2/xxx
|
||||
|
||||
3/ Run :
|
||||
|
||||
python3 mainwip.py
|
||||
|
||||
4/
|
||||
|
||||
|
||||
|
||||
|
||||
# Install
|
||||
|
||||
You need python3 and pip3
|
||||
|
||||
sudo apt-get install python3-pip
|
||||
|
||||
pip3 install python-rtmidi (sudo apt install libasound2-dev, sudo apt install libjack-dev)
|
||||
|
||||
pip3 install mido
|
||||
|
||||
pip3 install numpy
|
||||
|
||||
|
||||
for Websocket client :
|
||||
Download : https://files.pythonhosted.org/packages/8b/0f/52de51b9b450ed52694208ab952d5af6ebbcbce7f166a48784095d930d8c/websocket_client-0.57.0.tar.gz
|
||||
|
||||
Decompress
|
||||
|
||||
python3 setup.py install
|
||||
|
213
client.py
Normal file
213
client.py
Normal file
@ -0,0 +1,213 @@
|
||||
#!/usr/bin/python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
'''
|
||||
NozoidUI Client v0.0.1
|
||||
|
||||
Input : local midi instruments
|
||||
Output : nozoidtUI server
|
||||
|
||||
|
||||
|
||||
Websocket INSTALLER
|
||||
|
||||
|
||||
https://files.pythonhosted.org/packages/8b/0f/52de51b9b450ed52694208ab952d5af6ebbcbce7f166a48784095d930d8c/websocket_client-0.57.0.tar.gz
|
||||
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
print ""
|
||||
print ""
|
||||
print "NozoidUI Client"
|
||||
print "v0.0.1"
|
||||
|
||||
from multiprocessing import Process, Queue, TimeoutError
|
||||
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
import os
|
||||
import time
|
||||
|
||||
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
|
||||
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
|
||||
import midi3
|
||||
|
||||
# from websocket_server import WebsocketServer
|
||||
#import socket
|
||||
import types, time
|
||||
import websocket
|
||||
|
||||
|
||||
try:
|
||||
import thread
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
import time
|
||||
|
||||
debug = 1
|
||||
|
||||
#
|
||||
# webUI server
|
||||
#
|
||||
|
||||
serverIP = "xrkia.org"
|
||||
# serverIP = "127.0.0.1"
|
||||
# serverIP = "10.8.0.46"
|
||||
wsPORT = 8081
|
||||
|
||||
|
||||
#
|
||||
# Midi part
|
||||
#
|
||||
|
||||
nozmidi = "BCR2000 Port 1"
|
||||
# nozmidi = "Arturia BeatStep"
|
||||
# nozmidi = "Virtual Midi A"
|
||||
# nozmidi = "Virtual Sequencer"
|
||||
# nowmidi = "IAC Driver Sequencer Bus 1"
|
||||
# nozmidi = "UM-ONE:UM-ONE MIDI 1 20:0"
|
||||
|
||||
midichanOCS2 = 2
|
||||
midichanMMO3 = 1
|
||||
|
||||
# resetMMO3 = [64,64,0,32,96] # un truc comme ca pour les valeurs de reset ?
|
||||
resetMMO3 = [0] * 32
|
||||
resetOCS2 = [0] * 32
|
||||
|
||||
# /cc cc number value
|
||||
def cc(midichannel, ccnumber, value, mididest):
|
||||
|
||||
print "NozoidUI Sending Midi channel", midichannel, "cc", ccnumber, "value", value, "to", mididest
|
||||
#if mididest == "BCR2000 Port 1":
|
||||
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1, ccnumber, value], mididest)
|
||||
|
||||
|
||||
# /reset nozoids with "default" values
|
||||
def reset(nozoid):
|
||||
|
||||
print ""
|
||||
print "reseting", nozoid
|
||||
|
||||
if nozoid == "mmo3":
|
||||
for ccnumber in xrange(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichanMMO3-1, ccnumber, resetMMO3[ccnumber]], nozmidi)
|
||||
sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber]))
|
||||
else:
|
||||
for ccnumber in xrange(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichanOCS2-1, ccnumber, resetOCS2[ccnumber]], nozmidi)
|
||||
sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber]))
|
||||
|
||||
#
|
||||
# Websocket part
|
||||
#
|
||||
|
||||
def on_error(ws, error):
|
||||
print(error)
|
||||
|
||||
def on_close(ws):
|
||||
print("### closed ###")
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
|
||||
def run(*args):
|
||||
|
||||
try:
|
||||
|
||||
while True:
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
finally:
|
||||
ws.close()
|
||||
print("thread terminating...")
|
||||
|
||||
|
||||
thread.start_new_thread(run, ())
|
||||
|
||||
def on_message(ws, message):
|
||||
#
|
||||
print ""
|
||||
print(message)
|
||||
if len(message) > 200:
|
||||
message = message[:200]+'..'
|
||||
|
||||
oscpath = message.split(" ")
|
||||
if debug > 0:
|
||||
#print "Client got from WS", client['id'], "said :", message, "splitted in an oscpath :", oscpath
|
||||
print "Client got from WS said :", message, "splitted in an oscpath :", oscpath
|
||||
|
||||
wscommand = oscpath[0].split("/")
|
||||
print "WS command was :",wscommand
|
||||
|
||||
if len(oscpath) == 1:
|
||||
args[0] = "noargs"
|
||||
#print "noargs command"
|
||||
|
||||
elif wscommand[2] == "cc":
|
||||
if wscommand[1] == "ocs2":
|
||||
print "Incoming OCS-2 WS"
|
||||
cc(midichanOCS2, int(wscommand[3]), int(oscpath[1]), nozmidi)
|
||||
else:
|
||||
print "Incoming MMO-3 WS"
|
||||
cc(midichanMMO3, int(wscommand[3]), int(oscpath[1]), nozmidi)
|
||||
|
||||
|
||||
elif wscommand[2] == "reset":
|
||||
if wscommand[1] == "ocs2":
|
||||
reset("ocs2")
|
||||
else:
|
||||
reset("mmo3")
|
||||
|
||||
|
||||
# if needed a loop back : WS Client -> server -> WS Client
|
||||
# sendWSall(message)
|
||||
|
||||
|
||||
|
||||
print "Running...."
|
||||
|
||||
# Main loop do nothing. Maybe do the webui server ?
|
||||
try:
|
||||
|
||||
print ""
|
||||
print "Connecting to NozoidUI server..."
|
||||
print "at", serverIP, "port",wsPORT
|
||||
|
||||
#websocket.enableTrace(True)
|
||||
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
|
||||
on_message = on_message,
|
||||
on_error = on_error,
|
||||
on_close = on_close)
|
||||
|
||||
midi3.ws = ws
|
||||
midi3.wsmode = True
|
||||
|
||||
print "Midi Configuration..."
|
||||
print "Midi Destination", nozmidi
|
||||
|
||||
midi3.check()
|
||||
|
||||
ws.on_open = on_open
|
||||
ws.run_forever()
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
# Gently stop on CTRL C
|
||||
|
||||
print "Fin de NozoidUI."
|
||||
|
||||
|
||||
|
||||
|
64
jamidi.json
Normal file
64
jamidi.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"local" :
|
||||
[
|
||||
{
|
||||
"_comment": "Server is localhost",
|
||||
"type": "serverconf",
|
||||
"name": "local",
|
||||
"IP": "127.0.0.1",
|
||||
"port": 8081
|
||||
}
|
||||
],
|
||||
|
||||
"llstrvpn" :
|
||||
[
|
||||
{
|
||||
"_comment": "Server is llstrvpn",
|
||||
"type": "serverconf",
|
||||
"name": "llstrvpn",
|
||||
"IP": "10.8.0.46",
|
||||
"port": 8081
|
||||
}
|
||||
],
|
||||
|
||||
"xkkia" :
|
||||
[
|
||||
{
|
||||
"_comment": "Server is xrkia.org",
|
||||
"type": "serverconf",
|
||||
"name": "xrkia",
|
||||
"IP": "xrkia.org",
|
||||
"port": 8081
|
||||
}
|
||||
],
|
||||
|
||||
"ocs2": [
|
||||
{
|
||||
"_comment": "OCS-2 parameters",
|
||||
"type": "mididevice",
|
||||
"mididevice": "UM-ONE:UM-ONE MIDI 1 20:0",
|
||||
"midichan" : 2
|
||||
}
|
||||
],
|
||||
|
||||
"mmo3": [
|
||||
{
|
||||
"_comment": "MMO-3 parameters",
|
||||
"type": "mididevice",
|
||||
"mididevice": "UM-ONE:UM-ONE MIDI 1 20:0",
|
||||
"midichan" : 1
|
||||
}
|
||||
],
|
||||
|
||||
"default": [
|
||||
{
|
||||
"_comment": "Client : default midi device",
|
||||
"type": "mididevice",
|
||||
"mididevice": "BCR2000 Port 1",
|
||||
"midichan" : 1
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
|
||||
}
|
308
main.py
Normal file
308
main.py
Normal file
@ -0,0 +1,308 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
'''
|
||||
Jamidi Server v0.1b
|
||||
|
||||
'''
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("Jamidi Server")
|
||||
print("v0.1b")
|
||||
|
||||
#from multiprocessing import Process, Queue, TimeoutError
|
||||
#import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
import os
|
||||
import time
|
||||
|
||||
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
|
||||
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
|
||||
import midi3
|
||||
|
||||
from websocket_server import WebsocketServer
|
||||
#import socket
|
||||
import types, time, json
|
||||
import argparse
|
||||
|
||||
debug = 1
|
||||
|
||||
|
||||
print ("")
|
||||
print ("Arguments parsing if needed...")
|
||||
argsparser = argparse.ArgumentParser(description="Jamidi Server v0.1b commands help mode")
|
||||
argsparser.add_argument("-s","--servername",help="Servername: 'local', 'llstrvpn' (local by default)", type=str)
|
||||
argsparser.add_argument('--current',help="Send all current CC values to all new client. Default option" , dest='current', action='store_true')
|
||||
argsparser.add_argument('--no-current',help="Do not send all current CC values to all new client. ", dest='current', action='store_false')
|
||||
argsparser.add_argument('--broadcast',help="Broadcast all incomings commands to all client. Default option" , dest='broadcast', action='store_true')
|
||||
argsparser.add_argument('--no-broadcast',help="Do not broadcast all incomings commands to all client", dest='broadcast', action='store_false')
|
||||
argsparser.add_argument('--reset',help="Send reset values to local device a startup. Default option" , dest='reset', action='store_true')
|
||||
argsparser.add_argument('--no-reset',help="Do not send reset values to local device a startup.", dest='reset', action='store_false')
|
||||
argsparser.set_defaults(reset=True)
|
||||
argsparser.set_defaults(current=True)
|
||||
argsparser.set_defaults(broadcast=True)
|
||||
|
||||
args = argsparser.parse_args()
|
||||
|
||||
# Mode
|
||||
if args.servername:
|
||||
servername = args.servername
|
||||
else:
|
||||
servername = "local"
|
||||
|
||||
# Broadcast commands to all clients ?
|
||||
if args.broadcast == False:
|
||||
print("Broadcast disabled")
|
||||
broadcast = False
|
||||
else:
|
||||
print("Broadcast enabled")
|
||||
broadcast = True
|
||||
|
||||
# Send current values to all new client ?
|
||||
if args.current == False:
|
||||
print("Do not send current values at startup disabled")
|
||||
current = False
|
||||
else:
|
||||
print("Current values update at startup disabled")
|
||||
current = True
|
||||
|
||||
|
||||
# reset = [64,64,0,32,96] # un truc comme ca pour les valeurs de reset ?
|
||||
resetMMO3 = [0] * 32
|
||||
resetOCS2 = [0] * 32
|
||||
|
||||
# record current values
|
||||
crtvalueMMO3 = [0] * 32
|
||||
crtvalueOCS2 = [0] * 32
|
||||
|
||||
# record number of loaded pages (aka client id or players)
|
||||
Players=0
|
||||
|
||||
|
||||
#
|
||||
# Midi part
|
||||
#
|
||||
|
||||
|
||||
print("Midi Configuration...")
|
||||
# print("Midi Destination", nozmidi)
|
||||
|
||||
midi3.check()
|
||||
|
||||
def GetTime():
|
||||
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
|
||||
|
||||
|
||||
|
||||
# /cc cc number value
|
||||
def cc(midichannel, ccnumber, value, mididest):
|
||||
|
||||
if debug>0:
|
||||
print("Jamidi Sending Midi channel", midichannel, "cc", ccnumber, "value", value, "to", mididest)
|
||||
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1, ccnumber, value], mididest)
|
||||
|
||||
|
||||
# /reset nozoids with "default" values
|
||||
def reset(nozoid):
|
||||
|
||||
print("")
|
||||
print(GetTime(),"reseting", nozoid)
|
||||
|
||||
if nozoid == "mmo3":
|
||||
for ccnumber in range(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+Confs["mmo3"][0]["midichan"]-1, ccnumber, resetMMO3[ccnumber]], Confs["mmo3"][0]["mididevice"])
|
||||
sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber]))
|
||||
crtvalueMMO3[ccnumber]=resetMMO3[ccnumber]
|
||||
else:
|
||||
for ccnumber in range(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+Confs["ocs2"][0]["midichan"]-1, ccnumber, resetOCS2[ccnumber]], Confs["ocs2"][0]["mididevice"])
|
||||
sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(resetOCS2[ccnumber]))
|
||||
crtvalueOCS2[ccnumber]=resetOCS2[ccnumber]
|
||||
print("End of reset for", nozoid)
|
||||
print("")
|
||||
|
||||
# /send all current cc values
|
||||
def sendallcurrentccvalues(nozoid):
|
||||
|
||||
if broadcast == True:
|
||||
#print ""
|
||||
print(GetTime(),"sending all current cc values of", nozoid)
|
||||
|
||||
if nozoid == "mmo3":
|
||||
for ccnumber in range(0,32):
|
||||
sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(crtvalueMMO3[ccnumber]))
|
||||
else:
|
||||
for ccnumber in range(0,32):
|
||||
sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(crtvalueOCS2[ccnumber]))
|
||||
#
|
||||
# Settings from jamidi.json
|
||||
#
|
||||
|
||||
# Load midi routing definitions in clientconfr.json
|
||||
def LoadConfs():
|
||||
global Confs, nbmidiconf
|
||||
|
||||
if os.path.exists('jamidi.json'):
|
||||
f=open("jamidi.json","r")
|
||||
|
||||
s = f.read()
|
||||
Confs = json.loads(s)
|
||||
|
||||
|
||||
# return midi confname number for given type 'Specials', 'cc2cc'
|
||||
def findConfs(confname,conftype):
|
||||
|
||||
#print("searching", midiconfname,'...')
|
||||
position = -1
|
||||
for counter in range(len(Confs[conftype])):
|
||||
if confname == Confs[conftype][counter]['name']:
|
||||
#print(confname, "is ", counter)
|
||||
position = counter
|
||||
return position
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Websocket part
|
||||
#
|
||||
|
||||
# Called for every WS client connecting (after handshake)
|
||||
def new_client(client, wserver):
|
||||
|
||||
global Players
|
||||
|
||||
print(GetTime(),"New WS client connected and was given id %d" % client['id'])
|
||||
#sendWSall("/status Hello %d" % client['id'])
|
||||
if current == True:
|
||||
sendallcurrentccvalues("mmo3")
|
||||
sendallcurrentccvalues("ocs2")
|
||||
|
||||
Players+=1
|
||||
sendWSall("/status Hello %d" %(client['id']))
|
||||
sendWSall("/players %d" %(Players))
|
||||
|
||||
|
||||
# Called for every WS client disconnecting
|
||||
def client_left(client, wserver):
|
||||
|
||||
global Players
|
||||
|
||||
try:
|
||||
print(GetTime(),"WS Client(%d) disconnected" % client['id'])
|
||||
Players-=1
|
||||
sendWSall("/players %d" %(Players))
|
||||
except:
|
||||
print("Something weird if coming from",client,"on the wire...")
|
||||
pass
|
||||
|
||||
|
||||
# Called for each WS received message.
|
||||
def message_received(client, wserver, message):
|
||||
|
||||
print("")
|
||||
if len(message) > 200:
|
||||
message = message[:200]+'..'
|
||||
|
||||
oscpath = message.split(" ")
|
||||
if debug > 0:
|
||||
print(GetTime(),"Main got from WS", client['id'], "said :", message, "splitted in an oscpath :", oscpath)
|
||||
else:
|
||||
print(GetTime(),"Main got WS Client", client['id'], "said :", message)
|
||||
|
||||
# wscommand will be like ['', 'ocs2', 'cc', '9']
|
||||
wscommand = oscpath[0].split("/")
|
||||
if debug > 0:
|
||||
print("wscommand :",wscommand)
|
||||
|
||||
if len(oscpath) == 1:
|
||||
args[0] = "noargs"
|
||||
#print "noargs command"
|
||||
|
||||
# like /ocs2/cc/2 127
|
||||
elif wscommand[2] == "cc":
|
||||
ccvr=int(wscommand[3]) #cc variable
|
||||
ccvl=int(oscpath[1]) #cc value
|
||||
if debug > 0:
|
||||
print("ccvr=%d/ccvl=%d"%(ccvr,ccvl))
|
||||
if wscommand[1] == "ocs2":
|
||||
#cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"])
|
||||
crtvalueOCS2[ccvr]=ccvl
|
||||
else:
|
||||
#cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"])
|
||||
crtvalueMMO3[ccvr]=ccvl
|
||||
|
||||
cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"])
|
||||
|
||||
# like /ocs2/reset 1
|
||||
elif wscommand[2] == "reset":
|
||||
if wscommand[1] == "ocs2":
|
||||
reset("ocs2")
|
||||
else:
|
||||
reset("mmo3")
|
||||
|
||||
#print ""
|
||||
|
||||
# if needed a loop back : WS Client -> server -> WS Client
|
||||
sendWSall(message)
|
||||
|
||||
'''
|
||||
def handle_timeout(self):
|
||||
self.timed_out = True
|
||||
'''
|
||||
|
||||
def sendWSall(message):
|
||||
|
||||
if broadcast == True:
|
||||
if debug >0:
|
||||
print(GetTime(),"sending to all %s" % (message))
|
||||
|
||||
wserver.send_message_to_all(message)
|
||||
|
||||
|
||||
LoadConfs()
|
||||
|
||||
serverIP = Confs[servername][0]["IP"]
|
||||
wsPORT = Confs[servername][0]["port"]
|
||||
|
||||
print("Running....")
|
||||
|
||||
# Main loop do nothing. Maybe do the webui server ?
|
||||
try:
|
||||
#while True:
|
||||
|
||||
# Websocket startup
|
||||
wserver = WebsocketServer(wsPORT,host=serverIP)
|
||||
|
||||
#print wserver
|
||||
print("")
|
||||
print(GetTime(),"Launching Jamidi Websocket server...")
|
||||
print(GetTime(),"at", serverIP, "port",wsPORT)
|
||||
wserver.set_fn_new_client(new_client)
|
||||
wserver.set_fn_client_left(client_left)
|
||||
wserver.set_fn_message_received(message_received)
|
||||
|
||||
if reset == True:
|
||||
reset("mmo3")
|
||||
reset("ocs2")
|
||||
|
||||
|
||||
#print ""
|
||||
print(GetTime(),"WS server running forever...")
|
||||
|
||||
wserver.run_forever()
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
# Gently stop on CTRL C
|
||||
|
||||
|
||||
print("Fin de Jamidi.")
|
||||
|
||||
|
||||
|
||||
|
488
midi3.py
Normal file
488
midi3.py
Normal file
@ -0,0 +1,488 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Midi3 light version for soundt/Jamidi/clapt
|
||||
v0.7.0
|
||||
|
||||
Midi Handler :
|
||||
|
||||
- Hook to the MIDI host
|
||||
- Enumerate connected midi devices and spawn a process/device to handle incoming events
|
||||
|
||||
by Sam Neurohack
|
||||
from /team/laser
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import rtmidi
|
||||
from rtmidi.midiutil import open_midiinput
|
||||
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
|
||||
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
|
||||
import mido
|
||||
from mido import MidiFile
|
||||
|
||||
import traceback
|
||||
import weakref
|
||||
import sys
|
||||
from sys import platform
|
||||
import os
|
||||
|
||||
|
||||
is_py2 = sys.version[0] == '2'
|
||||
if is_py2:
|
||||
from queue import Queue
|
||||
from OSC import OSCServer, OSCClient, OSCMessage
|
||||
else:
|
||||
from queue import Queue
|
||||
from OSC3 import OSCServer, OSCClient, OSCMessage
|
||||
|
||||
|
||||
print("")
|
||||
|
||||
midiname = ["Name"] * 16
|
||||
midiport = [rtmidi.MidiOut() for i in range(16) ]
|
||||
|
||||
OutDevice = []
|
||||
InDevice = []
|
||||
|
||||
# max 16 midi port array
|
||||
|
||||
midinputsname = ["Name"] * 16
|
||||
midinputsqueue = [Queue() for i in range(16) ]
|
||||
midinputs = []
|
||||
|
||||
debug = 0
|
||||
|
||||
# False = server / True = Client
|
||||
wsmode = False
|
||||
|
||||
#Mser = False
|
||||
|
||||
MidInsNumber = 0
|
||||
|
||||
|
||||
clock = mido.Message(type="clock")
|
||||
|
||||
start = mido.Message(type ="start")
|
||||
stop = mido.Message(type ="stop")
|
||||
ccontinue = mido.Message(type ="continue")
|
||||
reset = mido.Message(type ="reset")
|
||||
songpos = mido.Message(type ="songpos")
|
||||
|
||||
#mode = "maxwell"
|
||||
|
||||
'''
|
||||
print "clock",clock)
|
||||
print "start",start)
|
||||
print "continue", ccontinue)
|
||||
print "reset",reset)
|
||||
print "sonpos",songpos)
|
||||
'''
|
||||
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
# Python 3
|
||||
Exception = Exception
|
||||
|
||||
|
||||
STATUS_MAP = {
|
||||
'noteon': NOTE_ON,
|
||||
'noteoff': NOTE_OFF,
|
||||
'programchange': PROGRAM_CHANGE,
|
||||
'controllerchange': CONTROLLER_CHANGE,
|
||||
'pitchbend': PITCH_BEND,
|
||||
'polypressure': POLY_PRESSURE,
|
||||
'channelpressure': CHANNEL_PRESSURE
|
||||
}
|
||||
|
||||
|
||||
|
||||
notes = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
|
||||
def midi2note(midinote):
|
||||
|
||||
print("midinote",midinote, "note", notes[midinote%12]+str(round(midinote/12)))
|
||||
return notes[midinote%12]+str(round(midinote/12))
|
||||
|
||||
|
||||
#
|
||||
# MIDI Startup and handling
|
||||
#
|
||||
|
||||
mqueue = Queue()
|
||||
inqueue = Queue()
|
||||
|
||||
#
|
||||
# Events from Generic MIDI Handling
|
||||
#
|
||||
|
||||
def MidinProcess(inqueue, portname):
|
||||
|
||||
inqueue_get = inqueue.get
|
||||
|
||||
while True:
|
||||
time.sleep(0.001)
|
||||
msg = inqueue_get()
|
||||
print("")
|
||||
print("Generic from", portname,"msg : ", msg)
|
||||
|
||||
# Note On
|
||||
if msg[0]==NOTE_ON:
|
||||
|
||||
MidiChannel = msg[0]-144
|
||||
MidiNote = msg[1]
|
||||
MidiVel = msg[2]
|
||||
print("NOTE ON :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel)
|
||||
|
||||
|
||||
if MidiNote < 63 and MidiVel >0:
|
||||
|
||||
if platform == 'darwin':
|
||||
os.system("afplay snare.wav")
|
||||
else:
|
||||
os.system("aplay snare.wav")
|
||||
|
||||
|
||||
if MidiNote > 62 and MidiVel >0:
|
||||
|
||||
if platform == 'darwin':
|
||||
os.system("afplay kick.wav")
|
||||
else:
|
||||
os.system("aplay kick.wav")
|
||||
|
||||
|
||||
|
||||
# Note Off
|
||||
if msg[0]==NOTE_OFF:
|
||||
print("NOTE OFF :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel)
|
||||
|
||||
|
||||
# MMO-3 Midi CC message CHANNEL 1
|
||||
if msg[0] == CONTROLLER_CHANGE:
|
||||
print("channel 1 (MMO-3) CC :", msg[1], msg[2])
|
||||
|
||||
print("Midi in process send /mmo3/cc/"+str(msg[1])+" "+str(msg[2])+" to WS")
|
||||
if wsmode == True:
|
||||
ws.send("/mmo3/cc/"+str(msg[1])+" "+str(msg[2]))
|
||||
|
||||
# OCS-2 Midi CC message CHANNEL 2
|
||||
if msg[0] == CONTROLLER_CHANGE+1:
|
||||
print("channel 2 (OCS-2) CC :", msg[1], msg[2])
|
||||
|
||||
print("Midi in process send /ocs2/cc/"+str(msg[1])+" "+str(msg[2])+" to WS")
|
||||
if wsmode == True:
|
||||
ws.send("/ocs2/cc/"+str(msg[1])+" "+str(msg[2]))
|
||||
|
||||
|
||||
# other midi message
|
||||
if msg[0] != NOTE_OFF and msg[0] != NOTE_ON and msg[0] != CONTROLLER_CHANGE:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
# Generic call back : new msg forwarded to queue
|
||||
class AddQueue(object):
|
||||
def __init__(self, portname, port):
|
||||
self.portname = portname
|
||||
self.port = port
|
||||
#print "AddQueue", port)
|
||||
self._wallclock = time.time()
|
||||
|
||||
def __call__(self, event, data=None):
|
||||
message, deltatime = event
|
||||
self._wallclock += deltatime
|
||||
#print "inqueue : [%s] @%0.6f %r" % ( self.portname, self._wallclock, message))
|
||||
message.append(deltatime)
|
||||
midinputsqueue[self.port].put(message)
|
||||
|
||||
|
||||
#
|
||||
# MIDI OUT Handling
|
||||
#
|
||||
|
||||
|
||||
class OutObject():
|
||||
|
||||
_instances = set()
|
||||
counter = 0
|
||||
|
||||
def __init__(self, name, kind, port):
|
||||
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.port = port
|
||||
|
||||
self._instances.add(weakref.ref(self))
|
||||
OutObject.counter += 1
|
||||
|
||||
print("Adding OutDevice name", self.name, "kind", self.kind, "port", self.port)
|
||||
|
||||
@classmethod
|
||||
def getinstances(cls):
|
||||
dead = set()
|
||||
for ref in cls._instances:
|
||||
obj = ref()
|
||||
if obj is not None:
|
||||
yield obj
|
||||
else:
|
||||
dead.add(ref)
|
||||
cls._instances -= dead
|
||||
|
||||
def __del__(self):
|
||||
OutObject.counter -= 1
|
||||
|
||||
|
||||
|
||||
def OutConfig():
|
||||
global midiout, MidInsNumber
|
||||
|
||||
#
|
||||
if len(OutDevice) == 0:
|
||||
print("")
|
||||
print("MIDIout...")
|
||||
print("List and attach to available devices on host with IN port :")
|
||||
|
||||
# Display list of available midi IN devices on the host, create and start an OUT instance to talk to each of these Midi IN devices
|
||||
midiout = rtmidi.MidiOut()
|
||||
available_ports = midiout.get_ports()
|
||||
|
||||
for port, name in enumerate(available_ports):
|
||||
|
||||
midiname[port]=name
|
||||
midiport[port].open_port(port)
|
||||
#print )
|
||||
#print "New OutDevice [%i] %s" % (port, name))
|
||||
|
||||
OutDevice.append(OutObject(name, "generic", port))
|
||||
|
||||
#print "")
|
||||
print(len(OutDevice), "Out devices")
|
||||
#ListOutDevice()
|
||||
MidInsNumber = len(OutDevice)+1
|
||||
|
||||
def ListOutDevice():
|
||||
|
||||
for item in OutObject.getinstances():
|
||||
|
||||
print(item.name)
|
||||
|
||||
def FindOutDevice(name):
|
||||
|
||||
port = -1
|
||||
for item in OutObject.getinstances():
|
||||
#print "searching", name, "in", item.name)
|
||||
if name == item.name:
|
||||
#print 'found port',item.port)
|
||||
port = item.port
|
||||
return port
|
||||
|
||||
|
||||
def DelOutDevice(name):
|
||||
|
||||
Outnumber = Findest(name)
|
||||
print('deleting OutDevice', name)
|
||||
|
||||
if Outnumber != -1:
|
||||
print('found OutDevice', Outnumber)
|
||||
delattr(OutObject, str(name))
|
||||
print("OutDevice", Outnumber,"was removed")
|
||||
else:
|
||||
print("OutDevice was not found")
|
||||
|
||||
|
||||
|
||||
#
|
||||
# MIDI IN Handling
|
||||
# Create processing thread and queue for each device
|
||||
#
|
||||
|
||||
class InObject():
|
||||
|
||||
_instances = set()
|
||||
counter = 0
|
||||
|
||||
def __init__(self, name, kind, port, rtmidi):
|
||||
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.port = port
|
||||
self.rtmidi = rtmidi
|
||||
self.queue = Queue()
|
||||
|
||||
self._instances.add(weakref.ref(self))
|
||||
InObject.counter += 1
|
||||
|
||||
print("Adding InDevice name", self.name, "kind", self.kind, "port", self.port)
|
||||
|
||||
@classmethod
|
||||
def getinstances(cls):
|
||||
dead = set()
|
||||
for ref in cls._instances:
|
||||
obj = ref()
|
||||
if obj is not None:
|
||||
yield obj
|
||||
else:
|
||||
dead.add(ref)
|
||||
cls._instances -= dead
|
||||
|
||||
def __del__(self):
|
||||
InObject.counter -= 1
|
||||
|
||||
|
||||
def InConfig():
|
||||
|
||||
print("")
|
||||
print("MIDIin...")
|
||||
# client mode
|
||||
if wsmode == True:
|
||||
print("ws object", ws)
|
||||
print("List and attach to available devices on host with OUT port :")
|
||||
|
||||
if platform == 'darwin':
|
||||
mido.set_backend('mido.backends.rtmidi/MACOSX_CORE')
|
||||
|
||||
genericnumber = 0
|
||||
|
||||
for port, name in enumerate(mido.get_input_names()):
|
||||
|
||||
|
||||
outport = FindOutDevice(name)
|
||||
midinputsname[port]=name
|
||||
|
||||
#print "name",name, "Port",port, "Outport", outport)
|
||||
# print "midinames", midiname)
|
||||
|
||||
#ListInDevice()
|
||||
|
||||
try:
|
||||
#print name, name.find("RtMidi output"))
|
||||
if name.find("RtMidi output") > -1:
|
||||
print("No thread started for device", name)
|
||||
else:
|
||||
portin = object
|
||||
port_name = ""
|
||||
portin, port_name = open_midiinput(outport)
|
||||
#midinputs.append(portin)
|
||||
InDevice.append(InObject(name, "generic", outport, portin))
|
||||
|
||||
thread = Thread(target=MidinProcess, args=(midinputsqueue[port],port_name))
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
|
||||
#print "Thread launched for midi port", port, "portname", port_name, "Inname", midiname.index(port_name)
|
||||
#print "counter", InObject.counter
|
||||
#midinputs[port].set_callback(AddQueue(name),midinputsqueue[port])
|
||||
#midinputs[port].set_callback(AddQueue(name))
|
||||
#genericnumber += 1
|
||||
InDevice[InObject.counter-1].rtmidi.set_callback(AddQueue(name,port))
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
#print "")
|
||||
print(InObject.counter, "In devices")
|
||||
#ListInDevice()
|
||||
|
||||
|
||||
def ListInDevice():
|
||||
|
||||
#print "known IN devices :"
|
||||
for item in InObject.getinstances():
|
||||
|
||||
print(item.name)
|
||||
print("")
|
||||
|
||||
def FindInDevice(name):
|
||||
|
||||
port = -1
|
||||
for item in InObject.getinstances():
|
||||
#print "searching", name, "in", item.name)
|
||||
if name in item.name:
|
||||
#print 'found port',item.port)
|
||||
port = item.port
|
||||
return port
|
||||
|
||||
|
||||
def DelInDevice(name):
|
||||
|
||||
Innumber = Findest(name)
|
||||
print('deleting InDevice', name)
|
||||
|
||||
if Innumber != -1:
|
||||
print('found InDevice', Innumber)
|
||||
delattr(InObject, str(name))
|
||||
print("InDevice", Innumber,"was removed")
|
||||
else:
|
||||
print("InDevice was not found")
|
||||
|
||||
|
||||
|
||||
def End():
|
||||
global midiout
|
||||
|
||||
#midiin.close_port()
|
||||
midiout.close_port()
|
||||
|
||||
#del virtual
|
||||
if launchpad.Here != -1:
|
||||
del launchpad.Here
|
||||
if bhoreal.Here != -1:
|
||||
del bhoreal.Here
|
||||
if LPD8.Here != -1:
|
||||
del LPD8.Here
|
||||
|
||||
# mididest : all or specifiname, won't be sent to launchpad or Bhoreal.
|
||||
def MidiMsg(midimsg, mididest):
|
||||
|
||||
|
||||
desterror = -1
|
||||
|
||||
print("midi3 got midimsg", midimsg, "for", mididest)
|
||||
|
||||
for port in range(len(OutDevice)):
|
||||
# To mididest
|
||||
if midiname[port].find(mididest) != -1:
|
||||
if debug>0:
|
||||
print("midi 3 sending to name", midiname[port], "port", port, ":", midimsg)
|
||||
midiport[port].send_message(midimsg)
|
||||
desterror = 0
|
||||
|
||||
if desterror == -1:
|
||||
print("mididest",mididest, ": ** This midi destination doesn't exists **")
|
||||
|
||||
# send midi msg over ws.
|
||||
#if wsmode == True:
|
||||
# ws.send("/ocs2/cc/1 2")
|
||||
|
||||
|
||||
|
||||
def NoteOn(note, velocity, mididest):
|
||||
global MidInsNumber
|
||||
|
||||
|
||||
for port in range(MidInsNumber):
|
||||
|
||||
# To mididest
|
||||
if midiname[port].find(mididest) == 0:
|
||||
midiport[port].send_message([NOTE_ON, note, velocity])
|
||||
|
||||
|
||||
|
||||
def listdevice(number):
|
||||
|
||||
return midiname[number]
|
||||
|
||||
def check():
|
||||
|
||||
OutConfig()
|
||||
InConfig()
|
||||
|
||||
|
||||
|
451
nozWS.py
Executable file
451
nozWS.py
Executable file
@ -0,0 +1,451 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
'''
|
||||
NozoidUI Client v0.0.1
|
||||
|
||||
Input : local midi instruments
|
||||
Output : nozoidtUI server
|
||||
|
||||
Websocket INSTALLER
|
||||
|
||||
https://files.pythonhosted.org/packages/8b/0f/52de51b9b450ed52694208ab952d5af6ebbcbce7f166a48784095d930d8c/websocket_client-0.57.0.tar.gz
|
||||
'''
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("NozoidUI Client")
|
||||
print("v0.0.1")
|
||||
|
||||
from multiprocessing import Process, Queue, TimeoutError
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
import os
|
||||
import time
|
||||
|
||||
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
|
||||
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
|
||||
import midi3
|
||||
|
||||
from websocket_server import WebsocketServer
|
||||
|
||||
#import socket
|
||||
import types, time
|
||||
import websocket
|
||||
|
||||
import socket
|
||||
import struct
|
||||
import argparse
|
||||
|
||||
argsparser = argparse.ArgumentParser(description="Nozoid : Transform musical parameters in XXX visualisation")
|
||||
argsparser.add_argument("-i","--iport",help="remserial IN port (9090 by default)",type=int)
|
||||
argsparser.add_argument("-o","--oport",help="remserial OUT port (9091 by default)",type=int)
|
||||
argsparser.add_argument("-t","--timeout",help="timeout connection (0.002 s by default)",type=float)
|
||||
args = argsparser.parse_args()
|
||||
|
||||
nozIP='127.0.0.1'
|
||||
portrcv = 9090
|
||||
portwrt = 9091
|
||||
timeout=0.002
|
||||
|
||||
if args.iport:
|
||||
portrcv=args.iport
|
||||
if args.oport:
|
||||
portwrt=args.oport
|
||||
if args.timeout:
|
||||
timeout=args.timeout
|
||||
|
||||
|
||||
try:
|
||||
import _thread
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
import time
|
||||
|
||||
debug = 0
|
||||
|
||||
#
|
||||
# webUI server
|
||||
#
|
||||
|
||||
serverIP = "xrkia.org"
|
||||
# serverIP = "127.0.0.1"
|
||||
# serverIP = "10.8.0.46"
|
||||
wsPORT = 8081
|
||||
|
||||
|
||||
#
|
||||
# Midi part
|
||||
#
|
||||
|
||||
# nozmidi = "BCR2000 Port 1"
|
||||
# nozmidi = "Arturia BeatStep"
|
||||
# nozmidi = "Virtual Midi A"
|
||||
# nozmidi = "Virtual Sequencer"
|
||||
# nowmidi = "IAC Driver Sequencer Bus 1"
|
||||
nozmidi = "UM-ONE:UM-ONE MIDI 1 20:0"
|
||||
|
||||
midichanOCS2 = 2
|
||||
midichanMMO3 = 1
|
||||
|
||||
# resetMMO3 = [64,64,0,32,96] # un truc comme ca pour les valeurs de reset ?
|
||||
resetMMO3 = [0] * 32
|
||||
resetOCS2 = [0] * 32
|
||||
|
||||
# /cc cc number value
|
||||
def cc(midichannel, ccnumber, value, mididest):
|
||||
|
||||
print("NozoidUI Sending Midi channel", midichannel, "cc", ccnumber, "value", value, "to", mididest)
|
||||
#if mididest == "BCR2000 Port 1":
|
||||
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1, ccnumber, value], mididest)
|
||||
|
||||
|
||||
# /reset nozoids with "default" values
|
||||
def reset(nozoid):
|
||||
|
||||
print("")
|
||||
print("reseting", nozoid)
|
||||
|
||||
if nozoid == "mmo3":
|
||||
for ccnumber in range(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichanMMO3-1, ccnumber, resetMMO3[ccnumber]], nozmidi)
|
||||
sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber]))
|
||||
else:
|
||||
for ccnumber in range(0,32):
|
||||
midi3.MidiMsg([CONTROLLER_CHANGE+midichanOCS2-1, ccnumber, resetOCS2[ccnumber]], nozmidi)
|
||||
sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber]))
|
||||
|
||||
#
|
||||
# Websocket part
|
||||
#
|
||||
|
||||
def on_error(ws, error):
|
||||
print(error)
|
||||
|
||||
def on_close(ws):
|
||||
print("### closed ###")
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
|
||||
def run(*args):
|
||||
|
||||
try:
|
||||
|
||||
NozStream()
|
||||
#while True:
|
||||
|
||||
# time.sleep(1)
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
finally:
|
||||
ws.close()
|
||||
print("thread terminating...")
|
||||
|
||||
|
||||
_thread.start_new_thread(run, ())
|
||||
|
||||
def on_message(ws, message):
|
||||
#
|
||||
print("")
|
||||
print(message)
|
||||
if len(message) > 200:
|
||||
message = message[:200]+'..'
|
||||
|
||||
oscpath = message.split(" ")
|
||||
if debug > 0:
|
||||
#print "Client got from WS", client['id'], "said :", message, "splitted in an oscpath :", oscpath
|
||||
print("Client got from WS said :", message, "splitted in an oscpath :", oscpath)
|
||||
|
||||
wscommand = oscpath[0].split("/")
|
||||
print("WS command was :",wscommand)
|
||||
|
||||
if len(oscpath) == 1:
|
||||
args[0] = "noargs"
|
||||
#print "noargs command"
|
||||
|
||||
elif wscommand[2] == "cc":
|
||||
if wscommand[1] == "ocs2":
|
||||
print("Incoming OCS-2 WS")
|
||||
cc(midichanOCS2, int(wscommand[3]), int(oscpath[1]), nozmidi)
|
||||
else:
|
||||
print("Incoming MMO-3 WS")
|
||||
cc(midichanMMO3, int(wscommand[3]), int(oscpath[1]), nozmidi)
|
||||
|
||||
|
||||
elif wscommand[2] == "reset":
|
||||
if wscommand[1] == "ocs2":
|
||||
reset("ocs2")
|
||||
else:
|
||||
reset("mmo3")
|
||||
|
||||
|
||||
# if needed a loop back : WS Client -> server -> WS Client
|
||||
# sendWSall(message)
|
||||
|
||||
##################
|
||||
#BEGIN NOZOID PART
|
||||
##################
|
||||
|
||||
#backup each oscillator value
|
||||
osc = [0] * 512
|
||||
oscnumber = 0
|
||||
Knob = [255] * 255
|
||||
|
||||
TimeoutCompt=0
|
||||
LastNozMsg=struct.pack('>BBBB',255,255,0,0)
|
||||
|
||||
def knob2cc(s):
|
||||
a1, a2 = 0,65535
|
||||
b1, b2 = 0, 127
|
||||
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
|
||||
|
||||
def lhex(h):
|
||||
return ':'.join(x.encode('hex') for x in h)
|
||||
|
||||
def Write(sockwrt,stuff):
|
||||
# Send data
|
||||
print('sending',stuff)
|
||||
my_bytes = bytearray()
|
||||
for count in range(len(stuff)):
|
||||
my_bytes.append(stuff[count])
|
||||
sockwrt.sendall(my_bytes)
|
||||
|
||||
|
||||
def Read4():
|
||||
#print "**SockRcv %s"%SockRcv
|
||||
return Read(SockRcv,4)
|
||||
|
||||
# Read a given number of bytes
|
||||
def Read(sockrcv,length):
|
||||
|
||||
# Look for the response
|
||||
amount_received_t = 0
|
||||
data = ""
|
||||
|
||||
data = sockrcv.recv(length)
|
||||
amount_received_t = len(data)
|
||||
|
||||
while amount_received_t < length:
|
||||
amount_expected = length-amount_received_t
|
||||
#print "**data:",lhex(data),
|
||||
#print "**still needing %d byte(s) to read"%amount_expected
|
||||
data_received = sockrcv.recv(amount_expected)
|
||||
amount_received = len(data_received)
|
||||
|
||||
data += data_received
|
||||
amount_received_t = len(data)
|
||||
|
||||
if ord(data[0])==0xFF:
|
||||
return data
|
||||
else:
|
||||
while ord(data[0])!=0xFF:
|
||||
#print "*datax0",lhex(data)
|
||||
data=data[1:]
|
||||
data_received = sockrcv.recv(1)
|
||||
data+=data_received
|
||||
#data+=struct.pack('>B',255)
|
||||
#print "*datax1",lhex(data)
|
||||
#raw_input("Hit Enter To Continue...")
|
||||
print("**Shiftin'!!!",lhex(data))
|
||||
#raw_input("Hit Enter To Continue...")
|
||||
return data
|
||||
#return struct.pack('>BBBB',255,255,0,0)
|
||||
|
||||
# Nozoids Messages decoding
|
||||
def Msg(NozMsg,nozport):
|
||||
|
||||
global osc
|
||||
global oscnumber
|
||||
|
||||
if ord(NozMsg[1]) < 160:
|
||||
(val,) = struct.unpack_from('>H', NozMsg, 2)
|
||||
knob=ord(NozMsg[1])+(nozport*127)
|
||||
#ccknobv= cc knob value
|
||||
#if knob == 30 or knob == 31:
|
||||
if knob >= 30:
|
||||
#return (knob, val)
|
||||
#return (knob, 0)
|
||||
return (knob, 0-32767)
|
||||
ccknobv=knob2cc(val)
|
||||
if Knob[knob] != ccknobv:
|
||||
if Knob[knob] != 255:
|
||||
print("knob %d has changed from %d to %d value"%(knob,Knob[knob],ccknobv))
|
||||
#raw_input("Hit Enter To Continue...")
|
||||
Knob[knob]=ccknobv
|
||||
#return (knob, val)
|
||||
return (knob, val-32767)
|
||||
|
||||
if (ord(NozMsg[1]) >= 0xA0 and ord(NozMsg[1]) < 0xF0) or (ord(NozMsg[1]) >= 0xF6 and ord(NozMsg[1]) <= 0xF8):
|
||||
OrdNozMsg=ord(NozMsg[1])
|
||||
|
||||
(val,) = struct.unpack_from('>h', NozMsg, 2)
|
||||
oscnumber = ord(NozMsg[1])-0x9F+(nozport*127)
|
||||
oscvalue = val
|
||||
osc[oscnumber]=oscvalue
|
||||
return (oscnumber, oscvalue)
|
||||
|
||||
# type of nozoid
|
||||
if ord(NozMsg[1]) == 0xF0:
|
||||
#if NozMsg[1] == 0xF0:
|
||||
print("Nozoid type sent:%s"%NozMsg[-2:])
|
||||
#raw_input("Hit Enter To Continue")
|
||||
noztype[nozport] = NozMsg[-2:]
|
||||
#print "noztype:%s"%noztype
|
||||
return (0xF0, NozMsg[-2:])
|
||||
|
||||
if ord(NozMsg[1]) >= 0xF3 and ord(NozMsg[1]) <= 0xF5:
|
||||
#if NozMsg[1] >= 0xF3 and NozMsg[1] <= 0xF5:#an osc value that belong to the [0,65535] interval (CV values ? see Arduino's code of MMO-3 and OSC-2 firmware)
|
||||
(val,) = struct.unpack_from('>H', NozMsg, 2)
|
||||
oscnumber=ord(NozMsg[1])-0x9F+(nozport*127)
|
||||
oscvalue=val-32767
|
||||
osc[oscnumber]=oscvalue
|
||||
return (oscnumber,oscvalue)
|
||||
|
||||
if ord(NozMsg[1]) == 0xFA: #a note/keyboard has been sent
|
||||
(val,) = struct.unpack_from('>H', NozMsg, 2)
|
||||
#print("set notekb to", str(val))
|
||||
return (0xFA, val)
|
||||
|
||||
if ord(NozMsg[1]) == 0xFF: #an error somewhere ?
|
||||
(val,) = struct.unpack_from('>H', NozMsg, 2)
|
||||
return (0xFF, val)
|
||||
|
||||
def NozStream():
|
||||
global osc
|
||||
global oscnumber
|
||||
global TimeoutCompt
|
||||
|
||||
if SockWrt:
|
||||
Write(SockWrt,[0xFF])
|
||||
#Write(SockWrt,[0xF6])#VCF
|
||||
#Write(SockWrt,[0xA0])#VCO1
|
||||
#Write(SockWrt,[0xA1])#VCO2
|
||||
Write(SockWrt,[0xA3])#LFO2
|
||||
#SockWrt.close()
|
||||
|
||||
while True:
|
||||
|
||||
try:
|
||||
NozMsg = Read4()#here we read !
|
||||
#print "**NozMsg:%s"%NozMsg
|
||||
LastNozMsg = NozMsg#backing up last good msg
|
||||
#print "NozMsg:",lhex(NozMsg)
|
||||
|
||||
MsgReturn = Msg(NozMsg,0)#Go decoding Msg and assigning global vars...
|
||||
print("MsgReturn:",MsgReturn)
|
||||
|
||||
#y=osc[oscnumber]
|
||||
|
||||
ws.send("/ocs2/OSC"+str(oscnumber)+" "+str(osc[oscnumber]))
|
||||
|
||||
|
||||
except socket.timeout:
|
||||
TimeoutCompt+=1
|
||||
print('**caught a timeout(%d)'%TimeoutCompt)
|
||||
time.sleep(timeout)
|
||||
NozMsg = struct.pack('>BBBB',255,255,0,0)#0xFFFF0000 (ie error)
|
||||
#print "NozMsg:",lhex(NozMsg)
|
||||
|
||||
|
||||
# Nozoid reader via remserial
|
||||
# ex : remserial -p 9090 -s "2000000 raw" /dev/ttyACM0
|
||||
#
|
||||
print("Nozoid Reader connecting...")
|
||||
try:
|
||||
# Create a TCP/IP socket
|
||||
SockRcv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# Connect the socket to the port where the server is listening
|
||||
serverrcv_address = (nozIP, portrcv)
|
||||
|
||||
SockRcv.connect(serverrcv_address)
|
||||
|
||||
SockRcv.settimeout(timeout)#timeout 0.002s
|
||||
|
||||
print('connecting to %s portrcv %s' % serverrcv_address, file=sys.stderr)
|
||||
except:
|
||||
print("** huh! did you launch remserial on %s:%d ?"%(nozIP,portrcv))
|
||||
SockRcv.close()
|
||||
SockRcv=None
|
||||
input("Hit Enter To Continue...")
|
||||
|
||||
#
|
||||
# Nozoid write via remserial
|
||||
# ex : remserial -p 9091 -w -s "2000000 raw" /dev/ttyACM0
|
||||
#
|
||||
print("Nozoid Writer connecting...")
|
||||
try:
|
||||
# Create a TCP/IP socket
|
||||
SockWrt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
serverwrt_address = (nozIP, portwrt)
|
||||
|
||||
# Connect the socket to the port where the server is listening
|
||||
SockWrt.connect(serverwrt_address)
|
||||
print('connecting to %s portwrt %s' % serverwrt_address, file=sys.stderr)
|
||||
except:
|
||||
print("** huh! did you launch remserial on %s:%d ?"%(nozIP,portwrt))
|
||||
SockWrt.close()
|
||||
SockWrt=None
|
||||
input("Hit Enter To Continue...")
|
||||
|
||||
|
||||
###################
|
||||
#END OF NOZOID PART
|
||||
###################
|
||||
|
||||
print("Running....")
|
||||
|
||||
def GetTime():
|
||||
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
|
||||
|
||||
def sendWSall(message):
|
||||
if debug >0:
|
||||
print(GetTime(),"sending to all %s" % (message))
|
||||
|
||||
ws.send_message_to_all(message)
|
||||
|
||||
# Main loop do nothing. Maybe do the webui server ?
|
||||
try:
|
||||
|
||||
print("")
|
||||
print("Connecting to NozoidUI server...")
|
||||
print("at", serverIP, "port",wsPORT)
|
||||
|
||||
#websocket.enableTrace(True)
|
||||
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
|
||||
on_message = on_message,
|
||||
on_error = on_error,
|
||||
on_close = on_close)
|
||||
|
||||
midi3.ws = ws
|
||||
midi3.wsmode = True
|
||||
|
||||
print("Midi Configuration...")
|
||||
print("Midi Destination", nozmidi)
|
||||
|
||||
midi3.check()
|
||||
|
||||
ws.on_open = on_open
|
||||
ws.run_forever()
|
||||
|
||||
#print "That Point Is Never Reach ?"
|
||||
if SockWrt:
|
||||
Write(SockWrt,[0xFF])
|
||||
print("closing nozoid connections!")
|
||||
SockWrt.shutdown(socket.SHUT_RDWR)
|
||||
SockWrt.close()
|
||||
SockRcv.shutdown(socket.SHUT_RDWR)
|
||||
SockRcv.close()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
# Gently stop on CTRL C
|
||||
|
||||
print("Fin de NozoidUI.")
|
104
rules.json
Normal file
104
rules.json
Normal file
@ -0,0 +1,104 @@
|
||||
{
|
||||
|
||||
|
||||
"cc2cc": [
|
||||
{
|
||||
"_comment": "route incoming ws CC to another local CC/midi device",
|
||||
"name": "ocs2 lfo1 fq (= chan 2 cc 13) to bcr chan 2 cc 10",
|
||||
"songname": "all",
|
||||
"chanIN" : 2,
|
||||
"ccs" : 13,
|
||||
"chanOUT" : 2,
|
||||
"ccOUT": 1,
|
||||
"valuetype" : "linear",
|
||||
"mididest" : "BCR2000 Port 1"
|
||||
},
|
||||
{
|
||||
"name": "bass2",
|
||||
"songname": "song2",
|
||||
"chanIN" : 1,
|
||||
"ccs" : "all",
|
||||
"chanOUT" : 1,
|
||||
"ccOUT": 10,
|
||||
"valuetype" : "curved",
|
||||
"mididest" : "Virtual Midi A"
|
||||
}
|
||||
],
|
||||
|
||||
"Specials": [
|
||||
{
|
||||
"_comment": "Pas implementé encore dans nozoidUI",
|
||||
"name": "autotempo",
|
||||
"songname": "song1",
|
||||
"chanIN" : 16,
|
||||
"notes" : 127,
|
||||
"notetype" : "on",
|
||||
"code": "autotempo",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "resetCCON",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 126,
|
||||
"notetype" : "on",
|
||||
"code": "resetCCON",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "resetCCOFF",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 126,
|
||||
"notetype" : "off",
|
||||
"code": "resetCCOFF",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "BangON",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 125,
|
||||
"notetype" : "on",
|
||||
"code": "bangON",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "BangOFF",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 125,
|
||||
"notetype" : "off",
|
||||
"code": "bangOFF",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "StrobeON",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 124,
|
||||
"notetype" : "on",
|
||||
"code": "strobeON",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
},
|
||||
{
|
||||
"name": "StrobeOFF",
|
||||
"songname": "all",
|
||||
"chanIN" : 16,
|
||||
"notes" : 124,
|
||||
"notetype" : "off",
|
||||
"code": "strobeOFF",
|
||||
"valuetype" : 105,
|
||||
"laser" : 0
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
}
|
BIN
web/.DS_Store
vendored
Normal file
BIN
web/.DS_Store
vendored
Normal file
Binary file not shown.
1
web/css/style.css
Executable file
1
web/css/style.css
Executable file
@ -0,0 +1 @@
|
||||
* { box-sizing: border-box; } body {margin: 0;}.row{display:table;padding:10px;width:100%;}.cell{width:8%;display:table-cell;height:75px;}body, html{background-color:#1d1c25;margin:0;padding:0;}.range{-webkit-appearance:none;-moz-appearance:none;position:absolute;left:50%;top:50%;width:200px;margin-top:10px;transform:translate(-50%, -50%);}input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]:focus{outline:none;}input[type=range]::-moz-range-track{-moz-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-webkit-grab;-webkit-transition:border 1000ms ease;transition:border 1000ms ease;}input[type=range]::-moz-range-thumb{-moz-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-moz-grab;-moz-transition:border 1000ms ease;transition:border 1000ms ease;}.range.blue::-webkit-slider-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-webkit-slider-thumb{border-color:rgb(87,111,230);}.range.purple::-webkit-slider-thumb{border-color:rgb(152,68,183);}.range.pink::-webkit-slider-thumb{border-color:rgb(255,53,127);}.range.blue::-moz-range-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-moz-range-thumb{border-color:rgb(87,111,230);}.range.purple::-moz-range-thumb{border-color:rgb(152,68,183);}.range.pink::-moz-range-thumb{border-color:rgb(255,53,127);}input[type=range]::-webkit-slider-thumb:active{cursor:-webkit-grabbing;}input[type=range]::-moz-range-thumb:active{cursor:-moz-grabbing;}.range.blue{position:relative;background-color:rgba(31,27,27,0.09);width:75%;top:37.5px;}*{box-sizing:border-box;}body{margin:0;}#iy3nk{height:68px;}#il4ah{text-align:center;color:#ef4079;}#i7ox{font-family:Verdana, Geneva, sans-serif;color:#40acef;text-align:left;}#io5q{color:#40acef;}#iy2eh{text-align:center;}#i8a73{text-align:center;color:#4540ef;}#i3fzo{text-align:center;color:#7740ef;}#ikbjg{text-align:center;color:#b140ef;}@media (max-width: 768px){.cell{width:100%;display:block;}}
|
1
web/css/style2.css
Executable file
1
web/css/style2.css
Executable file
@ -0,0 +1 @@
|
||||
input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]:focus{outline:none;}input[type=range]::-moz-range-track{-moz-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-webkit-grab;-webkit-transition:border 1000ms ease;transition:border 1000ms ease;}input[type=range]::-moz-range-thumb{-moz-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-moz-grab;-moz-transition:border 1000ms ease;transition:border 1000ms ease;}.range.blue::-webkit-slider-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-webkit-slider-thumb{border-color:rgb(87,111,230);}.range.purple::-webkit-slider-thumb{border-color:rgb(152,68,183);}.range.pink::-webkit-slider-thumb{border-color:rgb(255,53,127);}.range.blue::-moz-range-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-moz-range-thumb{border-color:rgb(87,111,230);}.range.purple::-moz-range-thumb{border-color:rgb(152,68,183);}.range.pink::-moz-range-thumb{border-color:rgb(255,53,127);}input[type=range]::-webkit-slider-thumb:active{cursor:-webkit-grabbing;}input[type=range]::-moz-range-thumb:active{cursor:-moz-grabbing;}.range.blue{position:relative;background-color:rgba(31,27,27,0.09);width:75%;top:37.5px;}body{margin:0;}#iy3nk{font-family: "Lucida Grande", Verdana, Arial, sans-serif; font-size: 10ex; height:68px;}#il4ah{text-align:center;color:#ef4079;}#i7ox{font-family:Verdana, Geneva, sans-serif;color:#40acef;text-align:left;}#io5q{color:#40acef;}#iy2eh{text-align:center;}#i8a73{text-align:center;color:#4540ef;}#i3fzo{text-align:center;color:#7740ef;}#ikbjg{text-align:center;color:#b140ef;}@media (max-width: 768px){}
|
74
web/index.html
Normal file
74
web/index.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Nozoids online</title>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
|
||||
#title{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
font-size: 4ex;
|
||||
}
|
||||
|
||||
#text: {
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
font-size: 3ex;
|
||||
}
|
||||
a:link {
|
||||
color: white;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
a:hover {
|
||||
color: blue;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="background-color:#000;">
|
||||
|
||||
<!--
|
||||
Top Line
|
||||
-->
|
||||
|
||||
<div id="title" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 3ex;">
|
||||
1 / Live sound : <strong>VLC network stream</strong> http://xrkia.org:8000/NZ
|
||||
|
||||
</div>
|
||||
<div id="title" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 3ex;">2 / Choose to play a MMO-3 or OCS-2 ?
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
Nozoids Grid
|
||||
-->
|
||||
|
||||
<div align="center" class="mainGrid">
|
||||
|
||||
<div>
|
||||
<a href="mmo3.html"> <img src="http://nozoid.com/wp-content/uploads/2019/04/MMO3_square_resized.jpg">
|
||||
</a>
|
||||
|
||||
<a href="ocs2.html"> <img src="http://nozoid.com/wp-content/uploads/2019/04/ocs2_square_resized.jpg">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
288
web/indexaurora.html
Normal file
288
web/indexaurora.html
Normal file
@ -0,0 +1,288 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Aurora</title>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 0.8fr 47px 0.7fr 2.8fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
.buttons-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
#title{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
font-size: 4ex;
|
||||
}
|
||||
#text{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
font-size: 2ex;
|
||||
}
|
||||
.encoders{
|
||||
margin: 0 auto;
|
||||
}
|
||||
.mainGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: ". . . . . . . .";
|
||||
}
|
||||
|
||||
.encodersGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: "." "." "." "." ".";
|
||||
}
|
||||
a:link {
|
||||
color: white;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<!-- Web audio buttons defaults -->
|
||||
<script type="application/javascript" src="webcomponents-lite.js"></script>
|
||||
<script>
|
||||
WebAudioControlsOptions={
|
||||
useMidi:1,
|
||||
knobSrc:"knobs/simplegray.png",
|
||||
knobSprites:100,
|
||||
switchSrc:"knobs/switch_toggle.png",
|
||||
sliderSrc:"knobs/vsliderbody.png",
|
||||
sliderKnobsrc:"knobs/vsliderknob.png",
|
||||
}
|
||||
</script>
|
||||
<script src="webaudio-controls.js"></script>
|
||||
<!-- link rel="stylesheet" href="LJgrid.css" / -->
|
||||
<link rel="stylesheet" type="text/css" href="selector.min.css">
|
||||
<script type="application/javascript" src="selector.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body style="background-color:#000;">
|
||||
<div class="grid-container">
|
||||
|
||||
<!--
|
||||
Title Line
|
||||
-->
|
||||
<div></div>
|
||||
<div>
|
||||
<h1 id="title">Aurora</h1>
|
||||
</div>
|
||||
|
||||
<div id="status" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 3ex;">not available
|
||||
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Encoders Line
|
||||
-->
|
||||
<div></div>
|
||||
<div class="buttons-container" id="text">
|
||||
<div>
|
||||
<div><webaudio-knob id="mixer/value" diameter="70" min="0" max="127" value="64"></webaudio-knob></div>
|
||||
<div><webaudio-param link="mixer/value" value="0"></webaudio-param></div>
|
||||
<div>2 curves mixer</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><webaudio-knob id="scaler/scale" diameter="70" min="0" max="127" value="64"></webaudio-knob></div>
|
||||
<div><webaudio-param link="scaler/scale" value="0"></webaudio-param></div>
|
||||
<div>Scale</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><webaudio-knob id="duplicator/num" diameter="70" min="0" max="127" value="64"></webaudio-knob></div>
|
||||
<div><webaudio-param link="duplicator/num" value="0"></webaudio-param></div>
|
||||
<div>Copy</div>
|
||||
</div>
|
||||
<div></div>
|
||||
<!-- LJ style WS : A nettoyer ! -->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
<!-- var LJ = 'ws://xrkia.org:8081/' -->
|
||||
<!-- var LJ = 'ws://laser.teamlaser.fr:8081/' -->
|
||||
var LJ = 'ws://127.0.0.1:8081/'
|
||||
var _WS = {
|
||||
uri: LJ,
|
||||
ws: null,
|
||||
|
||||
init : function (e) {
|
||||
_WS.s = new WebSocket(_WS.uri);
|
||||
_WS.s.onopen = function (e) { _WS.onOpen(e); };
|
||||
_WS.s.onclose = function (e) { _WS.onClose(e); };
|
||||
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
|
||||
_WS.s.onerror = function (e) { _WS.onError(e); };
|
||||
},
|
||||
|
||||
onOpen: function () {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= "Connected";
|
||||
},
|
||||
|
||||
onClose: function () {
|
||||
_WS.showout('DISCONNECTED');
|
||||
|
||||
},
|
||||
|
||||
onMessage: function (e) {
|
||||
var res = e.data.split(" ");
|
||||
console.log(e.data)
|
||||
//console.log(res[0].substring(0,6))
|
||||
//console.log(res)
|
||||
//console.log(res[0].slice(1))
|
||||
var divtext = document.getElementById('status');
|
||||
var divtextp = document.getElementById('players');
|
||||
|
||||
|
||||
switch (res[0].substring(0,6)) {
|
||||
|
||||
case "/statu":
|
||||
divtext.innerHTML="MMO-3";
|
||||
break;
|
||||
case "/playe":
|
||||
divtext.innerHTML="MMO-3 ("+res[1]+" player(s))";
|
||||
divtextp.innerHTML=" ("+res[1]+" player(s))";
|
||||
break;
|
||||
case "/simul":
|
||||
pl = e.data.slice(7);
|
||||
//console.log(pl)
|
||||
pl2 = eval(pl.replace(/[()]/g, ''));
|
||||
break;
|
||||
case "/plpoi":
|
||||
//console.log("plpoint");
|
||||
break;
|
||||
default:
|
||||
console.log("test "+res[0].slice(1)+" "+res[1]);
|
||||
document.getElementById(res[0].slice(1)).value = res[1];
|
||||
_WS.showin(e.data);
|
||||
}
|
||||
},
|
||||
|
||||
onError: function (e) {
|
||||
_WS.showin('<span style="color: red;">ERROR:</span> ' + e.data);
|
||||
},
|
||||
|
||||
showin: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showout: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showstatus: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
send: function (message) {
|
||||
if (!message.length) {
|
||||
alert('Empty message not allowed !');
|
||||
} else {
|
||||
_WS.showout(message);
|
||||
_WS.s.send(message);
|
||||
}
|
||||
},
|
||||
|
||||
close: function () {
|
||||
_WS.showout('GOODBYE !');
|
||||
_WS.s.close();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', _WS.init, false);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
web audio encoders scripts
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var message="";
|
||||
var log=[];
|
||||
var knobs = document.getElementsByTagName('webaudio-knob');
|
||||
|
||||
for(var i = 0; i < knobs.length; i++){
|
||||
knobs[i].addEventListener("input",Dump,false);
|
||||
knobs[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var sliders = document.getElementsByTagName('webaudio-slider');
|
||||
|
||||
for(var i = 0; i < sliders.length; i++){
|
||||
sliders[i].addEventListener("input",Dump,false);
|
||||
sliders[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var switches = document.getElementsByTagName('webaudio-switch');
|
||||
|
||||
for(var i = 0; i < switches.length; i++) {
|
||||
switches[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
|
||||
function Dump(e) {
|
||||
var str="";
|
||||
str=e.type + " : " + e.target.id + " : " + e.target.value + " ";
|
||||
console.log(str);
|
||||
log.unshift(str);
|
||||
log.length=1;
|
||||
str="";
|
||||
|
||||
for(var i=19;i>=0;--i) {
|
||||
if(log[i])
|
||||
str+=log[i]+"<br/>";
|
||||
}
|
||||
|
||||
_WS.send("/" + e.target.id + " " + e.target.value);
|
||||
|
||||
if (e.target.id === "on" && e.type === "change") {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
if (e.target.id === "rate" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
if (e.target.id === "range" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
if (e.target.id === "select" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<div id="title" align ="center" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 2ex;">
|
||||
<span id="players"></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
web/knobs/.DS_Store
vendored
Normal file
BIN
web/knobs/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
web/knobs/rebuild.png
Normal file
BIN
web/knobs/rebuild.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
web/knobs/simplegray.png
Executable file
BIN
web/knobs/simplegray.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 222 KiB |
BIN
web/knobs/switch_toggle.png
Executable file
BIN
web/knobs/switch_toggle.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
506
web/mmo3.html
Normal file
506
web/mmo3.html
Normal file
@ -0,0 +1,506 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MMO-3</title>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 0.8fr 47px 0.7fr 2.8fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
.buttons-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
#title{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
font-size: 4ex;
|
||||
}
|
||||
#text{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
font-size: 2ex;
|
||||
}
|
||||
.encoders{
|
||||
margin: 0 auto;
|
||||
}
|
||||
.mainGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: ". . . . . . . .";
|
||||
}
|
||||
|
||||
.encodersGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: "." "." "." "." ".";
|
||||
}
|
||||
a:link {
|
||||
color: white;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<!-- Web audio buttons defaults -->
|
||||
<script type="application/javascript" src="webcomponents-lite.js"></script>
|
||||
<script>
|
||||
WebAudioControlsOptions={
|
||||
useMidi:1,
|
||||
knobSrc:"knobs/simplegray.png",
|
||||
knobSprites:100,
|
||||
switchSrc:"knobs/switch_toggle.png",
|
||||
sliderSrc:"knobs/vsliderbody.png",
|
||||
sliderKnobsrc:"knobs/vsliderknob.png",
|
||||
}
|
||||
</script>
|
||||
<script src="webaudio-controls.js"></script>
|
||||
<!-- link rel="stylesheet" href="LJgrid.css" / -->
|
||||
<link rel="stylesheet" type="text/css" href="selector.min.css">
|
||||
<script type="application/javascript" src="selector.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body style="background-color:#000;">
|
||||
|
||||
<!--
|
||||
Top Line
|
||||
-->
|
||||
|
||||
<div id="status" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 3ex;">not available
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
Buttons Line
|
||||
|
||||
<div></div>
|
||||
<div class="buttons-container" id="text">
|
||||
<div>
|
||||
<webaudio-switch id="select" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/select.png" type="toggle" onclick="socket.emit('message', '/select');"></webaudio-switch>
|
||||
</div>
|
||||
<div>
|
||||
<webaudio-switch id="rate" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/rate.png" type="toggle" onclick="socket.emit('message', '/rate');"></webaudio-switch>
|
||||
</div>
|
||||
<div>
|
||||
<webaudio-switch id="steps" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/steps.png" type="toggle" onclick="socket.emit('message', '/steps');"></webaudio-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
-->
|
||||
<!--
|
||||
OCS 2 encoders 1 x 8 horizontal grid
|
||||
-->
|
||||
<div class="mainGrid">
|
||||
|
||||
<!-- OSC 1 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
OSC 1
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/0" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -0</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/1" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -1</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/2" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -2</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/3" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 3 -3</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- OSC 2 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
OCS 2
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/5" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -5</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/6" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -6</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/7" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -7</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/8" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 3 -8</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- OSC 3 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
OSC 3
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/9" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -9</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/10" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -10</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/11" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -11</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/12" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 3 -12</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- LFO 1 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 1
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/13" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -13</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/14" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WF -14</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/15" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">SYM -15</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/16" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">ATTACK -16</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LFO 2 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 2
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/17" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ 1 -17</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/18" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ 2 -18</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/19" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">MOD -19</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/20" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">DECAY -20</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LFO 3 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 3
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/21" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -21</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/22" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Param 1 -22</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/23" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Param 2 -23</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/24" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">SUSTAIN -24</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MIX vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
MIX
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/25" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">OSC 1 -25</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/26" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">OSC 2 -26</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/27" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">OSC 3 -27</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/28" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">RELEASE -28</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AUDIO vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
AUDIO
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="mmo3/cc/29" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">VOL -29</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<webaudio-switch id="mmo3/reset" value="1" height="70" width="70" tooltip="Switch-B" src="knobs/rebuild.png" type="toggle"></webaudio-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- LJ style WS : A nettoyer ! -->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
<!-- var LJ = 'ws://xrkia.org:8081/' -->
|
||||
<!-- var LJ = 'ws://laser.teamlaser.fr:8081/' -->
|
||||
var LJ = 'ws://127.0.0.1:8081/'
|
||||
var _WS = {
|
||||
uri: LJ,
|
||||
ws: null,
|
||||
|
||||
init : function (e) {
|
||||
_WS.s = new WebSocket(_WS.uri);
|
||||
_WS.s.onopen = function (e) { _WS.onOpen(e); };
|
||||
_WS.s.onclose = function (e) { _WS.onClose(e); };
|
||||
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
|
||||
_WS.s.onerror = function (e) { _WS.onError(e); };
|
||||
},
|
||||
|
||||
onOpen: function () {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= "Connected";
|
||||
},
|
||||
|
||||
onClose: function () {
|
||||
_WS.showout('DISCONNECTED');
|
||||
|
||||
},
|
||||
|
||||
onMessage: function (e) {
|
||||
var res = e.data.split(" ");
|
||||
console.log(e.data)
|
||||
//console.log(res[0].substring(0,6))
|
||||
//console.log(res)
|
||||
//console.log(res[0].slice(1))
|
||||
var divtext = document.getElementById('status');
|
||||
var divtextp = document.getElementById('players');
|
||||
|
||||
|
||||
switch (res[0].substring(0,6)) {
|
||||
|
||||
case "/statu":
|
||||
divtext.innerHTML="MMO-3";
|
||||
break;
|
||||
case "/playe":
|
||||
divtext.innerHTML="MMO-3 ("+res[1]+" player(s))";
|
||||
divtextp.innerHTML=" ("+res[1]+" player(s))";
|
||||
break;
|
||||
case "/simul":
|
||||
pl = e.data.slice(7);
|
||||
//console.log(pl)
|
||||
pl2 = eval(pl.replace(/[()]/g, ''));
|
||||
break;
|
||||
case "/plpoi":
|
||||
//console.log("plpoint");
|
||||
break;
|
||||
default:
|
||||
console.log("test "+res[0].slice(1)+" "+res[1]);
|
||||
document.getElementById(res[0].slice(1)).value = res[1];
|
||||
_WS.showin(e.data);
|
||||
}
|
||||
},
|
||||
|
||||
onError: function (e) {
|
||||
_WS.showin('<span style="color: red;">ERROR:</span> ' + e.data);
|
||||
},
|
||||
|
||||
showin: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showout: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showstatus: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
send: function (message) {
|
||||
if (!message.length) {
|
||||
alert('Empty message not allowed !');
|
||||
} else {
|
||||
_WS.showout(message);
|
||||
_WS.s.send(message);
|
||||
}
|
||||
},
|
||||
|
||||
close: function () {
|
||||
_WS.showout('GOODBYE !');
|
||||
_WS.s.close();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', _WS.init, false);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
web audio encoders scripts
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var message="";
|
||||
var log=[];
|
||||
var knobs = document.getElementsByTagName('webaudio-knob');
|
||||
|
||||
for(var i = 0; i < knobs.length; i++){
|
||||
knobs[i].addEventListener("input",Dump,false);
|
||||
knobs[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var sliders = document.getElementsByTagName('webaudio-slider');
|
||||
|
||||
for(var i = 0; i < sliders.length; i++){
|
||||
sliders[i].addEventListener("input",Dump,false);
|
||||
sliders[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var switches = document.getElementsByTagName('webaudio-switch');
|
||||
|
||||
for(var i = 0; i < switches.length; i++) {
|
||||
switches[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
|
||||
function Dump(e) {
|
||||
var str="";
|
||||
str=e.type + " : " + e.target.id + " : " + e.target.value + " ";
|
||||
console.log(str);
|
||||
log.unshift(str);
|
||||
log.length=1;
|
||||
str="";
|
||||
|
||||
for(var i=19;i>=0;--i) {
|
||||
if(log[i])
|
||||
str+=log[i]+"<br/>";
|
||||
}
|
||||
|
||||
_WS.send("/" + e.target.id + " " + e.target.value);
|
||||
|
||||
if (e.target.id === "on" && e.type === "change") {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
if (e.target.id === "rate" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
if (e.target.id === "range" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
if (e.target.id === "select" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<div id="title" align ="center" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 2ex;">
|
||||
<a href="ocs2.html">OCS-2</a>
|
||||
<span id="players"></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
515
web/ocs2.html
Normal file
515
web/ocs2.html
Normal file
@ -0,0 +1,515 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OCS-2</title>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 0.8fr 47px 0.7fr 2.8fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
.buttons-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
|
||||
}
|
||||
#title{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
font-size: 4ex;
|
||||
}
|
||||
#text{
|
||||
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
font-size: 2ex;
|
||||
}
|
||||
.encoders{
|
||||
margin: 0 auto;
|
||||
}
|
||||
.mainGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: ". . . . . . . .";
|
||||
}
|
||||
|
||||
.encodersGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
|
||||
gap: 1px 1px;
|
||||
grid-template-areas: "." "." "." "." ".";
|
||||
}
|
||||
a:link {
|
||||
color: white;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
a:visited {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<!-- Web audio buttons defaults -->
|
||||
<script type="application/javascript" src="webcomponents-lite.js"></script>
|
||||
<script>
|
||||
WebAudioControlsOptions={
|
||||
useMidi:1,
|
||||
knobSrc:"knobs/simplegray.png",
|
||||
knobSprites:100,
|
||||
switchSrc:"knobs/switch_toggle.png",
|
||||
sliderSrc:"knobs/vsliderbody.png",
|
||||
sliderKnobsrc:"knobs/vsliderknob.png",
|
||||
}
|
||||
</script>
|
||||
<script src="webaudio-controls.js"></script>
|
||||
<!-- link rel="stylesheet" href="LJgrid.css" / -->
|
||||
<link rel="stylesheet" type="text/css" href="selector.min.css">
|
||||
<script type="application/javascript" src="selector.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body style="background-color:#000;">
|
||||
|
||||
<!--
|
||||
Top Line
|
||||
-->
|
||||
|
||||
<div id="status" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 3ex;">not available
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
Buttons Line
|
||||
|
||||
<div></div>
|
||||
<div class="buttons-container" id="text">
|
||||
<div>
|
||||
<webaudio-switch id="select" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/select.png" type="toggle" onclick="socket.emit('message', '/select');"></webaudio-switch>
|
||||
</div>
|
||||
<div>
|
||||
<webaudio-switch id="rate" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/rate.png" type="toggle" onclick="socket.emit('message', '/rate');"></webaudio-switch>
|
||||
</div>
|
||||
<div>
|
||||
<webaudio-switch id="steps" value="1" height="35" width="100" tooltip="Switch-B" src="knobs/steps.png" type="toggle" onclick="socket.emit('message', '/steps');"></webaudio-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
-->
|
||||
<!--
|
||||
OCS 2 encoders 1 x 8 horizontal grid
|
||||
-->
|
||||
<div class="mainGrid">
|
||||
|
||||
<!-- VCO 1 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
VCO 1
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/0" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -0</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/1" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WF -1</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/2" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -2</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/3" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -3</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- VCO 2 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
VCO 2
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/5" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -5</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/6" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WF -6</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/7" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -7</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/8" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -8</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- VCF vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
VCF
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/9" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -9</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/10" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Q -10</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/11" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 1 -11</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/12" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">Mod 2 -12</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- LFO 1 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 1
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/13" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -13</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/14" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WF -14</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/15" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">SYM -15</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/16" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">ATTACK -16</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LFO 2 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 2
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/17" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -17</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/18" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WF -18</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/19" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">SYM -19</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/20" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">DECAY -20</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LFO 3 vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
LFO 3
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/21" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">FQ -21</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/22" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">MOD -22</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/23" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">WET EFFECT -23</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/24" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">SUSTAIN -24</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CV GEN vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
CV GEN
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/25" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">PARAM 1 -25</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/26" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">PARAM 2 -26</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/27" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">MOD EFFECT -27</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/28" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">RELEASE -28</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VCA vertical Grid -->
|
||||
<div class="encodersGrid">
|
||||
<div id="title">
|
||||
VCA
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/29" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">MIX 1/2 -29</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/30" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">MOD -30</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<div><webaudio-knob id="ocs2/cc/31" diameter="70" min="0" max="127" value="64"></webaudio-knob> </div>
|
||||
<div></div>
|
||||
<div id="text">VOL -31</div>
|
||||
</div>
|
||||
<div class="encoders">
|
||||
<webaudio-switch id="ocs2/reset" value="1" height="70" width="70" tooltip="Switch-B" src="knobs/rebuild.png" type="toggle"></webaudio-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- LJ style WS : A nettoyer ! -->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
<!-- var LJ = 'ws://xrkia.org:8081/' -->
|
||||
<!-- var LJ = 'ws://laser.teamlaser.fr:8081/' -->
|
||||
var LJ = 'ws://127.0.0.1:8081/'
|
||||
var _WS = {
|
||||
uri: LJ,
|
||||
ws: null,
|
||||
|
||||
init : function (e) {
|
||||
_WS.s = new WebSocket(_WS.uri);
|
||||
_WS.s.onopen = function (e) { _WS.onOpen(e); };
|
||||
_WS.s.onclose = function (e) { _WS.onClose(e); };
|
||||
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
|
||||
_WS.s.onerror = function (e) { _WS.onError(e); };
|
||||
},
|
||||
|
||||
onOpen: function () {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= "Connected";
|
||||
},
|
||||
|
||||
onClose: function () {
|
||||
_WS.showout('DISCONNECTED');
|
||||
|
||||
},
|
||||
|
||||
onMessage: function (e) {
|
||||
var res = e.data.split(" ");
|
||||
console.log(e.data)
|
||||
//console.log(res[0].substring(0,6))
|
||||
//console.log(res)
|
||||
//console.log(res[0].slice(1))
|
||||
var divtext = document.getElementById('status');
|
||||
var divtextp = document.getElementById('players');
|
||||
|
||||
|
||||
switch (res[0].substring(0,6)) {
|
||||
|
||||
case "/statu":
|
||||
divtext.innerHTML="OCS-2";
|
||||
break;
|
||||
case "/playe":
|
||||
divtext.innerHTML="OCS-2 ("+res[1]+" player(s))";
|
||||
divtextp.innerHTML=" ("+res[1]+" player(s))";
|
||||
break;
|
||||
case "/simul":
|
||||
pl = e.data.slice(7);
|
||||
//console.log(pl)
|
||||
pl2 = eval(pl.replace(/[()]/g, ''));
|
||||
break;
|
||||
case "/plpoi":
|
||||
//console.log("plpoint");
|
||||
break;
|
||||
default:
|
||||
//console.log(e);
|
||||
document.getElementById(res[0].slice(1)).value = res[1];
|
||||
_WS.showin(e.data);
|
||||
}
|
||||
},
|
||||
|
||||
onError: function (e) {
|
||||
_WS.showin('<span style="color: red;">ERROR:</span> ' + e.data);
|
||||
},
|
||||
|
||||
showin: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showout: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
showstatus: function (message) {
|
||||
var divtext = document.getElementById('status');
|
||||
divtext.innerHTML="";
|
||||
divtext.innerHTML= message.toString();
|
||||
},
|
||||
|
||||
send: function (message) {
|
||||
if (!message.length) {
|
||||
alert('Empty message not allowed !');
|
||||
} else {
|
||||
_WS.showout(message);
|
||||
_WS.s.send(message);
|
||||
}
|
||||
},
|
||||
|
||||
close: function () {
|
||||
_WS.showout('GOODBYE !');
|
||||
_WS.s.close();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', _WS.init, false);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
web audio encoders scripts
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var message="";
|
||||
var log=[];
|
||||
var knobs = document.getElementsByTagName('webaudio-knob');
|
||||
|
||||
for(var i = 0; i < knobs.length; i++){
|
||||
knobs[i].addEventListener("input",Dump,false);
|
||||
knobs[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var sliders = document.getElementsByTagName('webaudio-slider');
|
||||
|
||||
for(var i = 0; i < sliders.length; i++){
|
||||
sliders[i].addEventListener("input",Dump,false);
|
||||
sliders[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
var switches = document.getElementsByTagName('webaudio-switch');
|
||||
|
||||
for(var i = 0; i < switches.length; i++) {
|
||||
switches[i].addEventListener("change",Dump,false);
|
||||
}
|
||||
|
||||
function Dump(e) {
|
||||
var str="";
|
||||
str=e.type + " : " + e.target.id + " : " + e.target.value + " ";
|
||||
console.log(str);
|
||||
log.unshift(str);
|
||||
log.length=1;
|
||||
str="";
|
||||
|
||||
for(var i=19;i>=0;--i) {
|
||||
if(log[i])
|
||||
str+=log[i]+"<br/>";
|
||||
}
|
||||
//var evview=document.getElementById("events");
|
||||
//evview.innerHTML=str;
|
||||
//console.log( e.type + "/" + e.target.id + "/" + e.target.value);
|
||||
//console.log('/' + e.target.id + ' ' + e.target.value);
|
||||
//socket.emit('message', '/' + e.target.id + ' ' + e.target.value);
|
||||
_WS.send("/" + e.target.id + " " + e.target.value);
|
||||
|
||||
if (e.target.id === "on" && e.type === "change") {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
if (e.target.id === "rate" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
if (e.target.id === "range" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
if (e.target.id === "select" && e.type === "change") {
|
||||
e.target.value = 1 ;
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="title" align ="center" style="font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
|
||||
text-align: center;color: #ddd;font-size: 2ex;">
|
||||
<a href="mmo3.html">MMO-3</a>
|
||||
<span id="players"></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
1
web/selector.min.css
vendored
Executable file
1
web/selector.min.css
vendored
Executable file
@ -0,0 +1 @@
|
||||
div.selector-element{font-size: 0.75em; display:inline-block;position:relative;width:100%;max-width: 85px;height: 24px;font-family:sans-serif;color: #fbfff5;background-color: #111111}div.selector-element div.selector-selected{display:inline-block;width:100%;height:100%;position:relative;cursor:pointer;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;line-height:30px;padding:0 12px;transition:.15s ease-in-out;border:1px solid gray}div.selector-element div.selector-selected:hover{background-color: #060606}div.selector-element div.selector-selected:after{content:'';position:absolute;top:50%;right:22px;transform:translateY(-50%);height:0;width:0;border-top:5px solid silver;border-left:5px solid transparent;border-right:5px solid transparent}div.selector-element div.selector-selected p.selected-text{display:inline-block;margin:0;width:100%;height:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-right:30px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.selector-element div.selector-options{color: #dbdbdb; display:none;position:absolute;background-color: #111111;top:100%;width:100%;height:auto;max-height:156px;overflow:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:1px solid grey;border-top:0;box-shadow:0 2px 3px rgba(0,0,0,0.2);z-index:1;transition:.2s ease-in-out}div.selector-element div.selector-options.options-search{max-height:220px}div.selector-element.open div.selector-options{display:block}div.selector-element div.selector-options div.selector-option{display:block;position:relative;width:100%;height:32px;border-bottom:1px solid silver;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0 12px;line-height:31px;cursor:pointer;overflow:hidden;transition:.15s ease-in-out}div.selector-element div.selector-options div.selector-option.hide{height:0;border-bottom:0;transition:.1s ease-in-out}div.selector-element div.selector-options div.selector-option.option-selected{background-color: #111111}div.selector-element div.selector-options div.selector-option:hover{color: #fbfff5; background-color: #111111}div.selector-element div.selector-options div.selector-option:last-of-type{border-bottom:0}div.selector-element div.selector-options div.selector-option.option-disabled{opacity:.6}div.selector-element div.selector-options div.option-search{display:block;height:32px;line-height:33px;position:relative;width:100%;border-bottom:1px solid grey}div.selector-element div.selector-options div.option-search span{position:absolute;top:50%;transform:translateY(-48%) !important;height:14px;width:14px;cursor:pointer;z-index:1;background:url("../knobs/close.svg") center no-repeat;background-size:11px;right:12px}div.selector-element div.selector-options div.option-search input{display:inline-block;width:100%;height:100%;padding:0 36px 0 36px;font-family:sans-serif;font-size:1em;color: white;outline:0;border:0;background:url("../knobs/search.svg") left 13px center no-repeat;background-size:13px}
|
1
web/selector.min.js
vendored
Executable file
1
web/selector.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1875
web/webaudio-controls.js
Executable file
1875
web/webaudio-controls.js
Executable file
File diff suppressed because it is too large
Load Diff
197
web/webcomponents-lite.js
Executable file
197
web/webcomponents-lite.js
Executable file
@ -0,0 +1,197 @@
|
||||
(function(){/*
|
||||
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
'use strict';var p,q="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,ba="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};function ca(){ca=function(){};q.Symbol||(q.Symbol=da)}var da=function(){var a=0;return function(b){return"jscomp_symbol_"+(b||"")+a++}}();
|
||||
function ea(){ca();var a=q.Symbol.iterator;a||(a=q.Symbol.iterator=q.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&ba(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return fa(this)}});ea=function(){}}function fa(a){var b=0;return ha(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})}function ha(a){ea();a={next:a};a[q.Symbol.iterator]=function(){return this};return a}function ia(a){ea();var b=a[Symbol.iterator];return b?b.call(a):fa(a)}
|
||||
function ja(a){for(var b,c=[];!(b=a.next()).done;)c.push(b.value);return c}
|
||||
(function(){if(!function(){var a=document.createEvent("Event");a.initEvent("foo",!0,!0);a.preventDefault();return a.defaultPrevented}()){var a=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(a.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var b=/Trident/.test(navigator.userAgent);if(!window.CustomEvent||b&&"function"!==typeof window.CustomEvent)window.CustomEvent=function(a,b){b=b||{};var c=document.createEvent("CustomEvent");
|
||||
c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c},window.CustomEvent.prototype=window.Event.prototype;if(!window.Event||b&&"function"!==typeof window.Event){var c=window.Event;window.Event=function(a,b){b=b||{};var c=document.createEvent("Event");c.initEvent(a,!!b.bubbles,!!b.cancelable);return c};if(c)for(var d in c)window.Event[d]=c[d];window.Event.prototype=c.prototype}if(!window.MouseEvent||b&&"function"!==typeof window.MouseEvent){b=window.MouseEvent;window.MouseEvent=function(a,
|
||||
b){b=b||{};var c=document.createEvent("MouseEvent");c.initMouseEvent(a,!!b.bubbles,!!b.cancelable,b.view||window,b.detail,b.screenX,b.screenY,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};if(b)for(d in b)window.MouseEvent[d]=b[d];window.MouseEvent.prototype=b.prototype}Array.from||(Array.from=function(a){return[].slice.call(a)});Object.assign||(Object.assign=function(a,b){for(var c=[].slice.call(arguments,1),d=0,e;d<c.length;d++)if(e=c[d])for(var f=
|
||||
a,m=e,n=Object.getOwnPropertyNames(m),t=0;t<n.length;t++)e=n[t],f[e]=m[e];return a})})(window.WebComponents);(function(){function a(){}function b(a,b){if(!a.childNodes.length)return[];switch(a.nodeType){case Node.DOCUMENT_NODE:return t.call(a,b);case Node.DOCUMENT_FRAGMENT_NODE:return C.call(a,b);default:return n.call(a,b)}}var c="undefined"===typeof HTMLTemplateElement,d=!(document.createDocumentFragment().cloneNode()instanceof DocumentFragment),e=!1;/Trident/.test(navigator.userAgent)&&function(){function a(a,b){if(a instanceof DocumentFragment)for(var d;d=a.firstChild;)c.call(this,d,b);else c.call(this,
|
||||
a,b);return a}e=!0;var b=Node.prototype.cloneNode;Node.prototype.cloneNode=function(a){a=b.call(this,a);this instanceof DocumentFragment&&(a.__proto__=DocumentFragment.prototype);return a};DocumentFragment.prototype.querySelectorAll=HTMLElement.prototype.querySelectorAll;DocumentFragment.prototype.querySelector=HTMLElement.prototype.querySelector;Object.defineProperties(DocumentFragment.prototype,{nodeType:{get:function(){return Node.DOCUMENT_FRAGMENT_NODE},configurable:!0},localName:{get:function(){},
|
||||
configurable:!0},nodeName:{get:function(){return"#document-fragment"},configurable:!0}});var c=Node.prototype.insertBefore;Node.prototype.insertBefore=a;var d=Node.prototype.appendChild;Node.prototype.appendChild=function(b){b instanceof DocumentFragment?a.call(this,b,null):d.call(this,b);return b};var f=Node.prototype.removeChild,h=Node.prototype.replaceChild;Node.prototype.replaceChild=function(b,c){b instanceof DocumentFragment?(a.call(this,b,c),f.call(this,c)):h.call(this,b,c);return c};Document.prototype.createDocumentFragment=
|
||||
function(){var a=this.createElement("df");a.__proto__=DocumentFragment.prototype;return a};var g=Document.prototype.importNode;Document.prototype.importNode=function(a,b){b=g.call(this,a,b||!1);a instanceof DocumentFragment&&(b.__proto__=DocumentFragment.prototype);return b}}();var f=Node.prototype.cloneNode,h=Document.prototype.createElement,g=Document.prototype.importNode,k=Node.prototype.removeChild,l=Node.prototype.appendChild,m=Node.prototype.replaceChild,n=Element.prototype.querySelectorAll,
|
||||
t=Document.prototype.querySelectorAll,C=DocumentFragment.prototype.querySelectorAll,eb=function(){if(!c){var a=document.createElement("template"),b=document.createElement("template");b.content.appendChild(document.createElement("div"));a.content.appendChild(b);a=a.cloneNode(!0);return 0===a.content.childNodes.length||0===a.content.firstChild.content.childNodes.length||d}}();if(c){var J=document.implementation.createHTMLDocument("template"),Ca=!0,Da=document.createElement("style");Da.textContent="template{display:none;}";
|
||||
var Ea=document.head;Ea.insertBefore(Da,Ea.firstElementChild);a.prototype=Object.create(HTMLElement.prototype);var x=!document.createElement("div").hasOwnProperty("innerHTML");a.D=function(b){if(!b.content){b.content=J.createDocumentFragment();for(var c;c=b.firstChild;)l.call(b.content,c);if(x)b.__proto__=a.prototype;else if(b.cloneNode=function(b){return a.ca(this,b)},Ca)try{na(b),aa(b)}catch(Mg){Ca=!1}a.J(b.content)}};var na=function(b){Object.defineProperty(b,"innerHTML",{get:function(){for(var a=
|
||||
"",b=this.content.firstChild;b;b=b.nextSibling)a+=b.outerHTML||b.data.replace(oa,U);return a},set:function(b){J.body.innerHTML=b;for(a.J(J);this.content.firstChild;)k.call(this.content,this.content.firstChild);for(;J.body.firstChild;)l.call(this.content,J.body.firstChild)},configurable:!0})},aa=function(a){Object.defineProperty(a,"outerHTML",{get:function(){return"<template>"+this.innerHTML+"</template>"},set:function(a){if(this.parentNode){J.body.innerHTML=a;for(a=this.ownerDocument.createDocumentFragment();J.body.firstChild;)l.call(a,
|
||||
J.body.firstChild);m.call(this.parentNode,a,this)}else throw Error("Failed to set the 'outerHTML' property on 'Element': This element has no parent node.");},configurable:!0})};na(a.prototype);aa(a.prototype);a.J=function(c){c=b(c,"template");for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)a.D(f)};document.addEventListener("DOMContentLoaded",function(){a.J(document)});Document.prototype.createElement=function(){var b=h.apply(this,arguments);"template"===b.localName&&a.D(b);return b};var oa=/[&\u00A0<>]/g,
|
||||
U=function(a){switch(a){case "&":return"&";case "<":return"<";case ">":return">";case "\u00a0":return" "}}}if(c||eb){a.ca=function(a,b){var c=f.call(a,!1);this.D&&this.D(c);b&&(l.call(c.content,f.call(a.content,!0)),fb(c.content,a.content));return c};var fb=function(c,d){if(d.querySelectorAll&&(d=b(d,"template"),0!==d.length)){c=b(c,"template");for(var e=0,f=c.length,h,g;e<f;e++)g=d[e],h=c[e],a&&a.D&&a.D(g),m.call(h.parentNode,pa.call(g,!0),h)}},pa=Node.prototype.cloneNode=function(b){if(!e&&
|
||||
d&&this instanceof DocumentFragment)if(b)var c=qa.call(this.ownerDocument,this,!0);else return this.ownerDocument.createDocumentFragment();else c=this.nodeType===Node.ELEMENT_NODE&&"template"===this.localName?a.ca(this,b):f.call(this,b);b&&fb(c,this);return c},qa=Document.prototype.importNode=function(c,d){d=d||!1;if("template"===c.localName)return a.ca(c,d);var e=g.call(this,c,d);if(d){fb(e,c);c=b(e,'script:not([type]),script[type="application/javascript"],script[type="text/javascript"]');for(var f,
|
||||
k=0;k<c.length;k++){f=c[k];d=h.call(document,"script");d.textContent=f.textContent;for(var l=f.attributes,qa=0,pa;qa<l.length;qa++)pa=l[qa],d.setAttribute(pa.name,pa.value);m.call(f.parentNode,d,f)}}return e}}c&&(window.HTMLTemplateElement=a)})();var ka=Array.isArray?Array.isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)};var la=0,ma,ra="undefined"!==typeof window?window:void 0,sa=ra||{},ta=sa.MutationObserver||sa.WebKitMutationObserver,ua="undefined"!==typeof Uint8ClampedArray&&"undefined"!==typeof importScripts&&"undefined"!==typeof MessageChannel;function va(){return"undefined"!==typeof ma?function(){ma(wa)}:xa()}function ya(){var a=0,b=new ta(wa),c=document.createTextNode("");b.observe(c,{characterData:!0});return function(){c.data=a=++a%2}}
|
||||
function za(){var a=new MessageChannel;a.port1.onmessage=wa;return function(){return a.port2.postMessage(0)}}function xa(){var a=setTimeout;return function(){return a(wa,1)}}var Aa=Array(1E3);function wa(){for(var a=0;a<la;a+=2)(0,Aa[a])(Aa[a+1]),Aa[a]=void 0,Aa[a+1]=void 0;la=0}var Ba,Fa;
|
||||
if("undefined"===typeof self&&"undefined"!==typeof process&&"[object process]"==={}.toString.call(process))Fa=function(){return process.ib(wa)};else{var Ga;if(ta)Ga=ya();else{var Ha;if(ua)Ha=za();else{var Ia;if(void 0===ra&&"function"===typeof require)try{var Ja=require("vertx");ma=Ja.kb||Ja.jb;Ia=va()}catch(a){Ia=xa()}else Ia=xa();Ha=Ia}Ga=Ha}Fa=Ga}Ba=Fa;function Ka(a,b){Aa[la]=a;Aa[la+1]=b;la+=2;2===la&&Ba()};function La(a,b){var c=this,d=new this.constructor(Ma);void 0===d[Na]&&Oa(d);var e=c.g;if(e){var f=arguments[e-1];Ka(function(){return Pa(e,d,f,c.f)})}else Qa(c,d,a,b);return d};function Ra(a){if(a&&"object"===typeof a&&a.constructor===this)return a;var b=new this(Ma);Sa(b,a);return b};var Na=Math.random().toString(36).substring(16);function Ma(){}var Ua=new Ta;function Va(a){try{return a.then}catch(b){return Ua.error=b,Ua}}function Wa(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function Xa(a,b,c){Ka(function(a){var d=!1,f=Wa(c,b,function(c){d||(d=!0,b!==c?Sa(a,c):r(a,c))},function(b){d||(d=!0,u(a,b))});!d&&f&&(d=!0,u(a,f))},a)}function Ya(a,b){1===b.g?r(a,b.f):2===b.g?u(a,b.f):Qa(b,void 0,function(b){return Sa(a,b)},function(b){return u(a,b)})}
|
||||
function Za(a,b,c){b.constructor===a.constructor&&c===La&&b.constructor.resolve===Ra?Ya(a,b):c===Ua?(u(a,Ua.error),Ua.error=null):void 0===c?r(a,b):"function"===typeof c?Xa(a,b,c):r(a,b)}function Sa(a,b){if(a===b)u(a,new TypeError("You cannot resolve a promise with itself"));else{var c=typeof b;null===b||"object"!==c&&"function"!==c?r(a,b):Za(a,b,Va(b))}}function $a(a){a.na&&a.na(a.f);ab(a)}function r(a,b){void 0===a.g&&(a.f=b,a.g=1,0!==a.I.length&&Ka(ab,a))}
|
||||
function u(a,b){void 0===a.g&&(a.g=2,a.f=b,Ka($a,a))}function Qa(a,b,c,d){var e=a.I,f=e.length;a.na=null;e[f]=b;e[f+1]=c;e[f+2]=d;0===f&&a.g&&Ka(ab,a)}function ab(a){var b=a.I,c=a.g;if(0!==b.length){for(var d,e,f=a.f,h=0;h<b.length;h+=3)d=b[h],e=b[h+c],d?Pa(c,d,e,f):e(f);a.I.length=0}}function Ta(){this.error=null}var bb=new Ta;
|
||||
function Pa(a,b,c,d){var e="function"===typeof c;if(e){try{var f=c(d)}catch(l){bb.error=l,f=bb}if(f===bb){var h=!0;var g=f.error;f.error=null}else var k=!0;if(b===f){u(b,new TypeError("A promises callback cannot return that same promise."));return}}else f=d,k=!0;void 0===b.g&&(e&&k?Sa(b,f):h?u(b,g):1===a?r(b,f):2===a&&u(b,f))}function cb(a,b){try{b(function(b){Sa(a,b)},function(b){u(a,b)})}catch(c){u(a,c)}}var db=0;function Oa(a){a[Na]=db++;a.g=void 0;a.f=void 0;a.I=[]};function gb(a,b){this.Ea=a;this.A=new a(Ma);this.A[Na]||Oa(this.A);if(ka(b))if(this.S=this.length=b.length,this.f=Array(this.length),0===this.length)r(this.A,this.f);else{this.length=this.length||0;for(a=0;void 0===this.g&&a<b.length;a++)hb(this,b[a],a);0===this.S&&r(this.A,this.f)}else u(this.A,Error("Array Methods must be provided an Array"))}
|
||||
function hb(a,b,c){var d=a.Ea,e=d.resolve;e===Ra?(e=Va(b),e===La&&void 0!==b.g?ib(a,b.g,c,b.f):"function"!==typeof e?(a.S--,a.f[c]=b):d===v?(d=new d(Ma),Za(d,b,e),jb(a,d,c)):jb(a,new d(function(a){return a(b)}),c)):jb(a,e(b),c)}function ib(a,b,c,d){var e=a.A;void 0===e.g&&(a.S--,2===b?u(e,d):a.f[c]=d);0===a.S&&r(e,a.f)}function jb(a,b,c){Qa(b,void 0,function(b){return ib(a,1,c,b)},function(b){return ib(a,2,c,b)})};function kb(a){return(new gb(this,a)).A};function lb(a){var b=this;return ka(a)?new b(function(c,d){for(var e=a.length,f=0;f<e;f++)b.resolve(a[f]).then(c,d)}):new b(function(a,b){return b(new TypeError("You must pass an array to race."))})};function mb(a){var b=new this(Ma);u(b,a);return b};function v(a){this[Na]=db++;this.f=this.g=void 0;this.I=[];if(Ma!==a){if("function"!==typeof a)throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(this instanceof v)cb(this,a);else throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");}}v.prototype={constructor:v,then:La,a:function(a){return this.then(null,a)}};/*
|
||||
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
window.Promise||(window.Promise=v,v.prototype["catch"]=v.prototype.a,v.prototype.then=v.prototype.then,v.all=kb,v.race=lb,v.resolve=Ra,v.reject=mb);(function(a){function b(a,b){if("function"===typeof window.CustomEvent)return new CustomEvent(a,b);var c=document.createEvent("CustomEvent");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c}function c(a){if(C)return a.ownerDocument!==document?a.ownerDocument:null;var b=a.__importDoc;if(!b&&a.parentNode){b=a.parentNode;if("function"===typeof b.closest)b=b.closest("link[rel=import]");else for(;!g(b)&&(b=b.parentNode););a.__importDoc=b}return b}function d(a){var b=m(document,"link[rel=import]:not([import-dependency])"),
|
||||
c=b.length;c?n(b,function(b){return h(b,function(){0===--c&&a()})}):a()}function e(a){function b(){"loading"!==document.readyState&&document.body&&(document.removeEventListener("readystatechange",b),a())}document.addEventListener("readystatechange",b);b()}function f(a){e(function(){return d(function(){return a&&a()})})}function h(a,b){if(a.__loaded)b&&b();else if("script"===a.localName&&!a.src||"style"===a.localName&&!a.firstChild)a.__loaded=!0,b&&b();else{var c=function(d){a.removeEventListener(d.type,
|
||||
c);a.__loaded=!0;b&&b()};a.addEventListener("load",c);aa&&"style"===a.localName||a.addEventListener("error",c)}}function g(a){return a.nodeType===Node.ELEMENT_NODE&&"link"===a.localName&&"import"===a.rel}function k(){var a=this;this.a={};this.b=0;this.c=new MutationObserver(function(b){return a.Ra(b)});this.c.observe(document.head,{childList:!0,subtree:!0});this.loadImports(document)}function l(a){n(m(a,"template"),function(a){n(m(a.content,'script:not([type]),script[type="application/javascript"],script[type="text/javascript"]'),
|
||||
function(a){var b=document.createElement("script");n(a.attributes,function(a){return b.setAttribute(a.name,a.value)});b.textContent=a.textContent;a.parentNode.replaceChild(b,a)});l(a.content)})}function m(a,b){return a.childNodes.length?a.querySelectorAll(b):eb}function n(a,b,c){var d=a?a.length:0,e=c?-1:1;for(c=c?d-1:0;c<d&&0<=c;c+=e)b(a[c],c)}var t=document.createElement("link"),C="import"in t,eb=t.querySelectorAll("*"),J=null;!1==="currentScript"in document&&Object.defineProperty(document,"currentScript",
|
||||
{get:function(){return J||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null)},configurable:!0});var Ca=/(url\()([^)]*)(\))/g,Da=/(@import[\s]+(?!url\())([^;]*)(;)/g,Ea=/(<link[^>]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g,x={La:function(a,b){a.href&&a.setAttribute("href",x.Y(a.getAttribute("href"),b));a.src&&a.setAttribute("src",x.Y(a.getAttribute("src"),b));if("style"===a.localName){var c=x.ta(a.textContent,b,Ca);a.textContent=x.ta(c,b,Da)}},ta:function(a,b,c){return a.replace(c,
|
||||
function(a,c,d,e){a=d.replace(/["']/g,"");b&&(a=x.Y(a,b));return c+"'"+a+"'"+e})},Y:function(a,b){if(void 0===x.ba){x.ba=!1;try{var c=new URL("b","http://a");c.pathname="c%20d";x.ba="http://a/c%20d"===c.href}catch(Lg){}}if(x.ba)return(new URL(a,b)).href;c=x.Ba;c||(c=document.implementation.createHTMLDocument("temp"),x.Ba=c,c.ma=c.createElement("base"),c.head.appendChild(c.ma),c.la=c.createElement("a"));c.ma.href=b;c.la.href=a;return c.la.href||a}},na={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=
|
||||
a.split(",");var d=a[1];d=-1<a[0].indexOf(";base64")?atob(d):decodeURIComponent(d);b(d)}else{var e=new XMLHttpRequest;e.open("GET",a,na.async);e.onload=function(){var a=e.responseURL||e.getResponseHeader("Location");a&&0===a.indexOf("/")&&(a=(location.origin||location.protocol+"//"+location.host)+a);var d=e.response||e.responseText;304===e.status||0===e.status||200<=e.status&&300>e.status?b(d,a):c(d)};e.send()}else c("error: href must be specified")}},aa=/Trident/.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent);
|
||||
k.prototype.loadImports=function(a){var b=this;a=m(a,"link[rel=import]");n(a,function(a){return b.s(a)})};k.prototype.s=function(a){var b=this,c=a.href;if(void 0!==this.a[c]){var d=this.a[c];d&&d.__loaded&&(a.__import=d,this.h(a))}else this.b++,this.a[c]="pending",na.load(c,function(a,d){a=b.Sa(a,d||c);b.a[c]=a;b.b--;b.loadImports(a);b.L()},function(){b.a[c]=null;b.b--;b.L()})};k.prototype.Sa=function(a,b){if(!a)return document.createDocumentFragment();aa&&(a=a.replace(Ea,function(a,b,c){return-1===
|
||||
a.indexOf("type=")?b+" type=import-disable "+c:a}));var c=document.createElement("template");c.innerHTML=a;if(c.content)a=c.content,l(a);else for(a=document.createDocumentFragment();c.firstChild;)a.appendChild(c.firstChild);if(c=a.querySelector("base"))b=x.Y(c.getAttribute("href"),b),c.removeAttribute("href");c=m(a,'link[rel=import],link[rel=stylesheet][href][type=import-disable],style:not([type]),link[rel=stylesheet][href]:not([type]),script:not([type]),script[type="application/javascript"],script[type="text/javascript"]');
|
||||
var d=0;n(c,function(a){h(a);x.La(a,b);a.setAttribute("import-dependency","");"script"===a.localName&&!a.src&&a.textContent&&(a.setAttribute("src","data:text/javascript;charset=utf-8,"+encodeURIComponent(a.textContent+("\n//# sourceURL="+b+(d?"-"+d:"")+".js\n"))),a.textContent="",d++)});return a};k.prototype.L=function(){var a=this;if(!this.b){this.c.disconnect();this.flatten(document);var b=!1,c=!1,d=function(){c&&b&&(a.loadImports(document),a.b||(a.c.observe(document.head,{childList:!0,subtree:!0}),
|
||||
a.Pa()))};this.Ua(function(){c=!0;d()});this.Ta(function(){b=!0;d()})}};k.prototype.flatten=function(a){var b=this;a=m(a,"link[rel=import]");n(a,function(a){var c=b.a[a.href];(a.__import=c)&&c.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(b.a[a.href]=a,a.readyState="loading",a.__import=a,b.flatten(c),a.appendChild(c))})};k.prototype.Ta=function(a){function b(e){if(e<d){var f=c[e],g=document.createElement("script");f.removeAttribute("import-dependency");n(f.attributes,function(a){return g.setAttribute(a.name,
|
||||
a.value)});J=g;f.parentNode.replaceChild(g,f);h(g,function(){J=null;b(e+1)})}else a()}var c=m(document,"script[import-dependency]"),d=c.length;b(0)};k.prototype.Ua=function(a){var b=m(document,"style[import-dependency],link[rel=stylesheet][import-dependency]"),d=b.length;if(d){var e=aa&&!!document.querySelector("link[rel=stylesheet][href][type=import-disable]");n(b,function(b){h(b,function(){b.removeAttribute("import-dependency");0===--d&&a()});if(e&&b.parentNode!==document.head){var f=document.createElement(b.localName);
|
||||
f.__appliedElement=b;f.setAttribute("type","import-placeholder");b.parentNode.insertBefore(f,b.nextSibling);for(f=c(b);f&&c(f);)f=c(f);f.parentNode!==document.head&&(f=null);document.head.insertBefore(b,f);b.removeAttribute("type")}})}else a()};k.prototype.Pa=function(){var a=this,b=m(document,"link[rel=import]");n(b,function(b){return a.h(b)},!0)};k.prototype.h=function(a){a.__loaded||(a.__loaded=!0,a.import&&(a.import.readyState="complete"),a.dispatchEvent(b(a.import?"load":"error",{bubbles:!1,
|
||||
cancelable:!1,detail:void 0})))};k.prototype.Ra=function(a){var b=this;n(a,function(a){return n(a.addedNodes,function(a){a&&a.nodeType===Node.ELEMENT_NODE&&(g(a)?b.s(a):b.loadImports(a))})})};var oa=null;if(C)t=m(document,"link[rel=import]"),n(t,function(a){a.import&&"loading"===a.import.readyState||(a.__loaded=!0)}),t=function(a){a=a.target;g(a)&&(a.__loaded=!0)},document.addEventListener("load",t,!0),document.addEventListener("error",t,!0);else{var U=Object.getOwnPropertyDescriptor(Node.prototype,
|
||||
"baseURI");Object.defineProperty((!U||U.configurable?Node:Element).prototype,"baseURI",{get:function(){var a=g(this)?this:c(this);return a?a.href:U&&U.get?U.get.call(this):(document.querySelector("base")||window.location).href},configurable:!0,enumerable:!0});Object.defineProperty(HTMLLinkElement.prototype,"import",{get:function(){return this.__import||null},configurable:!0,enumerable:!0});e(function(){oa=new k})}f(function(){return document.dispatchEvent(b("HTMLImportsLoaded",{cancelable:!0,bubbles:!0,
|
||||
detail:void 0}))});a.useNative=C;a.whenReady=f;a.importForElement=c;a.loadImports=function(a){oa&&oa.loadImports(a)}})(window.HTMLImports=window.HTMLImports||{});/*
|
||||
|
||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
window.WebComponents=window.WebComponents||{flags:{}};var nb=document.querySelector('script[src*="webcomponents-lite.js"]'),ob=/wc-(.+)/,w={};if(!w.noOpts){location.search.slice(1).split("&").forEach(function(a){a=a.split("=");var b;a[0]&&(b=a[0].match(ob))&&(w[b[1]]=a[1]||!0)});if(nb)for(var pb=0,qb;qb=nb.attributes[pb];pb++)"src"!==qb.name&&(w[qb.name]=qb.value||!0);if(w.log&&w.log.split){var rb=w.log.split(",");w.log={};rb.forEach(function(a){w.log[a]=!0})}else w.log={}}
|
||||
window.WebComponents.flags=w;var sb=w.shadydom;sb&&(window.ShadyDOM=window.ShadyDOM||{},window.ShadyDOM.force=sb);var tb=w.register||w.ce;tb&&window.customElements&&(window.customElements.forcePolyfill=tb);/*
|
||||
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
var y=window.ShadyDOM||{};y.Na=!(!Element.prototype.attachShadow||!Node.prototype.getRootNode);var ub=Object.getOwnPropertyDescriptor(Node.prototype,"firstChild");y.M=!!(ub&&ub.configurable&&ub.get);y.sa=y.force||!y.Na;function vb(a){return a.__shady&&void 0!==a.__shady.firstChild}function z(a){return"ShadyRoot"===a.ya}function wb(a){a=a.getRootNode();if(z(a))return a}var xb=Element.prototype,yb=xb.matches||xb.matchesSelector||xb.mozMatchesSelector||xb.msMatchesSelector||xb.oMatchesSelector||xb.webkitMatchesSelector;
|
||||
function zb(a,b){if(a&&b)for(var c=Object.getOwnPropertyNames(b),d=0,e;d<c.length&&(e=c[d]);d++){var f=Object.getOwnPropertyDescriptor(b,e);f&&Object.defineProperty(a,e,f)}}function Ab(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];for(d=0;d<c.length;d++)zb(a,c[d]);return a}function Bb(a,b){for(var c in b)a[c]=b[c]}var Cb=document.createTextNode(""),Db=0,Eb=[];(new MutationObserver(function(){for(;Eb.length;)try{Eb.shift()()}catch(a){throw Cb.textContent=Db++,a;}})).observe(Cb,{characterData:!0});
|
||||
function Fb(a){Eb.push(a);Cb.textContent=Db++}var Gb=!!document.contains;function Hb(a,b){for(;b;){if(b==a)return!0;b=b.parentNode}return!1};var Ib=[],Jb;function Kb(a){Jb||(Jb=!0,Fb(Lb));Ib.push(a)}function Lb(){Jb=!1;for(var a=!!Ib.length;Ib.length;)Ib.shift()();return a}Lb.list=Ib;function Mb(){this.a=!1;this.addedNodes=[];this.removedNodes=[];this.V=new Set}function Nb(a){a.a||(a.a=!0,Fb(function(){Ob(a)}))}function Ob(a){if(a.a){a.a=!1;var b=a.takeRecords();b.length&&a.V.forEach(function(a){a(b)})}}Mb.prototype.takeRecords=function(){if(this.addedNodes.length||this.removedNodes.length){var a=[{addedNodes:this.addedNodes,removedNodes:this.removedNodes}];this.addedNodes=[];this.removedNodes=[];return a}return[]};
|
||||
function Pb(a,b){a.__shady=a.__shady||{};a.__shady.N||(a.__shady.N=new Mb);a.__shady.N.V.add(b);var c=a.__shady.N;return{Ca:b,C:c,Ga:a,takeRecords:function(){return c.takeRecords()}}}function Qb(a){var b=a&&a.C;b&&(b.V.delete(a.Ca),b.V.size||(a.Ga.__shady.N=null))}
|
||||
function Rb(a,b){var c=b.getRootNode();return a.map(function(a){var b=c===a.target.getRootNode();if(b&&a.addedNodes){if(b=Array.from(a.addedNodes).filter(function(a){return c===a.getRootNode()}),b.length)return a=Object.create(a),Object.defineProperty(a,"addedNodes",{value:b,configurable:!0}),a}else if(b)return a}).filter(function(a){return a})};var A={},Sb=Element.prototype.insertBefore,Tb=Element.prototype.removeChild,Ub=Element.prototype.setAttribute,Vb=Element.prototype.removeAttribute,Wb=Element.prototype.cloneNode,Xb=Document.prototype.importNode,Yb=Element.prototype.addEventListener,Zb=Element.prototype.removeEventListener,$b=Window.prototype.addEventListener,ac=Window.prototype.removeEventListener,bc=Element.prototype.dispatchEvent,cc=Element.prototype.querySelector,dc=Element.prototype.querySelectorAll,ec=Node.prototype.contains||
|
||||
HTMLElement.prototype.contains;A.appendChild=Element.prototype.appendChild;A.insertBefore=Sb;A.removeChild=Tb;A.setAttribute=Ub;A.removeAttribute=Vb;A.cloneNode=Wb;A.importNode=Xb;A.addEventListener=Yb;A.removeEventListener=Zb;A.ab=$b;A.bb=ac;A.dispatchEvent=bc;A.querySelector=cc;A.querySelectorAll=dc;A.contains=ec;var fc=/[&\u00A0"]/g,gc=/[&\u00A0<>]/g;function hc(a){switch(a){case "&":return"&";case "<":return"<";case ">":return">";case '"':return""";case "\u00a0":return" "}}function ic(a){for(var b={},c=0;c<a.length;c++)b[a[c]]=!0;return b}var jc=ic("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),kc=ic("style script xmp iframe noembed noframes plaintext noscript".split(" "));
|
||||
function lc(a,b){"template"===a.localName&&(a=a.content);for(var c="",d=b?b(a):a.childNodes,e=0,f=d.length,h;e<f&&(h=d[e]);e++){a:{var g=h;var k=a;var l=b;switch(g.nodeType){case Node.ELEMENT_NODE:for(var m=g.localName,n="<"+m,t=g.attributes,C=0;k=t[C];C++)n+=" "+k.name+'="'+k.value.replace(fc,hc)+'"';n+=">";g=jc[m]?n:n+lc(g,l)+"</"+m+">";break a;case Node.TEXT_NODE:g=g.data;g=k&&kc[k.localName]?g:g.replace(gc,hc);break a;case Node.COMMENT_NODE:g="\x3c!--"+g.data+"--\x3e";break a;default:throw window.console.error(g),
|
||||
Error("not implemented");}}c+=g}return c};var B={},D=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,!1),E=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT,null,!1);function mc(a){var b=[];D.currentNode=a;for(a=D.firstChild();a;)b.push(a),a=D.nextSibling();return b}B.parentNode=function(a){D.currentNode=a;return D.parentNode()};B.firstChild=function(a){D.currentNode=a;return D.firstChild()};B.lastChild=function(a){D.currentNode=a;return D.lastChild()};B.previousSibling=function(a){D.currentNode=a;return D.previousSibling()};
|
||||
B.nextSibling=function(a){D.currentNode=a;return D.nextSibling()};B.childNodes=mc;B.parentElement=function(a){E.currentNode=a;return E.parentNode()};B.firstElementChild=function(a){E.currentNode=a;return E.firstChild()};B.lastElementChild=function(a){E.currentNode=a;return E.lastChild()};B.previousElementSibling=function(a){E.currentNode=a;return E.previousSibling()};B.nextElementSibling=function(a){E.currentNode=a;return E.nextSibling()};
|
||||
B.children=function(a){var b=[];E.currentNode=a;for(a=E.firstChild();a;)b.push(a),a=E.nextSibling();return b};B.innerHTML=function(a){return lc(a,function(a){return mc(a)})};B.textContent=function(a){switch(a.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:a=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1);for(var b="",c;c=a.nextNode();)b+=c.nodeValue;return b;default:return a.nodeValue}};var nc=Object.getOwnPropertyDescriptor(Element.prototype,"innerHTML")||Object.getOwnPropertyDescriptor(HTMLElement.prototype,"innerHTML"),oc=document.implementation.createHTMLDocument("inert"),pc=Object.getOwnPropertyDescriptor(Document.prototype,"activeElement"),qc={parentElement:{get:function(){var a=this.__shady&&this.__shady.parentNode;a&&a.nodeType!==Node.ELEMENT_NODE&&(a=null);return void 0!==a?a:B.parentElement(this)},configurable:!0},parentNode:{get:function(){var a=this.__shady&&this.__shady.parentNode;
|
||||
return void 0!==a?a:B.parentNode(this)},configurable:!0},nextSibling:{get:function(){var a=this.__shady&&this.__shady.nextSibling;return void 0!==a?a:B.nextSibling(this)},configurable:!0},previousSibling:{get:function(){var a=this.__shady&&this.__shady.previousSibling;return void 0!==a?a:B.previousSibling(this)},configurable:!0},className:{get:function(){return this.getAttribute("class")||""},set:function(a){this.setAttribute("class",a)},configurable:!0},nextElementSibling:{get:function(){if(this.__shady&&
|
||||
void 0!==this.__shady.nextSibling){for(var a=this.nextSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return B.nextElementSibling(this)},configurable:!0},previousElementSibling:{get:function(){if(this.__shady&&void 0!==this.__shady.previousSibling){for(var a=this.previousSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return B.previousElementSibling(this)},configurable:!0}},rc={childNodes:{get:function(){if(vb(this)){if(!this.__shady.childNodes){this.__shady.childNodes=
|
||||
[];for(var a=this.firstChild;a;a=a.nextSibling)this.__shady.childNodes.push(a)}var b=this.__shady.childNodes}else b=B.childNodes(this);b.item=function(a){return b[a]};return b},configurable:!0},childElementCount:{get:function(){return this.children.length},configurable:!0},firstChild:{get:function(){var a=this.__shady&&this.__shady.firstChild;return void 0!==a?a:B.firstChild(this)},configurable:!0},lastChild:{get:function(){var a=this.__shady&&this.__shady.lastChild;return void 0!==a?a:B.lastChild(this)},
|
||||
configurable:!0},textContent:{get:function(){if(vb(this)){for(var a=[],b=0,c=this.childNodes,d;d=c[b];b++)d.nodeType!==Node.COMMENT_NODE&&a.push(d.textContent);return a.join("")}return B.textContent(this)},set:function(a){if("undefined"===typeof a||null===a)a="";switch(this.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:for(;this.firstChild;)this.removeChild(this.firstChild);(0<a.length||this.nodeType===Node.ELEMENT_NODE)&&this.appendChild(document.createTextNode(a));break;default:this.nodeValue=
|
||||
a}},configurable:!0},firstElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.firstChild){for(var a=this.firstChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return B.firstElementChild(this)},configurable:!0},lastElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.lastChild){for(var a=this.lastChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return B.lastElementChild(this)},configurable:!0},children:{get:function(){var a=vb(this)?
|
||||
Array.prototype.filter.call(this.childNodes,function(a){return a.nodeType===Node.ELEMENT_NODE}):B.children(this);a.item=function(b){return a[b]};return a},configurable:!0},innerHTML:{get:function(){var a="template"===this.localName?this.content:this;return vb(this)?lc(a):B.innerHTML(a)},set:function(a){for(var b="template"===this.localName?this.content:this;b.firstChild;)b.removeChild(b.firstChild);var c=this.localName;c&&"template"!==c||(c="div");c=oc.createElement(c);for(nc&&nc.set?nc.set.call(c,
|
||||
a):c.innerHTML=a;c.firstChild;)b.appendChild(c.firstChild)},configurable:!0}},sc={shadowRoot:{get:function(){return this.__shady&&this.__shady.Va||null},configurable:!0}},tc={activeElement:{get:function(){var a=pc&&pc.get?pc.get.call(document):y.M?void 0:document.activeElement;if(a&&a.nodeType){var b=!!z(this);if(this===document||b&&this.host!==a&&A.contains.call(this.host,a)){for(b=wb(a);b&&b!==this;)a=b.host,b=wb(a);a=this===document?b?null:a:b===this?a:null}else a=null}else a=null;return a},set:function(){},
|
||||
configurable:!0}};function F(a,b,c){for(var d in b){var e=Object.getOwnPropertyDescriptor(a,d);e&&e.configurable||!e&&c?Object.defineProperty(a,d,b[d]):c&&console.warn("Could not define",d,"on",a)}}function G(a){F(a,qc);F(a,rc);F(a,tc)}var uc=y.M?function(){}:function(a){a.__shady&&a.__shady.za||(a.__shady=a.__shady||{},a.__shady.za=!0,F(a,qc,!0))},vc=y.M?function(){}:function(a){a.__shady&&a.__shady.xa||(a.__shady=a.__shady||{},a.__shady.xa=!0,F(a,rc,!0),F(a,sc,!0))};function wc(a,b,c){uc(a);c=c||null;a.__shady=a.__shady||{};b.__shady=b.__shady||{};c&&(c.__shady=c.__shady||{});a.__shady.previousSibling=c?c.__shady.previousSibling:b.lastChild;var d=a.__shady.previousSibling;d&&d.__shady&&(d.__shady.nextSibling=a);(d=a.__shady.nextSibling=c)&&d.__shady&&(d.__shady.previousSibling=a);a.__shady.parentNode=b;c?c===b.__shady.firstChild&&(b.__shady.firstChild=a):(b.__shady.lastChild=a,b.__shady.firstChild||(b.__shady.firstChild=a));b.__shady.childNodes=null}
|
||||
function xc(a){if(!a.__shady||void 0===a.__shady.firstChild){a.__shady=a.__shady||{};a.__shady.firstChild=B.firstChild(a);a.__shady.lastChild=B.lastChild(a);vc(a);for(var b=a.__shady.childNodes=B.childNodes(a),c=0,d;c<b.length&&(d=b[c]);c++)d.__shady=d.__shady||{},d.__shady.parentNode=a,d.__shady.nextSibling=b[c+1]||null,d.__shady.previousSibling=b[c-1]||null,uc(d)}};function yc(a,b,c){if(b===a)throw Error("Failed to execute 'appendChild' on 'Node': The new child element contains the parent.");if(c){var d=c.__shady&&c.__shady.parentNode;if(void 0!==d&&d!==a||void 0===d&&B.parentNode(c)!==a)throw Error("Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.");}if(c===b)return b;b.parentNode&&zc(b.parentNode,b);d=wb(a);var e;if(e=d)a:{if(!b.__noInsertionPoint){var f;"slot"===b.localName?f=[b]:
|
||||
b.querySelectorAll&&(f=b.querySelectorAll("slot"));if(f&&f.length){e=f;break a}}e=void 0}(f=e)&&d.H.push.apply(d.H,[].concat(f instanceof Array?f:ja(ia(f))));d&&("slot"===a.localName||f)&&Ac(d);if(vb(a)){d=c;vc(a);a.__shady=a.__shady||{};void 0!==a.__shady.firstChild&&(a.__shady.childNodes=null);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){f=b.childNodes;for(e=0;e<f.length;e++)wc(f[e],a,d);b.__shady=b.__shady||{};d=void 0!==b.__shady.firstChild?null:void 0;b.__shady.firstChild=b.__shady.lastChild=
|
||||
d;b.__shady.childNodes=d}else wc(b,a,d);if(Bc(a)){Ac(a.__shady.root);var h=!0}else a.__shady.root&&(h=!0)}h||(h=z(a)?a.host:a,c?(c=Cc(c),A.insertBefore.call(h,b,c)):A.appendChild.call(h,b));Dc(a,b);return b}
|
||||
function zc(a,b){if(b.parentNode!==a)throw Error("The node to be removed is not a child of this node: "+b);var c=wb(b);if(vb(a)){b.__shady=b.__shady||{};a.__shady=a.__shady||{};b===a.__shady.firstChild&&(a.__shady.firstChild=b.__shady.nextSibling);b===a.__shady.lastChild&&(a.__shady.lastChild=b.__shady.previousSibling);var d=b.__shady.previousSibling,e=b.__shady.nextSibling;d&&(d.__shady=d.__shady||{},d.__shady.nextSibling=e);e&&(e.__shady=e.__shady||{},e.__shady.previousSibling=d);b.__shady.parentNode=
|
||||
b.__shady.previousSibling=b.__shady.nextSibling=void 0;void 0!==a.__shady.childNodes&&(a.__shady.childNodes=null);if(Bc(a)){Ac(a.__shady.root);var f=!0}}Ec(b);if(c){(d=a&&"slot"===a.localName)&&(f=!0);Fc(c);e=c.l;for(var h in e)for(var g=e[h],k=0;k<g.length;k++){var l=g[k];if(Hb(b,l)){g.splice(k,1);var m=c.o.indexOf(l);0<=m&&c.o.splice(m,1);k--;if(m=l.__shady.K)for(l=0;l<m.length;l++){var n=m[l],t=B.parentNode(n);t&&A.removeChild.call(t,n)}m=!0}}(m||d)&&Ac(c)}f||(f=z(a)?a.host:a,(!a.__shady.root&&
|
||||
"slot"!==b.localName||f===B.parentNode(b))&&A.removeChild.call(f,b));Dc(a,null,b);return b}function Ec(a){if(a.__shady&&void 0!==a.__shady.ka)for(var b=a.childNodes,c=0,d=b.length,e;c<d&&(e=b[c]);c++)Ec(e);a.__shady&&(a.__shady.ka=void 0)}function Cc(a){var b=a;a&&"slot"===a.localName&&(b=(b=a.__shady&&a.__shady.K)&&b.length?b[0]:Cc(a.nextSibling));return b}function Bc(a){return(a=a&&a.__shady&&a.__shady.root)&&Gc(a)}
|
||||
function Hc(a,b){if("slot"===b)a=a.parentNode,Bc(a)&&Ac(a.__shady.root);else if("slot"===a.localName&&"name"===b&&(b=wb(a))){var c=a.Aa,d=Ic(a);if(d!==c){c=b.l[c];var e=c.indexOf(a);0<=e&&c.splice(e,1);c=b.l[d]||(b.l[d]=[]);c.push(a);1<c.length&&(b.l[d]=Jc(c))}Ac(b)}}function Dc(a,b,c){if(a=a.__shady&&a.__shady.N)b&&a.addedNodes.push(b),c&&a.removedNodes.push(c),Nb(a)}
|
||||
function Kc(a){if(a&&a.nodeType){a.__shady=a.__shady||{};var b=a.__shady.ka;void 0===b&&(b=z(a)?a:(b=a.parentNode)?Kc(b):a,A.contains.call(document.documentElement,a)&&(a.__shady.ka=b));return b}}function Lc(a,b,c){var d=[];Mc(a.childNodes,b,c,d);return d}function Mc(a,b,c,d){for(var e=0,f=a.length,h;e<f&&(h=a[e]);e++){var g;if(g=h.nodeType===Node.ELEMENT_NODE){g=h;var k=b,l=c,m=d,n=k(g);n&&m.push(g);l&&l(n)?g=n:(Mc(g.childNodes,k,l,m),g=void 0)}if(g)break}}var Nc=null;
|
||||
function Oc(a,b,c){Nc||(Nc=window.ShadyCSS&&window.ShadyCSS.ScopingShim);Nc&&"class"===b?Nc.setElementClass(a,c):(A.setAttribute.call(a,b,c),Hc(a,b))}function Pc(a,b){if(a.ownerDocument!==document)return A.importNode.call(document,a,b);var c=A.importNode.call(document,a,!1);if(b){a=a.childNodes;b=0;for(var d;b<a.length;b++)d=Pc(a[b],!0),c.appendChild(d)}return c};var Qc="__eventWrappers"+Date.now(),Rc={blur:!0,focus:!0,focusin:!0,focusout:!0,click:!0,dblclick:!0,mousedown:!0,mouseenter:!0,mouseleave:!0,mousemove:!0,mouseout:!0,mouseover:!0,mouseup:!0,wheel:!0,beforeinput:!0,input:!0,keydown:!0,keyup:!0,compositionstart:!0,compositionupdate:!0,compositionend:!0,touchstart:!0,touchend:!0,touchmove:!0,touchcancel:!0,pointerover:!0,pointerenter:!0,pointerdown:!0,pointermove:!0,pointerup:!0,pointercancel:!0,pointerout:!0,pointerleave:!0,gotpointercapture:!0,lostpointercapture:!0,
|
||||
dragstart:!0,drag:!0,dragenter:!0,dragleave:!0,dragover:!0,drop:!0,dragend:!0,DOMActivate:!0,DOMFocusIn:!0,DOMFocusOut:!0,keypress:!0};function Sc(a,b){var c=[],d=a;for(a=a===window?window:a.getRootNode();d;)c.push(d),d=d.assignedSlot?d.assignedSlot:d.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&d.host&&(b||d!==a)?d.host:d.parentNode;c[c.length-1]===document&&c.push(window);return c}
|
||||
function Tc(a,b){if(!z)return a;a=Sc(a,!0);for(var c=0,d,e,f,h;c<b.length;c++)if(d=b[c],f=d===window?window:d.getRootNode(),f!==e&&(h=a.indexOf(f),e=f),!z(f)||-1<h)return d}
|
||||
var Uc={get composed(){!1!==this.isTrusted&&void 0===this.Z&&(this.Z=Rc[this.type]);return this.Z||!1},composedPath:function(){this.b||(this.b=Sc(this.__target,this.composed));return this.b},get target(){return Tc(this.currentTarget,this.composedPath())},get relatedTarget(){if(!this.$)return null;this.c||(this.c=Sc(this.$,!0));return Tc(this.currentTarget,this.c)},stopPropagation:function(){Event.prototype.stopPropagation.call(this);this.a=!0},stopImmediatePropagation:function(){Event.prototype.stopImmediatePropagation.call(this);
|
||||
this.a=this.h=!0}};function Vc(a){function b(b,d){b=new a(b,d);b.Z=d&&!!d.composed;return b}Bb(b,a);b.prototype=a.prototype;return b}var Wc={focus:!0,blur:!0};function Xc(a){return a.__target!==a.target||a.$!==a.relatedTarget}function Yc(a,b,c){if(c=b.__handlers&&b.__handlers[a.type]&&b.__handlers[a.type][c])for(var d=0,e;(e=c[d])&&(!Xc(a)||a.target!==a.relatedTarget)&&(e.call(b,a),!a.h);d++);}
|
||||
function Zc(a){var b=a.composedPath();Object.defineProperty(a,"currentTarget",{get:function(){return d},configurable:!0});for(var c=b.length-1;0<=c;c--){var d=b[c];Yc(a,d,"capture");if(a.a)return}Object.defineProperty(a,"eventPhase",{get:function(){return Event.AT_TARGET}});var e;for(c=0;c<b.length;c++){d=b[c];var f=d.__shady&&d.__shady.root;if(0===c||f&&f===e)if(Yc(a,d,"bubble"),d!==window&&(e=d.getRootNode()),a.a)break}}
|
||||
function $c(a,b,c,d,e,f){for(var h=0;h<a.length;h++){var g=a[h],k=g.type,l=g.capture,m=g.once,n=g.passive;if(b===g.node&&c===k&&d===l&&e===m&&f===n)return h}return-1}
|
||||
function ad(a,b,c){if(b){var d=typeof b;if("function"===d||"object"===d)if("object"!==d||b.handleEvent&&"function"===typeof b.handleEvent){if(c&&"object"===typeof c){var e=!!c.capture;var f=!!c.once;var h=!!c.passive}else e=!!c,h=f=!1;var g=c&&c.aa||this,k=b[Qc];if(k){if(-1<$c(k,g,a,e,f,h))return}else b[Qc]=[];k=function(e){f&&this.removeEventListener(a,b,c);e.__target||bd(e);if(g!==this){var h=Object.getOwnPropertyDescriptor(e,"currentTarget");Object.defineProperty(e,"currentTarget",{get:function(){return g},
|
||||
configurable:!0})}if(e.composed||-1<e.composedPath().indexOf(g))if(Xc(e)&&e.target===e.relatedTarget)e.eventPhase===Event.BUBBLING_PHASE&&e.stopImmediatePropagation();else if(e.eventPhase===Event.CAPTURING_PHASE||e.bubbles||e.target===g||g instanceof Window){var k="function"===d?b.call(g,e):b.handleEvent&&b.handleEvent(e);g!==this&&(h?(Object.defineProperty(e,"currentTarget",h),h=null):delete e.currentTarget);return k}};b[Qc].push({node:this,type:a,capture:e,once:f,passive:h,cb:k});Wc[a]?(this.__handlers=
|
||||
this.__handlers||{},this.__handlers[a]=this.__handlers[a]||{capture:[],bubble:[]},this.__handlers[a][e?"capture":"bubble"].push(k)):(this instanceof Window?A.ab:A.addEventListener).call(this,a,k,c)}}}
|
||||
function cd(a,b,c){if(b){if(c&&"object"===typeof c){var d=!!c.capture;var e=!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var h=c&&c.aa||this,g=void 0;var k=null;try{k=b[Qc]}catch(l){}k&&(e=$c(k,h,a,d,e,f),-1<e&&(g=k.splice(e,1)[0].cb,k.length||(b[Qc]=void 0)));(this instanceof Window?A.bb:A.removeEventListener).call(this,a,g||b,c);g&&Wc[a]&&this.__handlers&&this.__handlers[a]&&(a=this.__handlers[a][d?"capture":"bubble"],g=a.indexOf(g),-1<g&&a.splice(g,1))}}
|
||||
function dd(){for(var a in Wc)window.addEventListener(a,function(a){a.__target||(bd(a),Zc(a))},!0)}function bd(a){a.__target=a.target;a.$=a.relatedTarget;if(y.M){var b=Object.getPrototypeOf(a);if(!b.hasOwnProperty("__patchProto")){var c=Object.create(b);c.fb=b;zb(c,Uc);b.__patchProto=c}a.__proto__=b.__patchProto}else zb(a,Uc)}var ed=Vc(window.Event),fd=Vc(window.CustomEvent),gd=Vc(window.MouseEvent);function hd(a,b){return{index:a,O:[],U:b}}
|
||||
function id(a,b,c,d){var e=0,f=0,h=0,g=0,k=Math.min(b-e,d-f);if(0==e&&0==f)a:{for(h=0;h<k;h++)if(a[h]!==c[h])break a;h=k}if(b==a.length&&d==c.length){g=a.length;for(var l=c.length,m=0;m<k-h&&jd(a[--g],c[--l]);)m++;g=m}e+=h;f+=h;b-=g;d-=g;if(0==b-e&&0==d-f)return[];if(e==b){for(b=hd(e,0);f<d;)b.O.push(c[f++]);return[b]}if(f==d)return[hd(e,b-e)];k=e;h=f;d=d-h+1;g=b-k+1;b=Array(d);for(l=0;l<d;l++)b[l]=Array(g),b[l][0]=l;for(l=0;l<g;l++)b[0][l]=l;for(l=1;l<d;l++)for(m=1;m<g;m++)if(a[k+m-1]===c[h+l-1])b[l][m]=
|
||||
b[l-1][m-1];else{var n=b[l-1][m]+1,t=b[l][m-1]+1;b[l][m]=n<t?n:t}k=b.length-1;h=b[0].length-1;d=b[k][h];for(a=[];0<k||0<h;)0==k?(a.push(2),h--):0==h?(a.push(3),k--):(g=b[k-1][h-1],l=b[k-1][h],m=b[k][h-1],n=l<m?l<g?l:g:m<g?m:g,n==g?(g==d?a.push(0):(a.push(1),d=g),k--,h--):n==l?(a.push(3),k--,d=l):(a.push(2),h--,d=m));a.reverse();b=void 0;k=[];for(h=0;h<a.length;h++)switch(a[h]){case 0:b&&(k.push(b),b=void 0);e++;f++;break;case 1:b||(b=hd(e,0));b.U++;e++;b.O.push(c[f]);f++;break;case 2:b||(b=hd(e,0));
|
||||
b.U++;e++;break;case 3:b||(b=hd(e,0)),b.O.push(c[f]),f++}b&&k.push(b);return k}function jd(a,b){return a===b};var kd={};function H(a,b,c){if(a!==kd)throw new TypeError("Illegal constructor");a=document.createDocumentFragment();a.__proto__=H.prototype;a.ya="ShadyRoot";xc(b);xc(a);a.host=b;a.Fa=c&&c.mode;b.__shady=b.__shady||{};b.__shady.root=a;b.__shady.Va="closed"!==a.Fa?a:null;a.T=!1;a.o=[];a.l={};a.H=[];c=B.childNodes(b);for(var d=0,e=c.length;d<e;d++)A.removeChild.call(b,c[d]);return a}H.prototype=Object.create(DocumentFragment.prototype);function Ac(a){a.T||(a.T=!0,Kb(function(){return ld(a)}))}
|
||||
function ld(a){for(var b;a;){a.T&&(b=a);a:{var c=a;a=c.host.getRootNode();if(z(a))for(var d=c.host.childNodes,e=0;e<d.length;e++)if(c=d[e],"slot"==c.localName)break a;a=void 0}}b&&b._renderRoot()}
|
||||
H.prototype._renderRoot=function(){this.T=!1;Fc(this);for(var a=0,b;a<this.o.length;a++){b=this.o[a];var c=b.__shady.assignedNodes;b.__shady.assignedNodes=[];b.__shady.K=[];if(b.__shady.oa=c)for(var d=0;d<c.length;d++){var e=c[d];e.__shady.ga=e.__shady.assignedSlot;e.__shady.assignedSlot===b&&(e.__shady.assignedSlot=null)}}for(b=this.host.firstChild;b;b=b.nextSibling)md(this,b);for(a=0;a<this.o.length;a++){b=this.o[a];if(!b.__shady.assignedNodes.length)for(c=b.firstChild;c;c=c.nextSibling)md(this,
|
||||
c,b);c=b.parentNode;(c=c.__shady&&c.__shady.root)&&Gc(c)&&c._renderRoot();nd(this,b.__shady.K,b.__shady.assignedNodes);if(c=b.__shady.oa){for(d=0;d<c.length;d++)c[d].__shady.ga=null;b.__shady.oa=null;c.length>b.__shady.assignedNodes.length&&(b.__shady.ia=!0)}b.__shady.ia&&(b.__shady.ia=!1,od(this,b))}a=this.o;b=[];for(c=0;c<a.length;c++)d=a[c].parentNode,d.__shady&&d.__shady.root||!(0>b.indexOf(d))||b.push(d);for(a=0;a<b.length;a++){c=b[a];d=c===this?this.host:c;e=[];c=c.childNodes;for(var f=0;f<
|
||||
c.length;f++){var h=c[f];if("slot"==h.localName){h=h.__shady.K;for(var g=0;g<h.length;g++)e.push(h[g])}else e.push(h)}c=void 0;f=B.childNodes(d);h=id(e,e.length,f,f.length);for(var k=g=0;g<h.length&&(c=h[g]);g++){for(var l=0,m;l<c.O.length&&(m=c.O[l]);l++)B.parentNode(m)===d&&A.removeChild.call(d,m),f.splice(c.index+k,1);k-=c.U}for(k=0;k<h.length&&(c=h[k]);k++)for(g=f[c.index],l=c.index;l<c.index+c.U;l++)m=e[l],A.insertBefore.call(d,m,g),f.splice(l,0,m)}};
|
||||
function md(a,b,c){b.__shady=b.__shady||{};var d=b.__shady.ga;b.__shady.ga=null;c||(c=(a=a.l[b.slot||"__catchall"])&&a[0]);c?(c.__shady.assignedNodes.push(b),b.__shady.assignedSlot=c):b.__shady.assignedSlot=void 0;d!==b.__shady.assignedSlot&&b.__shady.assignedSlot&&(b.__shady.assignedSlot.__shady.ia=!0)}function nd(a,b,c){for(var d=0,e;d<c.length&&(e=c[d]);d++)if("slot"==e.localName){var f=e.__shady.assignedNodes;f&&f.length&&nd(a,b,f)}else b.push(c[d])}
|
||||
function od(a,b){A.dispatchEvent.call(b,new Event("slotchange"));b.__shady.assignedSlot&&od(a,b.__shady.assignedSlot)}function Fc(a){if(a.H.length){for(var b=a.H,c,d=0;d<b.length;d++){var e=b[d];e.__shady=e.__shady||{};xc(e);xc(e.parentNode);var f=Ic(e);a.l[f]?(c=c||{},c[f]=!0,a.l[f].push(e)):a.l[f]=[e];a.o.push(e)}if(c)for(var h in c)a.l[h]=Jc(a.l[h]);a.H=[]}}function Ic(a){var b=a.name||a.getAttribute("name")||"__catchall";return a.Aa=b}
|
||||
function Jc(a){return a.sort(function(a,c){a=pd(a);for(var b=pd(c),e=0;e<a.length;e++){c=a[e];var f=b[e];if(c!==f)return a=Array.from(c.parentNode.childNodes),a.indexOf(c)-a.indexOf(f)}})}function pd(a){var b=[];do b.unshift(a);while(a=a.parentNode);return b}function Gc(a){Fc(a);return!!a.o.length}H.prototype.addEventListener=function(a,b,c){"object"!==typeof c&&(c={capture:!!c});c.aa=this;this.host.addEventListener(a,b,c)};
|
||||
H.prototype.removeEventListener=function(a,b,c){"object"!==typeof c&&(c={capture:!!c});c.aa=this;this.host.removeEventListener(a,b,c)};H.prototype.getElementById=function(a){return Lc(this,function(b){return b.id==a},function(a){return!!a})[0]||null};var qd=H.prototype;F(qd,rc,!0);F(qd,tc,!0);function rd(a){var b=a.getRootNode();z(b)&&ld(b);return a.__shady&&a.__shady.assignedSlot||null}
|
||||
var sd={addEventListener:ad.bind(window),removeEventListener:cd.bind(window)},td={addEventListener:ad,removeEventListener:cd,appendChild:function(a){return yc(this,a)},insertBefore:function(a,b){return yc(this,a,b)},removeChild:function(a){return zc(this,a)},replaceChild:function(a,b){yc(this,a,b);zc(this,b);return a},cloneNode:function(a){if("template"==this.localName)var b=A.cloneNode.call(this,a);else if(b=A.cloneNode.call(this,!1),a){a=this.childNodes;for(var c=0,d;c<a.length;c++)d=a[c].cloneNode(!0),
|
||||
b.appendChild(d)}return b},getRootNode:function(){return Kc(this)},contains:function(a){return Hb(this,a)},get isConnected(){var a=this.ownerDocument;if(Gb&&A.contains.call(a,this)||a.documentElement&&A.contains.call(a.documentElement,this))return!0;for(a=this;a&&!(a instanceof Document);)a=a.parentNode||(a instanceof H?a.host:void 0);return!!(a&&a instanceof Document)},dispatchEvent:function(a){Lb();return A.dispatchEvent.call(this,a)}},ud={get assignedSlot(){return rd(this)}},vd={querySelector:function(a){return Lc(this,
|
||||
function(b){return yb.call(b,a)},function(a){return!!a})[0]||null},querySelectorAll:function(a){return Lc(this,function(b){return yb.call(b,a)})}},wd={assignedNodes:function(a){if("slot"===this.localName){var b=this.getRootNode();z(b)&&ld(b);return this.__shady?(a&&a.flatten?this.__shady.K:this.__shady.assignedNodes)||[]:[]}}},xd=Ab({setAttribute:function(a,b){Oc(this,a,b)},removeAttribute:function(a){A.removeAttribute.call(this,a);Hc(this,a)},attachShadow:function(a){if(!this)throw"Must provide a host.";
|
||||
if(!a)throw"Not enough arguments.";return new H(kd,this,a)},get slot(){return this.getAttribute("slot")},set slot(a){Oc(this,"slot",a)},get assignedSlot(){return rd(this)}},vd,wd);Object.defineProperties(xd,sc);var yd=Ab({importNode:function(a,b){return Pc(a,b)},getElementById:function(a){return Lc(this,function(b){return b.id==a},function(a){return!!a})[0]||null}},vd);Object.defineProperties(yd,{_activeElement:tc.activeElement});
|
||||
var zd=HTMLElement.prototype.blur,Ad=Ab({blur:function(){var a=this.__shady&&this.__shady.root;(a=a&&a.activeElement)?a.blur():zd.call(this)}});function I(a,b){for(var c=Object.getOwnPropertyNames(b),d=0;d<c.length;d++){var e=c[d],f=Object.getOwnPropertyDescriptor(b,e);f.value?a[e]=f.value:Object.defineProperty(a,e,f)}};if(y.sa){var ShadyDOM={inUse:y.sa,patch:function(a){return a},isShadyRoot:z,enqueue:Kb,flush:Lb,settings:y,filterMutations:Rb,observeChildren:Pb,unobserveChildren:Qb,nativeMethods:A,nativeTree:B};window.ShadyDOM=ShadyDOM;window.Event=ed;window.CustomEvent=fd;window.MouseEvent=gd;dd();var Bd=window.customElements&&window.customElements.nativeHTMLElement||HTMLElement;I(window.Node.prototype,td);I(window.Window.prototype,sd);I(window.Text.prototype,ud);I(window.DocumentFragment.prototype,vd);I(window.Element.prototype,
|
||||
xd);I(window.Document.prototype,yd);window.HTMLSlotElement&&I(window.HTMLSlotElement.prototype,wd);I(Bd.prototype,Ad);y.M&&(G(window.Node.prototype),G(window.Text.prototype),G(window.DocumentFragment.prototype),G(window.Element.prototype),G(Bd.prototype),G(window.Document.prototype),window.HTMLSlotElement&&G(window.HTMLSlotElement.prototype));window.ShadowRoot=H};var Cd=new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));function Dd(a){var b=Cd.has(a);a=/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(a);return!b&&a}function K(a){var b=a.isConnected;if(void 0!==b)return b;for(;a&&!(a.__CE_isImportDocument||a instanceof Document);)a=a.parentNode||(window.ShadowRoot&&a instanceof ShadowRoot?a.host:void 0);return!(!a||!(a.__CE_isImportDocument||a instanceof Document))}
|
||||
function Ed(a,b){for(;b&&b!==a&&!b.nextSibling;)b=b.parentNode;return b&&b!==a?b.nextSibling:null}
|
||||
function L(a,b,c){c=void 0===c?new Set:c;for(var d=a;d;){if(d.nodeType===Node.ELEMENT_NODE){var e=d;b(e);var f=e.localName;if("link"===f&&"import"===e.getAttribute("rel")){d=e.import;if(d instanceof Node&&!c.has(d))for(c.add(d),d=d.firstChild;d;d=d.nextSibling)L(d,b,c);d=Ed(a,e);continue}else if("template"===f){d=Ed(a,e);continue}if(e=e.__CE_shadowRoot)for(e=e.firstChild;e;e=e.nextSibling)L(e,b,c)}d=d.firstChild?d.firstChild:Ed(a,d)}}function M(a,b,c){a[b]=c};function Fd(){this.a=new Map;this.s=new Map;this.h=[];this.c=!1}function Gd(a,b,c){a.a.set(b,c);a.s.set(c.constructor,c)}function Hd(a,b){a.c=!0;a.h.push(b)}function Id(a,b){a.c&&L(b,function(b){return a.b(b)})}Fd.prototype.b=function(a){if(this.c&&!a.__CE_patched){a.__CE_patched=!0;for(var b=0;b<this.h.length;b++)this.h[b](a)}};function N(a,b){var c=[];L(b,function(a){return c.push(a)});for(b=0;b<c.length;b++){var d=c[b];1===d.__CE_state?a.connectedCallback(d):Jd(a,d)}}
|
||||
function O(a,b){var c=[];L(b,function(a){return c.push(a)});for(b=0;b<c.length;b++){var d=c[b];1===d.__CE_state&&a.disconnectedCallback(d)}}
|
||||
function P(a,b,c){c=void 0===c?{}:c;var d=c.$a||new Set,e=c.va||function(b){return Jd(a,b)},f=[];L(b,function(b){if("link"===b.localName&&"import"===b.getAttribute("rel")){var c=b.import;c instanceof Node&&(c.__CE_isImportDocument=!0,c.__CE_hasRegistry=!0);c&&"complete"===c.readyState?c.__CE_documentLoadHandled=!0:b.addEventListener("load",function(){var c=b.import;if(!c.__CE_documentLoadHandled){c.__CE_documentLoadHandled=!0;var f=new Set(d);f.delete(c);P(a,c,{$a:f,va:e})}})}else f.push(b)},d);if(a.c)for(b=
|
||||
0;b<f.length;b++)a.b(f[b]);for(b=0;b<f.length;b++)e(f[b])}
|
||||
function Jd(a,b){if(void 0===b.__CE_state){var c=b.ownerDocument;if(c.defaultView||c.__CE_isImportDocument&&c.__CE_hasRegistry)if(c=a.a.get(b.localName)){c.constructionStack.push(b);var d=c.constructor;try{try{if(new d!==b)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{c.constructionStack.pop()}}catch(h){throw b.__CE_state=2,h;}b.__CE_state=1;b.__CE_definition=c;if(c.attributeChangedCallback)for(c=c.observedAttributes,d=0;d<c.length;d++){var e=c[d],
|
||||
f=b.getAttribute(e);null!==f&&a.attributeChangedCallback(b,e,null,f,null)}K(b)&&a.connectedCallback(b)}}}Fd.prototype.connectedCallback=function(a){var b=a.__CE_definition;b.connectedCallback&&b.connectedCallback.call(a)};Fd.prototype.disconnectedCallback=function(a){var b=a.__CE_definition;b.disconnectedCallback&&b.disconnectedCallback.call(a)};
|
||||
Fd.prototype.attributeChangedCallback=function(a,b,c,d,e){var f=a.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(b)&&f.attributeChangedCallback.call(a,b,c,d,e)};function Kd(a){var b=document;this.j=a;this.a=b;this.C=void 0;P(this.j,this.a);"loading"===this.a.readyState&&(this.C=new MutationObserver(this.b.bind(this)),this.C.observe(this.a,{childList:!0,subtree:!0}))}Kd.prototype.disconnect=function(){this.C&&this.C.disconnect()};Kd.prototype.b=function(a){var b=this.a.readyState;"interactive"!==b&&"complete"!==b||this.disconnect();for(b=0;b<a.length;b++)for(var c=a[b].addedNodes,d=0;d<c.length;d++)P(this.j,c[d])};function Ld(){var a=this;this.b=this.a=void 0;this.c=new Promise(function(b){a.b=b;a.a&&b(a.a)})}Ld.prototype.resolve=function(a){if(this.a)throw Error("Already resolved.");this.a=a;this.b&&this.b(a)};function Q(a){this.da=!1;this.j=a;this.ha=new Map;this.ea=function(a){return a()};this.R=!1;this.fa=[];this.Da=new Kd(a)}
|
||||
Q.prototype.define=function(a,b){var c=this;if(!(b instanceof Function))throw new TypeError("Custom element constructors must be functions.");if(!Dd(a))throw new SyntaxError("The element name '"+a+"' is not valid.");if(this.j.a.get(a))throw Error("A custom element with name '"+a+"' has already been defined.");if(this.da)throw Error("A custom element is already being defined.");this.da=!0;try{var d=function(a){var b=e[a];if(void 0!==b&&!(b instanceof Function))throw Error("The '"+a+"' callback must be a function.");
|
||||
return b},e=b.prototype;if(!(e instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");var f=d("connectedCallback");var h=d("disconnectedCallback");var g=d("adoptedCallback");var k=d("attributeChangedCallback");var l=b.observedAttributes||[]}catch(m){return}finally{this.da=!1}b={localName:a,constructor:b,connectedCallback:f,disconnectedCallback:h,adoptedCallback:g,attributeChangedCallback:k,observedAttributes:l,constructionStack:[]};Gd(this.j,a,b);this.fa.push(b);
|
||||
this.R||(this.R=!0,this.ea(function(){return Md(c)}))};function Md(a){if(!1!==a.R){a.R=!1;for(var b=a.fa,c=[],d=new Map,e=0;e<b.length;e++)d.set(b[e].localName,[]);P(a.j,document,{va:function(b){if(void 0===b.__CE_state){var e=b.localName,f=d.get(e);f?f.push(b):a.j.a.get(e)&&c.push(b)}}});for(e=0;e<c.length;e++)Jd(a.j,c[e]);for(;0<b.length;){var f=b.shift();e=f.localName;f=d.get(f.localName);for(var h=0;h<f.length;h++)Jd(a.j,f[h]);(e=a.ha.get(e))&&e.resolve(void 0)}}}
|
||||
Q.prototype.get=function(a){if(a=this.j.a.get(a))return a.constructor};Q.prototype.a=function(a){if(!Dd(a))return Promise.reject(new SyntaxError("'"+a+"' is not a valid custom element name."));var b=this.ha.get(a);if(b)return b.c;b=new Ld;this.ha.set(a,b);this.j.a.get(a)&&!this.fa.some(function(b){return b.localName===a})&&b.resolve(void 0);return b.c};Q.prototype.b=function(a){this.Da.disconnect();var b=this.ea;this.ea=function(c){return a(function(){return b(c)})}};
|
||||
window.CustomElementRegistry=Q;Q.prototype.define=Q.prototype.define;Q.prototype.get=Q.prototype.get;Q.prototype.whenDefined=Q.prototype.a;Q.prototype.polyfillWrapFlushCallback=Q.prototype.b;var Nd=window.Document.prototype.createElement,Od=window.Document.prototype.createElementNS,Pd=window.Document.prototype.importNode,Qd=window.Document.prototype.prepend,Rd=window.Document.prototype.append,Sd=window.DocumentFragment.prototype.prepend,Td=window.DocumentFragment.prototype.append,Ud=window.Node.prototype.cloneNode,Vd=window.Node.prototype.appendChild,Wd=window.Node.prototype.insertBefore,Xd=window.Node.prototype.removeChild,Yd=window.Node.prototype.replaceChild,Zd=Object.getOwnPropertyDescriptor(window.Node.prototype,
|
||||
"textContent"),$d=window.Element.prototype.attachShadow,ae=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),be=window.Element.prototype.getAttribute,ce=window.Element.prototype.setAttribute,de=window.Element.prototype.removeAttribute,ee=window.Element.prototype.getAttributeNS,fe=window.Element.prototype.setAttributeNS,ge=window.Element.prototype.removeAttributeNS,he=window.Element.prototype.insertAdjacentElement,ie=window.Element.prototype.prepend,je=window.Element.prototype.append,
|
||||
ke=window.Element.prototype.before,le=window.Element.prototype.after,me=window.Element.prototype.replaceWith,ne=window.Element.prototype.remove,oe=window.HTMLElement,pe=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),qe=window.HTMLElement.prototype.insertAdjacentElement;var re=new function(){};function se(){var a=te;window.HTMLElement=function(){function b(){var b=this.constructor,d=a.s.get(b);if(!d)throw Error("The custom element being constructed was not registered with `customElements`.");var e=d.constructionStack;if(0===e.length)return e=Nd.call(document,d.localName),Object.setPrototypeOf(e,b.prototype),e.__CE_state=1,e.__CE_definition=d,a.b(e),e;d=e.length-1;var f=e[d];if(f===re)throw Error("The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.");
|
||||
e[d]=re;Object.setPrototypeOf(f,b.prototype);a.b(f);return f}b.prototype=oe.prototype;return b}()};function ue(a,b,c){function d(b){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var f=[],l=0;l<d.length;l++){var m=d[l];m instanceof Element&&K(m)&&f.push(m);if(m instanceof DocumentFragment)for(m=m.firstChild;m;m=m.nextSibling)e.push(m);else e.push(m)}b.apply(this,d);for(d=0;d<f.length;d++)O(a,f[d]);if(K(this))for(d=0;d<e.length;d++)f=e[d],f instanceof Element&&N(a,f)}}void 0!==c.X&&(b.prepend=d(c.X));void 0!==c.append&&(b.append=d(c.append))};function ve(){var a=te;M(Document.prototype,"createElement",function(b){if(this.__CE_hasRegistry){var c=a.a.get(b);if(c)return new c.constructor}b=Nd.call(this,b);a.b(b);return b});M(Document.prototype,"importNode",function(b,c){b=Pd.call(this,b,c);this.__CE_hasRegistry?P(a,b):Id(a,b);return b});M(Document.prototype,"createElementNS",function(b,c){if(this.__CE_hasRegistry&&(null===b||"http://www.w3.org/1999/xhtml"===b)){var d=a.a.get(c);if(d)return new d.constructor}b=Od.call(this,b,c);a.b(b);return b});
|
||||
ue(a,Document.prototype,{X:Qd,append:Rd})};function we(){var a=te;function b(b,d){Object.defineProperty(b,"textContent",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(b){if(this.nodeType===Node.TEXT_NODE)d.set.call(this,b);else{var c=void 0;if(this.firstChild){var e=this.childNodes,g=e.length;if(0<g&&K(this)){c=Array(g);for(var k=0;k<g;k++)c[k]=e[k]}}d.set.call(this,b);if(c)for(b=0;b<c.length;b++)O(a,c[b])}}})}M(Node.prototype,"insertBefore",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);
|
||||
b=Wd.call(this,b,d);if(K(this))for(d=0;d<c.length;d++)N(a,c[d]);return b}c=K(b);d=Wd.call(this,b,d);c&&O(a,b);K(this)&&N(a,b);return d});M(Node.prototype,"appendChild",function(b){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Vd.call(this,b);if(K(this))for(var e=0;e<c.length;e++)N(a,c[e]);return b}c=K(b);e=Vd.call(this,b);c&&O(a,b);K(this)&&N(a,b);return e});M(Node.prototype,"cloneNode",function(b){b=Ud.call(this,b);this.ownerDocument.__CE_hasRegistry?P(a,b):
|
||||
Id(a,b);return b});M(Node.prototype,"removeChild",function(b){var c=K(b),e=Xd.call(this,b);c&&O(a,b);return e});M(Node.prototype,"replaceChild",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Yd.call(this,b,d);if(K(this))for(O(a,d),d=0;d<c.length;d++)N(a,c[d]);return b}c=K(b);var f=Yd.call(this,b,d),h=K(this);h&&O(a,d);c&&O(a,b);h&&N(a,b);return f});Zd&&Zd.get?b(Node.prototype,Zd):Hd(a,function(a){b(a,{enumerable:!0,configurable:!0,get:function(){for(var a=
|
||||
[],b=0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join("")},set:function(a){for(;this.firstChild;)Xd.call(this,this.firstChild);Vd.call(this,document.createTextNode(a))}})})};function xe(a){var b=Element.prototype;function c(b){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var g=[],k=0;k<d.length;k++){var l=d[k];l instanceof Element&&K(l)&&g.push(l);if(l instanceof DocumentFragment)for(l=l.firstChild;l;l=l.nextSibling)e.push(l);else e.push(l)}b.apply(this,d);for(d=0;d<g.length;d++)O(a,g[d]);if(K(this))for(d=0;d<e.length;d++)g=e[d],g instanceof Element&&N(a,g)}}void 0!==ke&&(b.before=c(ke));void 0!==ke&&(b.after=c(le));void 0!==
|
||||
me&&M(b,"replaceWith",function(b){for(var c=[],d=0;d<arguments.length;++d)c[d-0]=arguments[d];d=[];for(var h=[],g=0;g<c.length;g++){var k=c[g];k instanceof Element&&K(k)&&h.push(k);if(k instanceof DocumentFragment)for(k=k.firstChild;k;k=k.nextSibling)d.push(k);else d.push(k)}g=K(this);me.apply(this,c);for(c=0;c<h.length;c++)O(a,h[c]);if(g)for(O(a,this),c=0;c<d.length;c++)h=d[c],h instanceof Element&&N(a,h)});void 0!==ne&&M(b,"remove",function(){var b=K(this);ne.call(this);b&&O(a,this)})};function ye(){var a=te;function b(b,c){Object.defineProperty(b,"innerHTML",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(b){var d=this,e=void 0;K(this)&&(e=[],L(this,function(a){a!==d&&e.push(a)}));c.set.call(this,b);if(e)for(var f=0;f<e.length;f++){var l=e[f];1===l.__CE_state&&a.disconnectedCallback(l)}this.ownerDocument.__CE_hasRegistry?P(a,this):Id(a,this);return b}})}function c(b,c){M(b,"insertAdjacentElement",function(b,d){var e=K(d);b=c.call(this,b,d);e&&O(a,d);K(b)&&N(a,d);
|
||||
return b})}$d&&M(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=$d.call(this,a)});ae&&ae.get?b(Element.prototype,ae):pe&&pe.get?b(HTMLElement.prototype,pe):Hd(a,function(a){b(a,{enumerable:!0,configurable:!0,get:function(){return Ud.call(this,!0).innerHTML},set:function(a){var b="template"===this.localName,c=b?this.content:this,d=Nd.call(document,this.localName);for(d.innerHTML=a;0<c.childNodes.length;)Xd.call(c,c.childNodes[0]);for(a=b?d.content:d;0<a.childNodes.length;)Vd.call(c,
|
||||
a.childNodes[0])}})});M(Element.prototype,"setAttribute",function(b,c){if(1!==this.__CE_state)return ce.call(this,b,c);var d=be.call(this,b);ce.call(this,b,c);c=be.call(this,b);a.attributeChangedCallback(this,b,d,c,null)});M(Element.prototype,"setAttributeNS",function(b,c,f){if(1!==this.__CE_state)return fe.call(this,b,c,f);var d=ee.call(this,b,c);fe.call(this,b,c,f);f=ee.call(this,b,c);a.attributeChangedCallback(this,c,d,f,b)});M(Element.prototype,"removeAttribute",function(b){if(1!==this.__CE_state)return de.call(this,
|
||||
b);var c=be.call(this,b);de.call(this,b);null!==c&&a.attributeChangedCallback(this,b,c,null,null)});M(Element.prototype,"removeAttributeNS",function(b,c){if(1!==this.__CE_state)return ge.call(this,b,c);var d=ee.call(this,b,c);ge.call(this,b,c);var e=ee.call(this,b,c);d!==e&&a.attributeChangedCallback(this,c,d,e,b)});qe?c(HTMLElement.prototype,qe):he?c(Element.prototype,he):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");ue(a,Element.prototype,{X:ie,append:je});xe(a)}
|
||||
;var ze=window.customElements;if(!ze||ze.forcePolyfill||"function"!=typeof ze.define||"function"!=typeof ze.get){var te=new Fd;se();ve();ue(te,DocumentFragment.prototype,{X:Sd,append:Td});we();ye();document.__CE_hasRegistry=!0;var customElements=new Q(te);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:customElements})};function Ae(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}
|
||||
function Be(a){a=a.replace(Ce,"").replace(De,"");var b=Ee,c=a,d=new Ae;d.start=0;d.end=c.length;for(var e=d,f=0,h=c.length;f<h;f++)if("{"===c[f]){e.rules||(e.rules=[]);var g=e,k=g.rules[g.rules.length-1]||null;e=new Ae;e.start=f+1;e.parent=g;e.previous=k;g.rules.push(e)}else"}"===c[f]&&(e.end=f+1,e=e.parent||d);return b(d,a)}
|
||||
function Ee(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=Fe(c),c=c.replace(Ge," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=He:c.match(Ie)&&(a.type=Je,a.keyframesName=a.selector.split(Ge).pop()):a.type=0===c.indexOf("--")?Ke:Le);if(c=a.rules)for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)Ee(f,
|
||||
b);return a}function Fe(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}
|
||||
function Me(a,b,c){c=void 0===c?"":c;var d="";if(a.cssText||a.rules){var e=a.rules,f;if(f=e)f=e[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var h=e.length,g;f<h&&(g=e[f]);f++)d=Me(g,b,d)}else b?b=a.cssText:(b=a.cssText,b=b.replace(Ne,"").replace(Oe,""),b=b.replace(Pe,"").replace(Qe,"")),(d=b.trim())&&(d=" "+d+"\n")}d&&(a.selector&&(c+=a.selector+" {\n"),c+=d,a.selector&&(c+="}\n\n"));return c}
|
||||
var Le=1,Je=7,He=4,Ke=1E3,Ce=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,De=/@import[^;]*;/gim,Ne=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,Oe=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,Pe=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,Qe=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,Ie=/^@[^\s]*keyframes/,Ge=/\s+/g;var R=!(window.ShadyDOM&&window.ShadyDOM.inUse),Re;function Se(a){Re=a&&a.shimcssproperties?!1:R||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?Re=window.ShadyCSS.nativeCss:window.ShadyCSS?(Se(window.ShadyCSS),window.ShadyCSS=void 0):Se(window.WebComponents&&window.WebComponents.flags);var S=Re;var Te=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,Ue=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,Ve=/(--[\w-]+)\s*([:,;)]|$)/gi,We=/(animation\s*:)|(animation-name\s*:)/,Xe=/@media\s(.*)/,Ye=/\{[^}]*\}/g;var Ze=new Set;function $e(a,b){if(!a)return"";"string"===typeof a&&(a=Be(a));b&&af(a,b);return Me(a,S)}function bf(a){!a.__cssRules&&a.textContent&&(a.__cssRules=Be(a.textContent));return a.__cssRules||null}function cf(a){return!!a.parent&&a.parent.type===Je}function af(a,b,c,d){if(a){var e=!1,f=a.type;if(d&&f===He){var h=a.selector.match(Xe);h&&(window.matchMedia(h[1]).matches||(e=!0))}f===Le?b(a):c&&f===Je?c(a):f===Ke&&(e=!0);if((a=a.rules)&&!e){e=0;f=a.length;for(var g;e<f&&(g=a[e]);e++)af(g,b,c,d)}}}
|
||||
function df(a,b,c,d){var e=document.createElement("style");b&&e.setAttribute("scope",b);e.textContent=a;ef(e,c,d);return e}var T=null;function ef(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);T?a.compareDocumentPosition(T)===Node.DOCUMENT_POSITION_PRECEDING&&(T=a):T=a}
|
||||
function ff(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");a:{var d=0;var e=c+3;for(var f=a.length;e<f;e++)if("("===a[e])d++;else if(")"===a[e]&&0===--d)break a;e=-1}d=a.substring(c+4,e);c=a.substring(0,c);a=ff(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function gf(a,b){R?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)}
|
||||
function V(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")||""):(b=a.is,c=a.extends);return{is:b,P:c}};function hf(){}function jf(a,b,c){var d=W;a.__styleScoped?a.__styleScoped=null:kf(d,a,b||"",c)}function kf(a,b,c,d){b.nodeType===Node.ELEMENT_NODE&&lf(b,c,d);if(b="template"===b.localName?(b.content||b.gb).childNodes:b.children||b.childNodes)for(var e=0;e<b.length;e++)kf(a,b[e],c,d)}
|
||||
function lf(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"),a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var d=a.getAttribute(mf);c?d&&(b=d.replace("style-scope","").replace(b,""),gf(a,b)):gf(a,(d?d+" ":"")+"style-scope "+b)}}function nf(a,b,c){var d=W,e=a.__cssBuild;R||"shady"===e?b=$e(b,c):(a=V(a),b=of(d,b,a.is,a.P,c)+"\n\n");return b.trim()}
|
||||
function of(a,b,c,d,e){var f=pf(c,d);c=c?qf+c:"";return $e(b,function(b){b.c||(b.selector=b.m=rf(a,b,a.b,c,f),b.c=!0);e&&e(b,c,f)})}function pf(a,b){return b?"[is="+a+"]":a}function rf(a,b,c,d,e){var f=b.selector.split(sf);if(!cf(b)){b=0;for(var h=f.length,g;b<h&&(g=f[b]);b++)f[b]=c.call(a,g,d,e)}return f.join(sf)}function tf(a){return a.replace(uf,function(a,c,d){-1<d.indexOf("+")?d=d.replace(/\+/g,"___"):-1<d.indexOf("___")&&(d=d.replace(/___/g,"+"));return":"+c+"("+d+")"})}
|
||||
hf.prototype.b=function(a,b,c){var d=!1;a=a.trim();var e=uf.test(a);e&&(a=a.replace(uf,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=tf(a));a=a.replace(vf,wf+" $1");a=a.replace(xf,function(a,e,g){d||(a=yf(g,e,b,c),d=d||a.stop,e=a.Ka,g=a.value);return e+g});e&&(a=tf(a));return a};
|
||||
function yf(a,b,c,d){var e=a.indexOf(zf);0<=a.indexOf(wf)?a=Af(a,d):0!==e&&(a=c?Bf(a,c):a);c=!1;0<=e&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(Cf,function(a,b){return" > "+b}))}a=a.replace(Df,function(a,b,c){return'[dir="'+c+'"] '+b+", "+b+'[dir="'+c+'"]'});return{value:a,Ka:b,stop:f}}function Bf(a,b){a=a.split(Ef);a[0]+=b;return a.join(Ef)}
|
||||
function Af(a,b){var c=a.match(Ff);return(c=c&&c[2].trim()||"")?c[0].match(Gf)?a.replace(Ff,function(a,c,f){return b+f}):c.split(Gf)[0]===b?c:Hf:a.replace(wf,b)}function If(a){a.selector===Jf&&(a.selector="html")}hf.prototype.c=function(a){return a.match(zf)?this.b(a,Kf):Bf(a.trim(),Kf)};q.Object.defineProperties(hf.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}});
|
||||
var uf=/:(nth[-\w]+)\(([^)]+)\)/,Kf=":not(.style-scope)",sf=",",xf=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,Gf=/[[.:#*]/,wf=":host",Jf=":root",zf="::slotted",vf=new RegExp("^("+zf+")"),Ff=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Cf=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Df=/(.*):dir\((?:(ltr|rtl))\)/,qf=".",Ef=":",mf="class",Hf="should_not_match",W=new hf;function Lf(a,b,c,d){this.w=a||null;this.b=b||null;this.ja=c||[];this.G=null;this.P=d||"";this.a=this.u=this.B=null}function X(a){return a?a.__styleInfo:null}function Mf(a,b){return a.__styleInfo=b}Lf.prototype.c=function(){return this.w};Lf.prototype._getStyleRules=Lf.prototype.c;var Nf,Of=window.Element.prototype;Nf=Of.matches||Of.matchesSelector||Of.mozMatchesSelector||Of.msMatchesSelector||Of.oMatchesSelector||Of.webkitMatchesSelector;var Pf=navigator.userAgent.match("Trident");function Qf(){}function Rf(a){var b={},c=[],d=0;af(a,function(a){Sf(a);a.index=d++;a=a.i.cssText;for(var c;c=Ve.exec(a);){var e=c[1];":"!==c[2]&&(b[e]=!0)}},function(a){c.push(a)});a.b=c;a=[];for(var e in b)a.push(e);return a}
|
||||
function Sf(a){if(!a.i){var b={},c={};Tf(a,c)&&(b.v=c,a.rules=null);b.cssText=a.parsedCssText.replace(Ye,"").replace(Te,"");a.i=b}}function Tf(a,b){var c=a.i;if(c){if(c.v)return Object.assign(b,c.v),!0}else{c=a.parsedCssText;for(var d;a=Te.exec(c);){d=(a[2]||a[3]).trim();if("inherit"!==d||"unset"!==d)b[a[1].trim()]=d;d=!0}return d}}
|
||||
function Uf(a,b,c){b&&(b=0<=b.indexOf(";")?Vf(a,b,c):ff(b,function(b,e,f,h){if(!e)return b+h;(e=Uf(a,c[e],c))&&"initial"!==e?"apply-shim-inherit"===e&&(e="inherit"):e=Uf(a,c[f]||f,c)||f;return b+(e||"")+h}));return b&&b.trim()||""}
|
||||
function Vf(a,b,c){b=b.split(";");for(var d=0,e,f;d<b.length;d++)if(e=b[d]){Ue.lastIndex=0;if(f=Ue.exec(e))e=Uf(a,c[f[1]],c);else if(f=e.indexOf(":"),-1!==f){var h=e.substring(f);h=h.trim();h=Uf(a,h,c)||h;e=e.substring(0,f)+h}b[d]=e&&e.lastIndexOf(";")===e.length-1?e.slice(0,-1):e||""}return b.join(";")}
|
||||
function Wf(a,b){var c={},d=[];af(a,function(a){a.i||Sf(a);var e=a.m||a.parsedSelector;b&&a.i.v&&e&&Nf.call(b,e)&&(Tf(a,c),a=a.index,e=parseInt(a/32,10),d[e]=(d[e]||0)|1<<a%32)},null,!0);return{v:c,key:d}}
|
||||
function Xf(a,b,c,d){b.i||Sf(b);if(b.i.v){var e=V(a);a=e.is;e=e.P;e=a?pf(a,e):"html";var f=b.parsedSelector,h=":host > *"===f||"html"===f,g=0===f.indexOf(":host")&&!h;"shady"===c&&(h=f===e+" > *."+e||-1!==f.indexOf("html"),g=!h&&0===f.indexOf(e));"shadow"===c&&(h=":host > *"===f||"html"===f,g=g&&!h);if(h||g)c=e,g&&(R&&!b.m&&(b.m=rf(W,b,W.b,a?qf+a:"",e)),c=b.m||e),d({Xa:c,Qa:g,hb:h})}}
|
||||
function Yf(a,b){var c={},d={},e=b&&b.__cssBuild;af(b,function(b){Xf(a,b,e,function(e){Nf.call(a.b||a,e.Xa)&&(e.Qa?Tf(b,c):Tf(b,d))})},null,!0);return{Wa:d,Oa:c}}
|
||||
function Zf(a,b,c,d){var e=V(b),f=pf(e.is,e.P),h=new RegExp("(?:^|[^.#[:])"+(b.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])");e=X(b).w;var g=$f(e,d);return nf(b,e,function(b){var e="";b.i||Sf(b);b.i.cssText&&(e=Vf(a,b.i.cssText,c));b.cssText=e;if(!R&&!cf(b)&&b.cssText){var k=e=b.cssText;null==b.ra&&(b.ra=We.test(e));if(b.ra)if(null==b.W){b.W=[];for(var n in g)k=g[n],k=k(e),e!==k&&(e=k,b.W.push(n))}else{for(n=0;n<b.W.length;++n)k=g[b.W[n]],e=k(e);k=e}b.cssText=k;b.m=b.m||b.selector;e="."+d;
|
||||
n=b.m.split(",");k=0;for(var t=n.length,C;k<t&&(C=n[k]);k++)n[k]=C.match(h)?C.replace(f,e):e+" "+C;b.selector=n.join(",")}})}function $f(a,b){a=a.b;var c={};if(!R&&a)for(var d=0,e=a[d];d<a.length;e=a[++d]){var f=e,h=b;f.h=new RegExp("\\b"+f.keyframesName+"(?!\\B|-)","g");f.a=f.keyframesName+"-"+h;f.m=f.m||f.selector;f.selector=f.m.replace(f.keyframesName,f.a);c[e.keyframesName]=ag(e)}return c}function ag(a){return function(b){return b.replace(a.h,a.a)}}
|
||||
function bg(a,b){var c=cg,d=bf(a);a.textContent=$e(d,function(a){var d=a.cssText=a.parsedCssText;a.i&&a.i.cssText&&(d=d.replace(Ne,"").replace(Oe,""),a.cssText=Vf(c,d,b))})}q.Object.defineProperties(Qf.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});var cg=new Qf;var dg={},eg=window.customElements;if(eg&&!R){var fg=eg.define;eg.define=function(a,b,c){var d=document.createComment(" Shady DOM styles for "+a+" "),e=document.head;e.insertBefore(d,(T?T.nextSibling:null)||e.firstChild);T=d;dg[a]=d;return fg.call(eg,a,b,c)}};function gg(){this.cache={}}gg.prototype.store=function(a,b,c,d){var e=this.cache[a]||[];e.push({v:b,styleElement:c,u:d});100<e.length&&e.shift();this.cache[a]=e};gg.prototype.fetch=function(a,b,c){if(a=this.cache[a])for(var d=a.length-1;0<=d;d--){var e=a[d],f;a:{for(f=0;f<c.length;f++){var h=c[f];if(e.v[h]!==b[h]){f=!1;break a}}f=!0}if(f)return e}};function hg(){}
|
||||
function ig(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var d=0;d<c.addedNodes.length;d++){var e=c.addedNodes[d];if(e.nodeType===Node.ELEMENT_NODE){var f=e.getRootNode();var h=e;var g=[];h.classList?g=Array.from(h.classList):h instanceof window.SVGElement&&h.hasAttribute("class")&&(g=h.getAttribute("class").split(/\s+/));h=g;g=h.indexOf(W.a);if((h=-1<g?h[g+1]:"")&&f===e.ownerDocument)jf(e,h,!0);else if(f.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&
|
||||
(f=f.host))if(f=V(f).is,h===f)for(e=window.ShadyDOM.nativeMethods.querySelectorAll.call(e,":not(."+W.a+")"),f=0;f<e.length;f++)lf(e[f],h);else h&&jf(e,h,!0),jf(e,f)}}}}
|
||||
if(!R){var jg=new MutationObserver(ig),kg=function(a){jg.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)kg(document);else{var lg=function(){kg(document.body)};window.HTMLImports?window.HTMLImports.whenReady(lg):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){lg();document.removeEventListener("readystatechange",a)};document.addEventListener("readystatechange",a)}else lg()})}hg=function(){ig(jg.takeRecords())}}
|
||||
var mg=hg;var ng={};var og=Promise.resolve();function pg(a){if(a=ng[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function qg(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function rg(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a.qa||(a.qa=!0,og.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a.qa=!1}))};var sg=null,tg=window.HTMLImports&&window.HTMLImports.whenReady||null,ug;function vg(a){requestAnimationFrame(function(){tg?tg(a):(sg||(sg=new Promise(function(a){ug=a}),"complete"===document.readyState?ug():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&ug()})),sg.then(function(){a&&a()}))})};var wg=new gg;function Y(){var a=this;this.L={};this.c=document.documentElement;var b=new Ae;b.rules=[];this.h=Mf(this.c,new Lf(b));this.s=!1;this.b=this.a=null;vg(function(){xg(a)})}p=Y.prototype;p.wa=function(){mg()};p.Ma=function(a){return bf(a)};p.Za=function(a){return $e(a)};
|
||||
p.prepareTemplate=function(a,b,c){if(!a.Ia){a.Ia=!0;a.name=b;a.extends=c;ng[b]=a;var d=(d=a.content.querySelector("style"))?d.getAttribute("css-build")||"":"";var e=[];for(var f=a.content.querySelectorAll("style"),h=0;h<f.length;h++){var g=f[h];if(g.hasAttribute("shady-unscoped")){if(!R){var k=g.textContent;Ze.has(k)||(Ze.add(k),k=g.cloneNode(!0),document.head.appendChild(k));g.parentNode.removeChild(g)}}else e.push(g.textContent),g.parentNode.removeChild(g)}e=e.join("").trim();c={is:b,extends:c,
|
||||
eb:d};R||jf(a.content,b);xg(this);f=Ue.test(e)||Te.test(e);Ue.lastIndex=0;Te.lastIndex=0;e=Be(e);f&&S&&this.a&&this.a.transformRules(e,b);a._styleAst=e;a.a=d;d=[];S||(d=Rf(a._styleAst));if(!d.length||S)e=R?a.content:null,b=dg[b],f=nf(c,a._styleAst),b=f.length?df(f,c.is,e,b):void 0,a.pa=b;a.Ha=d}};
|
||||
function yg(a){!a.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(a.b=window.ShadyCSS.CustomStyleInterface,a.b.transformCallback=function(b){a.ua(b)},a.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.s)&&a.F()})})}function xg(a){!a.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(a.a=window.ShadyCSS.ApplyShim,a.a.invalidCallback=pg);yg(a)}
|
||||
p.F=function(){xg(this);if(this.b){var a=this.b.processStyles();if(this.b.enqueued){if(S)for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);if(c&&S&&this.a){var d=bf(c);xg(this);this.a.transformRules(d);c.textContent=$e(d)}}else for(zg(this,this.c,this.h),b=0;b<a.length;b++)(c=this.b.getStyleForCustomStyle(a[b]))&&bg(c,this.h.B);this.b.enqueued=!1;this.s&&!S&&this.styleDocument()}}};
|
||||
p.styleElement=function(a,b){var c=V(a).is,d=X(a);if(!d){var e=V(a);d=e.is;e=e.P;var f=dg[d];d=ng[d];if(d){var h=d._styleAst;var g=d.Ha}d=Mf(a,new Lf(h,f,g,e))}a!==this.c&&(this.s=!0);b&&(d.G=d.G||{},Object.assign(d.G,b));if(S){if(d.G){b=d.G;for(var k in b)null===k?a.style.removeProperty(k):a.style.setProperty(k,b[k])}if(((k=ng[c])||a===this.c)&&k&&k.pa&&!qg(k)){if(qg(k)||k._applyShimValidatingVersion!==k._applyShimNextVersion)xg(this),this.a&&this.a.transformRules(k._styleAst,c),k.pa.textContent=
|
||||
nf(a,d.w),rg(k);R&&(c=a.shadowRoot)&&(c.querySelector("style").textContent=nf(a,d.w));d.w=k._styleAst}}else if(zg(this,a,d),d.ja&&d.ja.length){c=d;k=V(a).is;d=(b=wg.fetch(k,c.B,c.ja))?b.styleElement:null;h=c.u;(g=b&&b.u)||(g=this.L[k]=(this.L[k]||0)+1,g=k+"-"+g);c.u=g;g=c.u;e=cg;e=d?d.textContent||"":Zf(e,a,c.B,g);f=X(a);var l=f.a;l&&!R&&l!==d&&(l._useCount--,0>=l._useCount&&l.parentNode&&l.parentNode.removeChild(l));R?f.a?(f.a.textContent=e,d=f.a):e&&(d=df(e,g,a.shadowRoot,f.b)):d?d.parentNode||
|
||||
(Pf&&-1<e.indexOf("@media")&&(d.textContent=e),ef(d,null,f.b)):e&&(d=df(e,g,null,f.b));d&&(d._useCount=d._useCount||0,f.a!=d&&d._useCount++,f.a=d);g=d;R||(d=c.u,f=e=a.getAttribute("class")||"",h&&(f=e.replace(new RegExp("\\s*x-scope\\s*"+h+"\\s*","g")," ")),f+=(f?" ":"")+"x-scope "+d,e!==f&&gf(a,f));b||wg.store(k,c.B,g,c.u)}};function Ag(a,b){return(b=b.getRootNode().host)?X(b)?b:Ag(a,b):a.c}
|
||||
function zg(a,b,c){a=Ag(a,b);var d=X(a);a=Object.create(d.B||null);var e=Yf(b,c.w);b=Wf(d.w,b).v;Object.assign(a,e.Oa,b,e.Wa);b=c.G;for(var f in b)if((e=b[f])||0===e)a[f]=e;f=cg;b=Object.getOwnPropertyNames(a);for(e=0;e<b.length;e++)d=b[e],a[d]=Uf(f,a[d],a);c.B=a}p.styleDocument=function(a){this.styleSubtree(this.c,a)};
|
||||
p.styleSubtree=function(a,b){var c=a.shadowRoot;(c||a===this.c)&&this.styleElement(a,b);if(b=c&&(c.children||c.childNodes))for(a=0;a<b.length;a++)this.styleSubtree(b[a]);else if(a=a.children||a.childNodes)for(b=0;b<a.length;b++)this.styleSubtree(a[b])};p.ua=function(a){var b=this,c=bf(a);af(c,function(a){if(R)If(a);else{var c=W;a.selector=a.parsedSelector;If(a);a.selector=a.m=rf(c,a,c.c,void 0,void 0)}S&&(xg(b),b.a&&b.a.transformRule(a))});S?a.textContent=$e(c):this.h.w.rules.push(c)};
|
||||
p.getComputedStyleValue=function(a,b){var c;S||(c=(X(a)||X(Ag(this,a))).B[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():""};p.Ya=function(a,b){var c=a.getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var d=a.getAttribute("class");if(d){d=d.split(/\s/);for(var e=0;e<d.length;e++)if(d[e]===W.a){c=d[e+1];break}}}c&&b.push(W.a,c);S||(c=X(a))&&c.u&&b.push(cg.a,c.u);gf(a,b.join(" "))};p.Ja=function(a){return X(a)};Y.prototype.flush=Y.prototype.wa;
|
||||
Y.prototype.prepareTemplate=Y.prototype.prepareTemplate;Y.prototype.styleElement=Y.prototype.styleElement;Y.prototype.styleDocument=Y.prototype.styleDocument;Y.prototype.styleSubtree=Y.prototype.styleSubtree;Y.prototype.getComputedStyleValue=Y.prototype.getComputedStyleValue;Y.prototype.setElementClass=Y.prototype.Ya;Y.prototype._styleInfoForNode=Y.prototype.Ja;Y.prototype.transformCustomStyleForDocument=Y.prototype.ua;Y.prototype.getStyleAst=Y.prototype.Ma;Y.prototype.styleAstToString=Y.prototype.Za;
|
||||
Y.prototype.flushCustomStyles=Y.prototype.F;Object.defineProperties(Y.prototype,{nativeShadow:{get:function(){return R}},nativeCss:{get:function(){return S}}});var Z=new Y,Bg,Cg;window.ShadyCSS&&(Bg=window.ShadyCSS.ApplyShim,Cg=window.ShadyCSS.CustomStyleInterface);window.ShadyCSS={ScopingShim:Z,prepareTemplate:function(a,b,c){Z.F();Z.prepareTemplate(a,b,c)},styleSubtree:function(a,b){Z.F();Z.styleSubtree(a,b)},styleElement:function(a){Z.F();Z.styleElement(a)},styleDocument:function(a){Z.F();Z.styleDocument(a)},getComputedStyleValue:function(a,b){return Z.getComputedStyleValue(a,b)},nativeCss:S,nativeShadow:R};Bg&&(window.ShadyCSS.ApplyShim=Bg);
|
||||
Cg&&(window.ShadyCSS.CustomStyleInterface=Cg);var Dg=window.customElements,Eg=window.HTMLImports,Fg=window.HTMLTemplateElement;window.WebComponents=window.WebComponents||{};if(Dg&&Dg.polyfillWrapFlushCallback){var Gg,Hg=function(){if(Gg){Fg.J&&Fg.J(window.document);var a=Gg;Gg=null;a();return!0}},Ig=Eg.whenReady;Dg.polyfillWrapFlushCallback(function(a){Gg=a;Ig(Hg)});Eg.whenReady=function(a){Ig(function(){Hg()?Eg.whenReady(a):a()})}}
|
||||
Eg.whenReady(function(){requestAnimationFrame(function(){window.WebComponents.ready=!0;document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})});var Jg=document.createElement("style");Jg.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var Kg=document.querySelector("head");Kg.insertBefore(Jg,Kg.firstChild);}).call(this);
|
||||
|
||||
//# sourceMappingURL=webcomponents-lite.js.map
|
1
web/webcomponents-lite.js.map
Normal file
1
web/webcomponents-lite.js.map
Normal file
File diff suppressed because one or more lines are too long
371
websocket_server.py
Executable file
371
websocket_server.py
Executable file
@ -0,0 +1,371 @@
|
||||
# Author: Johan Hanssen Seferidis
|
||||
# License: MIT
|
||||
|
||||
import sys
|
||||
import struct
|
||||
from base64 import b64encode
|
||||
from hashlib import sha1
|
||||
import logging
|
||||
from socket import error as SocketError
|
||||
import errno
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
from SocketServer import ThreadingMixIn, TCPServer, StreamRequestHandler
|
||||
else:
|
||||
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig()
|
||||
|
||||
'''
|
||||
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
|I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
|N|V|V|V| |S| | (if payload len==126/127) |
|
||||
| |1|2|3| |K| | |
|
||||
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
| Extended payload length continued, if payload len == 127 |
|
||||
+ - - - - - - - - - - - - - - - +-------------------------------+
|
||||
| Payload Data continued ... |
|
||||
+---------------------------------------------------------------+
|
||||
'''
|
||||
|
||||
FIN = 0x80
|
||||
OPCODE = 0x0f
|
||||
MASKED = 0x80
|
||||
PAYLOAD_LEN = 0x7f
|
||||
PAYLOAD_LEN_EXT16 = 0x7e
|
||||
PAYLOAD_LEN_EXT64 = 0x7f
|
||||
|
||||
OPCODE_CONTINUATION = 0x0
|
||||
OPCODE_TEXT = 0x1
|
||||
OPCODE_BINARY = 0x2
|
||||
OPCODE_CLOSE_CONN = 0x8
|
||||
OPCODE_PING = 0x9
|
||||
OPCODE_PONG = 0xA
|
||||
|
||||
|
||||
# -------------------------------- API ---------------------------------
|
||||
|
||||
class API():
|
||||
|
||||
def run_forever(self):
|
||||
try:
|
||||
logger.info("Listening on port %d for clients.." % self.port)
|
||||
self.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
self.server_close()
|
||||
logger.info("Server terminated.")
|
||||
except Exception as e:
|
||||
logger.error(str(e), exc_info=True)
|
||||
exit(1)
|
||||
|
||||
def new_client(self, client, server):
|
||||
pass
|
||||
|
||||
def client_left(self, client, server):
|
||||
pass
|
||||
|
||||
def message_received(self, client, server, message):
|
||||
pass
|
||||
|
||||
def set_fn_new_client(self, fn):
|
||||
self.new_client = fn
|
||||
|
||||
def set_fn_client_left(self, fn):
|
||||
self.client_left = fn
|
||||
|
||||
def set_fn_message_received(self, fn):
|
||||
self.message_received = fn
|
||||
|
||||
def send_message(self, client, msg):
|
||||
self._unicast_(client, msg)
|
||||
|
||||
def send_message_to_all(self, msg):
|
||||
self._multicast_(msg)
|
||||
|
||||
|
||||
# ------------------------- Implementation -----------------------------
|
||||
|
||||
class WebsocketServer(ThreadingMixIn, TCPServer, API):
|
||||
"""
|
||||
A websocket server waiting for clients to connect.
|
||||
|
||||
Args:
|
||||
port(int): Port to bind to
|
||||
host(str): Hostname or IP to listen for connections. By default 127.0.0.1
|
||||
is being used. To accept connections from any client, you should use
|
||||
0.0.0.0.
|
||||
loglevel: Logging level from logging module to use for logging. By default
|
||||
warnings and errors are being logged.
|
||||
|
||||
Properties:
|
||||
clients(list): A list of connected clients. A client is a dictionary
|
||||
like below.
|
||||
{
|
||||
'id' : id,
|
||||
'handler' : handler,
|
||||
'address' : (addr, port)
|
||||
}
|
||||
"""
|
||||
|
||||
allow_reuse_address = True
|
||||
daemon_threads = True # comment to keep threads alive until finished
|
||||
|
||||
clients = []
|
||||
id_counter = 0
|
||||
|
||||
def __init__(self, port, host='127.0.0.1', loglevel=logging.WARNING):
|
||||
logger.setLevel(loglevel)
|
||||
TCPServer.__init__(self, (host, port), WebSocketHandler)
|
||||
self.port = self.socket.getsockname()[1]
|
||||
|
||||
def _message_received_(self, handler, msg):
|
||||
self.message_received(self.handler_to_client(handler), self, msg)
|
||||
|
||||
def _ping_received_(self, handler, msg):
|
||||
handler.send_pong(msg)
|
||||
|
||||
def _pong_received_(self, handler, msg):
|
||||
pass
|
||||
|
||||
def _new_client_(self, handler):
|
||||
self.id_counter += 1
|
||||
client = {
|
||||
'id': self.id_counter,
|
||||
'handler': handler,
|
||||
'address': handler.client_address
|
||||
}
|
||||
self.clients.append(client)
|
||||
self.new_client(client, self)
|
||||
|
||||
def _client_left_(self, handler):
|
||||
client = self.handler_to_client(handler)
|
||||
self.client_left(client, self)
|
||||
if client in self.clients:
|
||||
self.clients.remove(client)
|
||||
|
||||
def _unicast_(self, to_client, msg):
|
||||
to_client['handler'].send_message(msg)
|
||||
|
||||
def _multicast_(self, msg):
|
||||
for client in self.clients:
|
||||
self._unicast_(client, msg)
|
||||
|
||||
def handler_to_client(self, handler):
|
||||
for client in self.clients:
|
||||
if client['handler'] == handler:
|
||||
return client
|
||||
|
||||
|
||||
class WebSocketHandler(StreamRequestHandler):
|
||||
|
||||
def __init__(self, socket, addr, server):
|
||||
self.server = server
|
||||
StreamRequestHandler.__init__(self, socket, addr, server)
|
||||
|
||||
def setup(self):
|
||||
StreamRequestHandler.setup(self)
|
||||
self.keep_alive = True
|
||||
self.handshake_done = False
|
||||
self.valid_client = False
|
||||
|
||||
def handle(self):
|
||||
while self.keep_alive:
|
||||
if not self.handshake_done:
|
||||
self.handshake()
|
||||
elif self.valid_client:
|
||||
self.read_next_message()
|
||||
|
||||
def read_bytes(self, num):
|
||||
# python3 gives ordinal of byte directly
|
||||
bytes = self.rfile.read(num)
|
||||
if sys.version_info[0] < 3:
|
||||
return map(ord, bytes)
|
||||
else:
|
||||
return bytes
|
||||
|
||||
def read_next_message(self):
|
||||
try:
|
||||
b1, b2 = self.read_bytes(2)
|
||||
except SocketError as e: # to be replaced with ConnectionResetError for py3
|
||||
if e.errno == errno.ECONNRESET:
|
||||
logger.info("Client closed connection.")
|
||||
print("Error: {}".format(e))
|
||||
self.keep_alive = 0
|
||||
return
|
||||
b1, b2 = 0, 0
|
||||
except ValueError as e:
|
||||
b1, b2 = 0, 0
|
||||
|
||||
fin = b1 & FIN
|
||||
opcode = b1 & OPCODE
|
||||
masked = b2 & MASKED
|
||||
payload_length = b2 & PAYLOAD_LEN
|
||||
|
||||
if opcode == OPCODE_CLOSE_CONN:
|
||||
logger.info("Client asked to close connection.")
|
||||
self.keep_alive = 0
|
||||
return
|
||||
if not masked:
|
||||
logger.warn("Client must always be masked.")
|
||||
self.keep_alive = 0
|
||||
return
|
||||
if opcode == OPCODE_CONTINUATION:
|
||||
logger.warn("Continuation frames are not supported.")
|
||||
return
|
||||
elif opcode == OPCODE_BINARY:
|
||||
logger.warn("Binary frames are not supported.")
|
||||
return
|
||||
elif opcode == OPCODE_TEXT:
|
||||
opcode_handler = self.server._message_received_
|
||||
elif opcode == OPCODE_PING:
|
||||
opcode_handler = self.server._ping_received_
|
||||
elif opcode == OPCODE_PONG:
|
||||
opcode_handler = self.server._pong_received_
|
||||
else:
|
||||
logger.warn("Unknown opcode %#x." % opcode)
|
||||
self.keep_alive = 0
|
||||
return
|
||||
|
||||
if payload_length == 126:
|
||||
payload_length = struct.unpack(">H", self.rfile.read(2))[0]
|
||||
elif payload_length == 127:
|
||||
payload_length = struct.unpack(">Q", self.rfile.read(8))[0]
|
||||
|
||||
masks = self.read_bytes(4)
|
||||
message_bytes = bytearray()
|
||||
for message_byte in self.read_bytes(payload_length):
|
||||
message_byte ^= masks[len(message_bytes) % 4]
|
||||
message_bytes.append(message_byte)
|
||||
opcode_handler(self, message_bytes.decode('utf8'))
|
||||
|
||||
def send_message(self, message):
|
||||
self.send_text(message)
|
||||
|
||||
def send_pong(self, message):
|
||||
self.send_text(message, OPCODE_PONG)
|
||||
|
||||
def send_text(self, message, opcode=OPCODE_TEXT):
|
||||
"""
|
||||
Important: Fragmented(=continuation) messages are not supported since
|
||||
their usage cases are limited - when we don't know the payload length.
|
||||
"""
|
||||
|
||||
# Validate message
|
||||
if isinstance(message, bytes):
|
||||
message = try_decode_UTF8(message) # this is slower but ensures we have UTF-8
|
||||
if not message:
|
||||
logger.warning("Can\'t send message, message is not valid UTF-8")
|
||||
return False
|
||||
elif sys.version_info < (3,0) and (isinstance(message, str) or isinstance(message, unicode)):
|
||||
pass
|
||||
elif isinstance(message, str):
|
||||
pass
|
||||
else:
|
||||
logger.warning('Can\'t send message, message has to be a string or bytes. Given type is %s' % type(message))
|
||||
return False
|
||||
|
||||
header = bytearray()
|
||||
payload = encode_to_UTF8(message)
|
||||
payload_length = len(payload)
|
||||
|
||||
# Normal payload
|
||||
if payload_length <= 125:
|
||||
header.append(FIN | opcode)
|
||||
header.append(payload_length)
|
||||
|
||||
# Extended payload
|
||||
elif payload_length >= 126 and payload_length <= 65535:
|
||||
header.append(FIN | opcode)
|
||||
header.append(PAYLOAD_LEN_EXT16)
|
||||
header.extend(struct.pack(">H", payload_length))
|
||||
|
||||
# Huge extended payload
|
||||
elif payload_length < 18446744073709551616:
|
||||
header.append(FIN | opcode)
|
||||
header.append(PAYLOAD_LEN_EXT64)
|
||||
header.extend(struct.pack(">Q", payload_length))
|
||||
|
||||
else:
|
||||
raise Exception("Message is too big. Consider breaking it into chunks.")
|
||||
return
|
||||
|
||||
self.request.send(header + payload)
|
||||
|
||||
def read_http_headers(self):
|
||||
headers = {}
|
||||
# first line should be HTTP GET
|
||||
http_get = self.rfile.readline().decode().strip()
|
||||
assert http_get.upper().startswith('GET')
|
||||
# remaining should be headers
|
||||
while True:
|
||||
header = self.rfile.readline().decode().strip()
|
||||
if not header:
|
||||
break
|
||||
head, value = header.split(':', 1)
|
||||
headers[head.lower().strip()] = value.strip()
|
||||
return headers
|
||||
|
||||
def handshake(self):
|
||||
headers = self.read_http_headers()
|
||||
|
||||
try:
|
||||
assert headers['upgrade'].lower() == 'websocket'
|
||||
except AssertionError:
|
||||
self.keep_alive = False
|
||||
return
|
||||
|
||||
try:
|
||||
key = headers['sec-websocket-key']
|
||||
except KeyError:
|
||||
logger.warning("Client tried to connect but was missing a key")
|
||||
self.keep_alive = False
|
||||
return
|
||||
|
||||
response = self.make_handshake_response(key)
|
||||
self.handshake_done = self.request.send(response.encode())
|
||||
self.valid_client = True
|
||||
self.server._new_client_(self)
|
||||
|
||||
@classmethod
|
||||
def make_handshake_response(cls, key):
|
||||
return \
|
||||
'HTTP/1.1 101 Switching Protocols\r\n'\
|
||||
'Upgrade: websocket\r\n' \
|
||||
'Connection: Upgrade\r\n' \
|
||||
'Sec-WebSocket-Accept: %s\r\n' \
|
||||
'\r\n' % cls.calculate_response_key(key)
|
||||
|
||||
@classmethod
|
||||
def calculate_response_key(cls, key):
|
||||
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
||||
hash = sha1(key.encode() + GUID.encode())
|
||||
response_key = b64encode(hash.digest()).strip()
|
||||
return response_key.decode('ASCII')
|
||||
|
||||
def finish(self):
|
||||
self.server._client_left_(self)
|
||||
|
||||
|
||||
def encode_to_UTF8(data):
|
||||
try:
|
||||
return data.encode('UTF-8')
|
||||
except UnicodeEncodeError as e:
|
||||
logger.error("Could not encode data to UTF-8 -- %s" % e)
|
||||
return False
|
||||
except Exception as e:
|
||||
raise(e)
|
||||
return False
|
||||
|
||||
|
||||
def try_decode_UTF8(data):
|
||||
try:
|
||||
return data.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
except Exception as e:
|
||||
raise(e)
|
Loading…
Reference in New Issue
Block a user