First commit

This commit is contained in:
sam 2020-04-21 15:06:01 +02:00
parent 7f4c43662a
commit 0528576418
26 changed files with 5600 additions and 2 deletions

143
README.md
View File

@ -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
View 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
View 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
}
]
}

BIN
kick.wav Executable file

Binary file not shown.

308
main.py Normal file
View 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
View 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
View 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
View 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
snare.wav Executable file

Binary file not shown.

BIN
web/.DS_Store vendored Normal file

Binary file not shown.

1
web/css/style.css Executable file
View 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
View 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
View 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
View 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

Binary file not shown.

BIN
web/knobs/rebuild.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

506
web/mmo3.html Normal file
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

1875
web/webaudio-controls.js Executable file

File diff suppressed because it is too large Load Diff

197
web/webcomponents-lite.js Executable file
View 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"&amp;";case "<":return"&lt;";case ">":return"&gt;";case "\u00a0":return"&nbsp;"}}}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"&amp;";case "<":return"&lt;";case ">":return"&gt;";case '"':return"&quot;";case "\u00a0":return"&nbsp;"}}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

File diff suppressed because one or more lines are too long

371
websocket_server.py Executable file
View 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)