Initial checkout from https://github.com/Humbedooh/blocky2
diff --git a/client/blocky.py b/client/blocky.py
new file mode 100644
index 0000000..99d16ff
--- /dev/null
+++ b/client/blocky.py
@@ -0,0 +1,488 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import subprocess
+import re
+import json
+import requests
+import netaddr
+import asfpy.daemon
+import yaml
+import socket
+import time
+import sys
+import argparse
+import syslog
+
+DEBUG = False
+CONFIG = None
+SYSLOG = None
+MAX_IPTABLES_TRIES = 10
+IPTABLES_EXEC = '/sbin/iptables'
+IP6TABLES_EXEC = '/sbin/ip6tables'
+LAST_UPLOAD = 0
+
+def getbans(chain = 'INPUT'):
+   """ Gets a list of all bans in a chain """
+   banlist = []
+   
+   # Get IPv4 list
+   for i in range(0,MAX_IPTABLES_TRIES):
+      try:
+         out = subprocess.check_output([IPTABLES_EXEC, '--list', chain, '-n', '--line-numbers'], stderr = subprocess.STDOUT)
+      except subprocess.CalledProcessError as err:
+         if 'you must be root' in err.output:
+            print("Looks like blocky doesn't have permission to access iptables, giving up completely! (are you running as root?)")
+            sys.exit(-1)
+         time.sleep(1) # write lock, probably
+      if out:
+         for line in out.split("\n"):
+            m = re.match(r"^(\d+)\s+([A-Z]+)\s+(all|tcp|udp)\s+(\S+)\s+([0-9a-f.:/]+)\s+([0-9a-f.:/]+)\s*(.*?)$", line)
+            if m:
+               ln = m.group(1)
+               action = m.group(2)
+               protocol = m.group(3)
+               option = m.group(4)
+               source = m.group(5)
+               destination = m.group(6)
+               extensions = m.group(7)
+               
+               entry = {
+                  'chain': chain,
+                  'linenumber': ln,
+                  'action': action,
+                  'protocol': protocol,
+                  'option': option,
+                  'source': source,
+                  'destination': destination,
+                  'extensions': extensions,
+               }
+               
+               banlist.append(entry)
+         break
+   # Get IPv6 list
+   if not os.path.exists(IP6TABLES_EXEC):
+      return banlist
+   for i in range(0,MAX_IPTABLES_TRIES):
+      try:
+         out = subprocess.check_output([IP6TABLES_EXEC, '--list', chain, '-n', '--line-numbers'], stderr = subprocess.STDOUT)
+      except subprocess.CalledProcessError as err:
+         if 'you must be root' in err.output:
+            print("Looks like blocky doesn't have permission to access iptables, giving up completely! (are you running as root?)")
+            sys.exit(-1)
+         time.sleep(1) # write lock, probably
+      if out:
+         for line in out.split("\n"):
+            # Unlike ipv4 iptables, the 'option' thing is blank here, so omit it
+            m = re.match(r"^(\d+)\s+([A-Z]+)\s+(all|tcp|udp)\s+([0-9a-f.:/]+)\s+([0-9a-f.:/]+)\s*(.*?)$", line)
+            if m:
+               ln = m.group(1)
+               action = m.group(2)
+               protocol = m.group(3)
+               source = m.group(4)
+               destination = m.group(5)
+               extensions = m.group(6)
+               
+               entry = {
+                  'chain': chain,
+                  'linenumber': ln,
+                  'action': action,
+                  'protocol': protocol,
+                  'option': '---',
+                  'source': source,
+                  'destination': destination,
+                  'extensions': extensions,
+               }
+               
+               banlist.append(entry)
+         break
+   return banlist
+      
+def iptables(ip, action):
+    """ Runs an iptables action on an IP (-A, -C or -D), returns true if
+        succeeded, false otherwise """
+    try:
+        exe = IPTABLES_EXEC
+        if ':' in ip:
+            exe = IP6TABLES_EXEC
+        subprocess.check_call([
+            exe,
+            action, "INPUT",
+            "-s", ip,
+            "-j", "DROP",
+            "-m", "comment",
+            "--comment",
+            "Banned by Blocky/2.0"
+        ], stderr=open(os.devnull, 'wb'))
+    except subprocess.CalledProcessError as err: # iptables error, expected result variant
+        return False
+    except OSError as err:
+        print("%s not found or inaccessible: %s" % (exe, err))
+        return False
+    return True
+   
+
+def ban(ip):
+   """ Bans an IP or CIDR block generically """
+   if iptables(ip, '-A'):
+      return True
+   return False
+
+def unban_line(ip, linenumber, chain = 'INPUT'):
+    """ Unbans an IP or block by line number """
+    if not linenumber:
+      return
+    exe = IPTABLES_EXEC
+    if ':' in ip:
+      exe = IP6TABLES_EXEC
+    if DEBUG:
+      print("Would have removed line %s from %s chain in iptables here..." % (linenumber, chain))
+      return True
+    try:
+        subprocess.check_call([
+            exe,
+            '-D', chain, linenumber
+        ], stderr=open(os.devnull, 'wb'))
+    except subprocess.CalledProcessError as err: # iptables error, expected result variant
+        return False
+    except OSError as err:
+        print("%s not found or inaccessible: %s" % (exe, err))
+        return False
+    return True
+
+def inlist(banlist, ip):
+   """ Check if an IP or CIDR is listed in iptables,
+   either by itself or contained within a block (or the reverse) """
+   lines = []
+   if '/0' in ip: # DO NOT WANT
+      return lines
+   # First, check verbatim
+   for entry in banlist:
+      if entry['source'] == ip:
+         lines.append(entry)
+   # Check if block, then check for matches within
+   if '/' in ip:
+      me = netaddr.IPNetwork(ip)
+      for entry in banlist:
+         source = entry['source']
+         if '/' not in source: # We don't want to do block vs block just yet
+            them = netaddr.IPAddress(source)
+            if them in me:
+               lines.append(entry)
+   
+   # Then the reverse; IP found within blocks?
+   else:
+      me = netaddr.IPAddress(ip)
+      for entry in banlist:
+         if '/' in entry['source'] and '/0' not in entry['source']: # blocks, but not /0
+            them = netaddr.IPNetwork(entry['source'])
+            if me in them:
+               lines.append(entry)
+   return lines
+
+def note_ban(me, entry):
+   apiurl = "%s/note" % CONFIG['server']['apiurl']
+   try:
+      requests.post(apiurl, json = {
+         'hostname': me,
+         'action': 'ban',
+         'ip': entry['source'],
+         'reason': entry.get('reason', "No reason specified")
+      })
+   except request.RequestException:
+      pass # If it fails with a http error, it fails - we'll continue anyway
+           # Not sure if we should even syslog that..
+
+def note_unban(me, entry):
+   apiurl = "%s/note" % CONFIG['server']['apiurl']
+   try:
+      requests.post(apiurl, json = {
+         'hostname': me,
+         'action': 'unban',
+         'ip': entry['source'],
+         'reason': entry.get('reason', "No reason specified")
+      })
+   except requests.RequestException:
+      pass # If it fails, it fails - we'll continue anyway
+           # Not sure if we should even syslog that..
+
+def run_legacy_checks():
+   """ Runs checks using the legacy blocky UI server (mod_lua) """
+   apiurl = CONFIG['server']['legacyurl']
+   actions = []
+   mylist = getbans()
+   try:
+      actions = requests.get(apiurl).json()
+      syslog.syslog(syslog.LOG_INFO, "Fetched a total of %u firewall actions from %s" % (len(actions), apiurl))
+   except:
+      syslog.syslog(syslog.LOG_WARNING, "Could not retrieve blocky actions list from %s - server down??!" % apiurl)
+   
+   whitelist = [] # Things we are unbanning, and thus shouldn't just ban right again
+   
+   # For each action element, find out what to do, and who to do it to.
+   for action in actions:
+      
+      # Unban request
+      target = action.get('target', '*')
+      if 'unban' in action:
+         if target == '*' or target == CONFIG['client']['hostname']:
+            ip = action.get('ip')
+            if ip:
+               ip = ip.strip()
+               block = None
+               if '/' in ip:
+                  block = netaddr.IPNetwork(ip)
+               else:
+                  if ':' in ip:
+                     block = netaddr.IPNetwork("%s/128" % ip) # IPv6
+                  else:
+                     block = netaddr.IPNetwork("%s/32" % ip)  # IPv4
+               whitelist.append(block)
+               found = inlist(mylist, ip)
+               if found:
+                  entry = found[0]
+                  syslog.syslog(syslog.LOG_INFO, "Removing %s from block list (found at line %s as %s)" % (ip, entry['linenumber'], entry['source']))
+                  if not unban_line(ip, found[0]['linenumber']):
+                     syslog.syslog(syslog.LOG_WARNING, "Could not remove ban for %s from iptables!" % ip)
+                  else:
+                     mylist = getbans() # Refresh after action succeeded
+                     
+      # Ban request?
+      elif 'ip' in action:
+         if target == '*' or target == CONFIG['client']['hostname']:
+            ip = action.get('ip')
+            if ip:
+               ip = ip.strip() # backwards compat
+               banit = True
+               block = None
+               if '/' in ip:
+                  block = netaddr.IPNetwork(ip)
+               else:
+                  if ':' in ip:
+                     block = netaddr.IPNetwork("%s/128" % ip) # IPv6
+                  else:
+                     block = netaddr.IPNetwork("%s/32" % ip)  # IPv4
+               for wblock in whitelist:
+                  if block in wblock or wblock in block:
+                     syslog.syslog(syslog.LOG_WARNING, "%s was requested banned but %s is whitelisted, ignoring ban" % (block, wblock))
+                     banit = False
+               if banit:
+                  found = inlist(mylist, ip)
+                  if not found:
+                     reason = action.get('reason', "No reason specified")
+                     syslog.syslog(syslog.LOG_INFO, "Adding %s to block list; %s" % (ip, reason))
+                     if not ban(ip):
+                        syslog.syslog(syslog.LOG_WARNING, "Could not add ban for %s in iptables!" % ip)
+                     else:
+                        mylist = getbans() # Refresh after action succeeded
+                        
+def run_new_checks():
+   """ Runs the blocky process using the modern UI server """
+   global LAST_UPLOAD
+   
+   # First, get our rules and post 'em to the server, if need be
+   mylist = getbans()
+   if LAST_UPLOAD < (time.time() - 600): # Only send once every ten minutes
+      try:
+         rv = None
+         js = {
+            'hostname': CONFIG['client']['hostname'],
+            'iptables': mylist
+         }
+         apiurl = "%s/myrules" % CONFIG['server']['apiurl']
+         rv = requests.put(apiurl, json = js)
+         assert(rv.status_code == 200)
+         LAST_UPLOAD = time.time()
+      except requests.RequestException:
+         if rv:
+            syslog.syslog(syslog.LOG_WARNING, rv.text)
+         syslog.syslog(syslog.LOG_WARNING, "Could not send my iptables list to server at %s - server down?" % apiurl)
+
+   # Then, get applicable actions from the server
+   whitelist = []
+   whiteblocks = [] # same as above, but as IPNetwork classes
+   banlist = []
+   try:
+      whiteurl = "%s/whitelist" % CONFIG['server']['apiurl']
+      whitelist = requests.get(whiteurl).json()['whitelist']
+   except requests.RequestException:
+      syslog.syslog(syslog.LOG_WARNING, "Could not fetch whitelist entries at %s - server down?" % whiteurl)
+   try:
+      banurl = "%s/bans" % CONFIG['server']['apiurl']
+      banlist = requests.get(banurl).json()['bans']
+   except requests.RequestException:
+      syslog.syslog(syslog.LOG_WARNING, "Could not fetch whitelist entries at %s - server down?" % banurl)
+   
+   # First, check if we've banned someone on the whitelist
+   for entry in whitelist:
+      ip = entry.get('ip')
+      reason = entry.get('reason', 'No reason specified')
+      target = entry.get('target', '*')
+      if target == '*' or target == CONFIG['client']['hostname']:
+         if ip:
+            block = None
+            if '/' in ip:
+               block = netaddr.IPNetwork(ip)
+            else:
+               if ':' in ip:
+                  block = netaddr.IPNetwork("%s/128" % ip) # IPv6
+               else:
+                  block = netaddr.IPNetwork("%s/32" % ip)  # IPv4
+               whiteblocks.append(block)
+               found = inlist(mylist, ip)
+               if found:
+                  entry = found[0]
+                  syslog.syslog(syslog.LOG_INFO, "Removing %s from block list (found at line %s as %s)" % (ip, entry['linenumber'], entry['source']))
+                  if not unban_line(ip, found[0]['linenumber']):
+                     syslog.syslog(syslog.LOG_WARNING, "Could not remove ban for %s from iptables!" % ip)
+                  else:
+                     note_unban(CONFIG['client']['hostname'], found[0]['linenumber'])
+                     mylist = getbans() # Refresh after action succeeded
+   
+   # Then process bans
+   for entry in banlist:
+      ip = entry.get('ip')
+      reason = entry.get('reason', 'No reason specified')
+      target = entry.get('target', '*')
+      if ip:
+         if target == '*' or target == CONFIG['client']['hostname']:
+            banit = True
+            block = None
+            if '/' in ip:
+               block = netaddr.IPNetwork(ip)
+            else:
+               if ':' in ip:
+                  block = netaddr.IPNetwork("%s/128" % ip) # IPv6
+               else:
+                  block = netaddr.IPNetwork("%s/32" % ip)  # IPv4
+            for wblock in whiteblocks:
+               if block in wblock or wblock in block:
+                  syslog.syslog(syslog.LOG_WARNING, "%s was requested banned but %s is whitelisted, ignoring ban" % (block, wblock))
+                  banit = False
+            if banit:
+               found = inlist(mylist, ip)
+               if not found:
+                  reason = entry.get('reason', "No reason specified")
+                  syslog.syslog(syslog.LOG_INFO, "Adding %s to block list; %s" % (ip, reason))
+                  if not ban(ip):
+                     syslog.syslog(syslog.LOG_WARNING, "Could not add ban for %s in iptables!" % ip)
+                  else:
+                     mylist = getbans() # Refresh after action succeeded
+                     found = inlist(mylist, ip)
+                     if found: # make sure we have it in iptables now
+                        note_ban(CONFIG['client']['hostname'], found[0])
+   # All done for this time!
+
+def psyslog(a,b):
+   """ nasty hack for copying syslog calls to stdout """
+   SYSLOG(a, b)
+   print("- " + b)
+   
+def run_daemon(stdout = False):
+   global SYSLOG, CONFIG
+   if stdout:
+      SYSLOG = syslog.syslog
+      syslog.syslog = psyslog
+   else:
+      syslog.openlog('blocky', logoption=syslog.LOG_PID, facility=syslog.LOG_LOCAL0)
+      syslog.syslog(syslog.LOG_INFO, "Blocky/2 started")
+   while True:
+      # Fetch actions list - legacy or new
+      if CONFIG['server'].get('legacyurl'):
+         syslog.syslog(syslog.LOG_INFO, "Using legacy server component at %s" % CONFIG['server']['legacyurl'])
+         run_legacy_checks()
+      elif CONFIG['server'].get('apiurl'):
+         syslog.syslog(syslog.LOG_INFO, "Using modern server component at %s" % CONFIG['server']['apiurl'])
+         run_new_checks()
+      if stdout:
+         return
+      time.sleep(CONFIG['client'].get('interval', 60))
+
+def base_parser():
+    arg_parser = argparse.ArgumentParser()
+    arg_parser.add_argument("-x", "--user", help="not used (legacy compat)")
+    arg_parser.add_argument("-y", "--group", help="not used (legacy compat)")
+    arg_parser.add_argument("-u", "--unban", help="An IP or CIDR block to unban manually")
+    arg_parser.add_argument("-b", "--ban", help="An IP or CIDR block to ban manually")
+    arg_parser.add_argument("-d", "--daemonize", action = 'store_true', help="Run blocky as a daemon")
+    arg_parser.add_argument("-s", "--stop", action = 'store_true', help="Stop blocky daemon")
+    arg_parser.add_argument("-f", "--foreground", action = 'store_true', help="Run blocky in the foreground (debugging)")
+    return arg_parser
+
+
+def start_client():
+   global CONFIG
+   # Figure out who we are
+   me = socket.gethostname()
+   if 'apache.org' not in me:
+      me += '.apache.org'
+   
+   # Load YAML
+   CONFIG = yaml.load(open('./blocky.yaml').read())
+   if 'client' not in CONFIG:
+      CONFIG['client'] = {}
+   if 'hostname' not in CONFIG['client']:
+      CONFIG['client']['hostname'] = me
+   
+   # Get current list of bans in iptables, upload it to blocky server
+   l = getbans()
+   
+   args = base_parser().parse_args()
+   
+   # CLI unban?
+   if args.unban:
+      ip = args.unban
+      found = inlist(l, ip) # random test
+      if found:
+         entry = found[0] # Only get the first entry, line numbers will then change ;\
+         print("Found a block for %s on line %s in the %s chain (as %s), removing..." % (ip, entry['linenumber'], entry['chain'], entry['source']))
+         if unban_line(entry['linenumber']):
+            print("Refreshing ban list...")
+            l = getbans()
+      else:
+         print("%s wasn't found in iptables, nothing to do" % ip)
+      return
+   
+   # CLI ban?
+   if args.ban:
+      ip = args.ban
+      found = inlist(l, ip)
+      if found:
+         print("%s is already banned here as %s, nothing to do" % (ip, found[0]['source']))
+      else:
+         if ban(ip):
+            print("IP %s successfully banned using generic ruleset" % ip)
+         else:
+            print("Could not ban %s, bummer" % ip)
+      return
+   
+   # Daemon stuff?
+   d = asfpy.daemon(run_daemon)
+   
+   # Start daemon?
+   if args.daemonize:
+      d.start()
+   # stop daemon?
+   elif args.stop:
+      d.stop()
+   elif args.foreground:
+      run_daemon(True)
+
+if __name__ == '__main__':
+   start_client()
+
diff --git a/client/blocky.yaml b/client/blocky.yaml
new file mode 100644
index 0000000..edfa536
--- /dev/null
+++ b/client/blocky.yaml
@@ -0,0 +1,12 @@
+client:
+    interval:     60
+
+server:
+    apiurl:        https://blocky.foo.org/blocky/api
+    # bla bla bla
+    #legacyurl:     https://blocky.apache.org/blocky.json
+
+iptables:
+    chains:
+        - INPUT
+        - fail2ban-default
diff --git a/server/api/api.html b/server/api/api.html
new file mode 100644
index 0000000..f03a80a
--- /dev/null
+++ b/server/api/api.html
@@ -0,0 +1,503 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+</head>
+<body>
+<h1>blocky/2 JSON API</h1>
+<h3>Overview:</h3><ul style='font-size: 12px; font-family: Open Sans, sans-serif;'><li><a href='#get-api-activity'>GET /api/activity</a>: Displays the current activity entries</li>
+<li><a href='#delete-api-bans'>DELETE /api/bans</a>: Remove a whitelist entry</li>
+<li><a href='#get-api-bans'>GET /api/bans</a>: Displays the current banlist entries</li>
+<li><a href='#put-api-bans'>PUT /api/bans</a>: Add a banmlist entry</li>
+<li><a href='#put-api-myrules'>PUT /api/myrules</a>: Set a host's iptables list</li>
+<li><a href='#delete-api-rules'>DELETE /api/rules</a>: Remove a rule entry</li>
+<li><a href='#get-api-rules'>GET /api/rules</a>: Displays the current ruleset entries</li>
+<li><a href='#put-api-rules'>PUT /api/rules</a>: Adds or overrides a ruleset</li>
+<li><a href='#post-api-search'>POST /api/search</a>: Search for whether an IP or block is banned or whitelisted</li>
+<li><a href='#delete-api-whitelist'>DELETE /api/whitelist</a>: Remove a whitelist entry</li>
+<li><a href='#get-api-whitelist'>GET /api/whitelist</a>: Displays the current whitelist entries</li>
+<li><a href='#put-api-whitelist'>PUT /api/whitelist</a>: Add a whitelist entry</li>
+</ul>
+
+                      <div id="get-api-activity" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #61affe; font-family: sans-serif; background: #61affe30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #61affe;">GET</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/activity</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Displays the current activity entries</div>
+                          <div style="float: left; width: 90%;display: none; ">
+                            <h4>JSON parameters:</h4>
+                            
+                            <br/>
+                            
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="delete-api-bans" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #f93e3e; font-family: sans-serif; background: #f93e3e30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #f93e3e;">DELETE</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/bans</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Remove a whitelist entry</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>force:</b></kbd> (boolean) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Force addition, even if it means removing conflicting rules already in the DB</span><br/>
+<kbd><b>reason:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The reason for banning</span><br/>
+<kbd><b>rule:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the ID of an existing rule</span><br/>
+<kbd><b>source:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the source IP Address or Network</span><br/>
+<kbd><b>target:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Target machine to ban on (or * for all machines)</span><br/>
+<kbd><b>timeout:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>If present and greater than 0, denotes the UNIX timestamp when this rule stops working</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="get-api-bans" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #61affe; font-family: sans-serif; background: #61affe30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #61affe;">GET</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/bans</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Displays the current banlistr entries</div>
+                          <div style="float: left; width: 90%;display: none; ">
+                            <h4>JSON parameters:</h4>
+                            
+                            <br/>
+                            
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="put-api-bans" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #fca130; font-family: sans-serif; background: #fca13030;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #fca130;">PUT</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/bans</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Add a banmlist entry</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>force:</b></kbd> (boolean) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Force addition, even if it means removing conflicting rules already in the DB</span><br/>
+<kbd><b>reason:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The reason for banning</span><br/>
+<kbd><b>rule:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the ID of an existing rule</span><br/>
+<kbd><b>source:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the source IP Address or Network</span><br/>
+<kbd><b>target:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Target machine to ban on (or * for all machines)</span><br/>
+<kbd><b>timeout:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>If present and greater than 0, denotes the UNIX timestamp when this rule stops working</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="put-api-myrules" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #fca130; font-family: sans-serif; background: #fca13030;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #fca130;">PUT</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/myrules</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Set a host's iptables list</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{
+    "iptables": [
+        {
+            "action": "DROP",
+            "chain": "INPUT",
+            "destination": "1.2.3.4",
+            "extensions": "/* comment here */",
+            "linenumber": 26,
+            "option": "comment",
+            "protocol": "tcp",
+            "source": "1.2.3.4"
+        }
+    ]
+}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="delete-api-rules" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #f93e3e; font-family: sans-serif; background: #f93e3e30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #f93e3e;">DELETE</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/rules</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Remove a rule entry</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>limit:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The limit that causes a ban</span><br/>
+<kbd><b>name:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The name of the rule</span><br/>
+<kbd><b>query:</b></kbd> (array) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The individual query args to look for</span><br/>
+<kbd><b>rid:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>rule ID, if overriding a previous rule</span><br/>
+<kbd><b>span:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The span (in hours) within which to look for offenses</span><br/>
+<kbd><b>type:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The type of rule</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="get-api-rules" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #61affe; font-family: sans-serif; background: #61affe30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #61affe;">GET</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/rules</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Displays the current ruleset entries</div>
+                          <div style="float: left; width: 90%;display: none; ">
+                            <h4>JSON parameters:</h4>
+                            
+                            <br/>
+                            
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="put-api-rules" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #fca130; font-family: sans-serif; background: #fca13030;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #fca130;">PUT</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/rules</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Adds or overrides a ruleset</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>limit:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The limit that causes a ban</span><br/>
+<kbd><b>name:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The name of the rule</span><br/>
+<kbd><b>query:</b></kbd> (array) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The individual query args to look for</span><br/>
+<kbd><b>rid:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>rule ID, if overriding a previous rule</span><br/>
+<kbd><b>span:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The span (in hours) within which to look for offenses</span><br/>
+<kbd><b>type:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The type of rule</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="post-api-search" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #49cc5c; font-family: sans-serif; background: #49cc5c30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #49cc5c;">POST</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/search</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Search for whether an IP or block is banned or whitelisted</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>source:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>IP Address or Network to search for</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="delete-api-whitelist" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #f93e3e; font-family: sans-serif; background: #f93e3e30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #f93e3e;">DELETE</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/whitelist</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Remove a whitelist entry</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>force:</b></kbd> (boolean) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Force addition, even if it means removing conflicting rules already in the DB</span><br/>
+<kbd><b>reason:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The reason for banning</span><br/>
+<kbd><b>rule:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the ID of an existing rule</span><br/>
+<kbd><b>source:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the source IP Address or Network</span><br/>
+<kbd><b>target:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Target machine to ban on (or * for all machines)</span><br/>
+<kbd><b>timeout:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>If present and greater than 0, denotes the UNIX timestamp when this rule stops working</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="get-api-whitelist" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #61affe; font-family: sans-serif; background: #61affe30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #61affe;">GET</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/whitelist</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Displays the current whitelist entries</div>
+                          <div style="float: left; width: 90%;display: none; ">
+                            <h4>JSON parameters:</h4>
+                            
+                            <br/>
+                            
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+
+                      <div id="put-api-whitelist" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid #fca130; font-family: sans-serif; background: #fca13030;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: #fca130;">PUT</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>/api/whitelist</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          Add a whitelist entry</div>
+                          <div style="float: left; width: 90%;display: block; ">
+                            <h4>JSON parameters:</h4>
+                            <div style='float: left; width: 90%;'><blockquote><pre style='width: 600px;'><kbd><b>force:</b></kbd> (boolean) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Force addition, even if it means removing conflicting rules already in the DB</span><br/>
+<kbd><b>reason:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>The reason for banning</span><br/>
+<kbd><b>rule:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the ID of an existing rule</span><br/>
+<kbd><b>source:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>the source IP Address or Network</span><br/>
+<kbd><b>target:</b></kbd> (string) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>Target machine to ban on (or * for all machines)</span><br/>
+<kbd><b>timeout:</b></kbd> (integer) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>If present and greater than 0, denotes the UNIX timestamp when this rule stops working</span><br/>
+</pre>
+</blockquote></div>
+                            <br/>
+                            <div style='float: left; width: 90%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>application/json</b>:
+{}</pre></blockquote>
+</div>
+                          </div>
+                          <div style="float: left; width: 90%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote><div style='float: left; width: 90%;'><pre style='width: 600px;'><b>200</b>:
+{
+    "message": "Action completed"
+}</pre>
+</div>
+<div style='float: left; width: 90%;'><pre style='width: 600px;'><b>default</b>:
+{
+    "code": 403,
+    "reason": "You need to be logged in to view this endpoint!"
+}</pre>
+</div>
+</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      
+</body></html>
diff --git a/server/api/handler.py b/server/api/handler.py
new file mode 100644
index 0000000..3fc5a4a
--- /dev/null
+++ b/server/api/handler.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This is the main WSGI handler file for Blocky.
+It compiles a list of valid URLs from the 'pages' library folder,
+and if a URL matches it runs the specific submodule's run() function. It
+also handles CGI parsing and exceptions in the applications.
+"""
+
+
+# Main imports
+import cgi
+import re
+import sys
+import traceback
+import yaml
+import json
+import plugins.session
+import plugins.database
+import plugins.openapi
+import plugins.worker
+import atexit
+import os
+import time
+import threading
+
+
+# Hack to start first worker as a background thread
+ppid = os.getppid()
+now = time.time()
+pidfile = "./blocky-bg-%s.pid" % ppid
+background = False
+
+def rmpidfile():
+    try:
+        print("Removing BG thread pid file...")
+        os.unlink(pidfile)
+    except:
+        pass
+
+if not os.path.exists(pidfile) or os.path.getmtime(pidfile) < (now - 60):
+    print("Starting this thread as background worker")
+    open(pidfile, "w").write(str(os.getpid()))
+    atexit.register(rmpidfile)
+    background = True
+
+# Compile valid API URLs from the pages library
+# Allow backwards compatibility by also accepting .lua URLs
+urls = []
+if __name__ != '__main__':
+    import pages
+    for page in pages.handlers:
+        urls.append((r"^(/api/%s)(/.+)?$" % page, pages.handlers[page].run))
+
+
+# Load Blocky master configuration
+config = yaml.load(open("yaml/blocky.yaml"))
+
+# Instantiate database connections
+DB = plugins.database.BlockyDatabase(config)
+
+# Load Open API specifications
+BlockyOpenAPI = plugins.openapi.OpenAPI("yaml/openapi.yaml")
+
+# Start as background worker?
+if background:
+    t = threading.Thread(target = plugins.worker.start, args = (DB, config, pidfile))
+    t.start()
+
+
+class BlockyAPIWrapper:
+    """
+    Middleware wrapper for exceptions in the application
+    """
+    def __init__(self, path, func):
+        self.func = func
+        self.API = BlockyOpenAPI
+        self.path = path
+        self.exception = plugins.openapi.BlockyHTTPError
+     
+    def __call__(self, environ, start_response, session):
+        """Run the function, return response OR return stacktrace"""
+        response = None
+        
+        # CORS IGNORE
+        if environ.get('REQUEST_METHOD', 'GET') == 'OPTIONS':
+            start_response('200 Fine, whatever', [
+                        ('Content-Type', 'application/json')])
+            yield json.dumps({
+                "code": 200,
+                "reason": "You're probably just being silly..."
+            })
+            return
+        
+        try:
+            # Read JSON client data if any
+            try:
+                request_size = int(environ.get('CONTENT_LENGTH', 0))
+            except (ValueError):
+                request_size = 0
+            requestBody = environ['wsgi.input'].read(request_size)
+            formdata = {}
+            if requestBody and len(requestBody) > 0:
+                try:
+                    formdata = json.loads(requestBody.decode('utf-8'))
+                    
+                except json.JSONDecodeError as err:
+                    start_response('400 Invalid request', [
+                               ('Content-Type', 'application/json')])
+                    yield json.dumps({
+                        "code": 400,
+                        "reason": "Invalid JSON: %s" % err
+                    })
+                    return
+            else:
+                # Try query string??
+                xdata = cgi.parse_qs(environ.get('QUERY_STRING'))
+                for k, v in xdata.items():
+                    formdata[k] = v[0]
+            
+            
+            # Validate URL against OpenAPI specs
+            try:
+                self.API.validate(environ['REQUEST_METHOD'], self.path, formdata)
+            except plugins.openapi.OpenAPIException as err:
+                start_response('400 Invalid request', [
+                            ('Content-Type', 'application/json')])
+                yield json.dumps({
+                    "code": 400,
+                    "reason": err.message
+                })
+                return
+            
+            # Call page with env, SR and form data
+            try:
+                response = self.func(self, environ, formdata, session)
+                if response:
+                    for bucket in response:
+                        yield bucket
+            except plugins.openapi.BlockyHTTPError as err:
+                errHeaders = {
+                    403: '403 Authentication failed',
+                    404: '404 Resource not found',
+                    500: '500 Internal Server Error',
+                    501: '501 Gateway error'
+                }
+                errHeader = errHeaders[err.code] if err.code in errHeaders else "400 Bad request"
+                start_response(errHeader, [
+                            ('Content-Type', 'application/json')])
+                yield json.dumps({
+                    "code": err.code,
+                    "reason": err.message
+                }, indent = 4) + "\n"
+                return
+            
+        except:
+            err_type, err_value, tb = sys.exc_info()
+            traceback_output = ['API traceback:']
+            traceback_output += traceback.format_tb(tb)
+            traceback_output.append('%s: %s' % (err_type.__name__, err_value))
+            # We don't know if response has been given yet, try giving one, fail gracefully.
+            try:
+                start_response('500 Internal Server Error', [
+                               ('Content-Type', 'application/json')])
+            except:
+                pass
+            yield json.dumps({
+                "API": "Blocky API 1.0",
+                "code": "500",
+                "reason": '\n'.join(traceback_output)
+            })
+    
+        
+def fourohfour(environ, start_response):
+    """A very simple 404 handler"""
+    start_response("404 Not Found", [
+                ('Content-Type', 'application/json')])
+    yield json.dumps({
+        "API": "Blocky API 2.0",
+        "code": 404,
+        "reason": "API endpoint not found"
+    }, indent = 4) + "\n"
+    return
+
+    
+def application(environ, start_response):
+    """
+    This is the main handler. Every API call goes through here.
+    Checks against the pages library, and if submod found, runs
+    it and returns the output.
+    """
+    path = environ.get('PATH_INFO', '')
+    for regex, function in urls:
+        m = re.match(regex, path)
+        if m:
+            callback = BlockyAPIWrapper(path, function)
+            session = plugins.session.BlockySession(DB, environ, config)
+            a = 0
+            for bucket in callback(environ, start_response, session):
+                if a == 0:
+                    session.headers.append(bucket)
+                    try:
+                        start_response("200 Okay", session.headers)
+                    except:
+                        pass
+                a += 1
+                # WSGI prefers byte strings, so convert if regular py3 string
+                if isinstance(bucket, str):
+                    yield bytes(bucket, encoding = 'utf-8')
+                elif isinstance(bucket, bytes):
+                    yield bucket
+            return
+            
+    for bucket in fourohfour(environ, start_response):
+        yield bytes(bucket, encoding = 'utf-8')
+
+
+
+if __name__ == '__main__':
+    BlockyOpenAPI.toHTML()
diff --git a/server/api/pages/__init__.py b/server/api/pages/__init__.py
new file mode 100644
index 0000000..cf4d933
--- /dev/null
+++ b/server/api/pages/__init__.py
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+Kibble API scripts library:
+
+    oauth:          oauth manager
+
+"""
+
+import importlib
+import os
+# Define all the submodules we have
+
+rootpath = os.path.dirname(__file__)
+print("Reading pages from %s" % rootpath)
+
+# Import each submodule into a hash called 'handlers'
+handlers = {}
+
+def loadPage(path):
+    for el in os.listdir(path):
+        filepath = os.path.join(path, el)
+        if el.find("__") == -1:
+            if os.path.isdir(filepath):
+                loadPage(filepath)
+            else:
+                p = filepath.replace(rootpath, "")[1:].replace('/', '.')[:-3]
+                xp = p.replace('.', '/')
+                #print("Loading endpoint pages.%s as %s" % (p, xp))
+                handlers[xp] = importlib.import_module("pages.%s" % p)
+
+loadPage(rootpath)
diff --git a/server/api/pages/activity.py b/server/api/pages/activity.py
new file mode 100644
index 0000000..d50e9d0
--- /dev/null
+++ b/server/api/pages/activity.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/activity
+########################################################################
+# get:
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Empty'
+#       description: 200 response
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Displays the current activity entries
+# 
+########################################################################
+
+
+
+"""
+This is the activity handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+
+def run(API, environ, indata, session):
+    global WHITE_CACHE, WHITE_TS
+    method = environ['REQUEST_METHOD']
+    
+    # Display the current activity entries
+    if method == "GET":
+        activity = []
+        res = session.DB.ES.search(
+                index=session.DB.dbname,
+                doc_type="note",
+                size = 500,
+                body = {
+                    'query': {
+                        'match_all': {}
+                    },
+                    'sort': {
+                        "epoch": "desc"
+                    }
+                }
+            )
+    
+        for hit in res['hits']['hits']:
+             doc = hit['_source']
+             if doc['ntype'] in ['manual', 'client', 'system', 'autoban']:
+                activity.append(doc)
+         
+        JSON_OUT = {
+            'activity': activity
+        }
+        yield json.dumps(JSON_OUT)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/bans.py b/server/api/pages/bans.py
new file mode 100644
index 0000000..15f87e6
--- /dev/null
+++ b/server/api/pages/bans.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/bans
+########################################################################
+# delete:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/IPAddress'
+#     description: Removes a banlist entry
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Removal successful
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Remove a whitelist entry
+# get:
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Empty'
+#       description: 200 response
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Displays the current banlistr entries
+# put:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/IPAddress'
+#     description: IP address or block to ban
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Ban entry added
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Add a banmlist entry
+# 
+########################################################################
+
+
+
+
+
+"""
+This is the banlist handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+
+# Cached vars
+BANLIST = []
+BAN_TS = 0
+BAN_CACHE_TIME = 30
+
+def find_rule(DB, doctype, ip):
+    """ Find a rule, either v1 or v2 style """
+    bid = plugins.worker.make_sha1(str(ip))
+    # Blocky/2 ban doc
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = bid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = bid)
+    # Blocky/1 ban doc
+    oid = str(ip).replace('/', '_').replace('_32', '').replace('_128', '')
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = oid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = oid)
+
+
+def to_whitelist_temp(DB, hit):
+    """ Temporarily turn a banlist entry into a whitelist entry """
+    doc = hit['_source']
+    ipaddress = doc.get('ip')
+    if not ipaddress:
+        ipaddress = hit['_id'].replace('_', '/') # Blocky/1 syntax, bah
+    if ipaddress:
+        ipaddress = ipaddress.strip() # blocky/1 bug
+        block = plugins.worker.to_block(ipaddress)
+        ip = str(block)
+        entry = {
+                'ip': ip,
+                'reason': "Temporary system whitelist due to unban",
+                'target': "*",
+                'epoch': int(time.time()),
+                'timeout': int(time.time() + 3600)
+            }
+        bid = plugins.worker.make_sha1(ip)
+        DB.ES.index(index=DB.dbname, doc_type = 'whitelist', id = bid, body = entry)
+        plugins.worker.addnote(DB, 'system', "Whitelisting %s temporarily to flush blocks" % (ipaddress))
+
+
+def remove_whitelist(session, white):
+    """ Remove a white, either v1 or v2 style """
+    plugins.worker.addnote(session.DB, 'system',"Removing whitelist entry for %s due to forced banlisting" % white)
+    bid = plugins.worker.make_sha1(str(white))
+    # Blocky/2 ban doc
+    if session.DB.ES.exists(index=session.DB.dbname, doc_type = 'whitelist', id = bid):
+        session.DB.ES.delete(index=session.DB.dbname, doc_type = 'whitelist', id = bid, refresh = 'wait_for')
+    # Blocky/1 ban doc
+    oid = str(white).replace('/', '_').replace('_32', '')
+    if session.DB.ES.exists(index=session.DB.dbname, doc_type = 'whitelist', id = oid):
+        session.DB.ES.delete(index=session.DB.dbname, doc_type = 'whitelist', id = oid, refresh = 'wait_for')
+
+
+def run(API, environ, indata, session):
+    global BANLIST, BAN_TS
+    method = environ['REQUEST_METHOD']
+    
+    # Adding a new entry?
+    if method == "PUT":
+        ip = indata['source']
+        reason = indata['reason']
+        target = indata.get('target', '*')
+        force = indata.get('force', False)
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        reason = "Banned by %s: %s" % (submitter, reason)
+        
+        # Check if ban already exists
+        if find_rule(session.DB, 'ban', ip):
+            raise API.exception(400, "A ban already exists for this IP!")
+        
+        # Check if this IP is within a whitelisted space
+        block = plugins.worker.to_block(ip)
+        whitelist = plugins.worker.get_whitelist(session.DB)
+        for white in whitelist:
+            if block in white:
+                if force:
+                    remove_whitelist(session, white)
+                else:
+                    raise API.exception(403, "IP Address is whitelisted as %s, cannot ban!" % white)
+            if white in block:
+                if force:
+                    remove_whitelist(session, white)
+                else:
+                    raise API.exception(403, "This ban would cancel whitelist entry for %s, cannot mix" % white)
+        
+        # all good? Okay, add the entry then
+        entry = {
+            'ip': ip,
+            'reason': reason,
+            'target': target,
+            'epoch': int(time.time())
+        }
+        bid = plugins.worker.make_sha1(str(block))
+        session.DB.ES.index(index=session.DB.dbname, doc_type = 'ban', id = bid, body = entry)
+        plugins.worker.addnote(session.DB, 'manual', "Manual ban for %s added by %s: %s" % (ip, submitter, reason))
+        yield json.dumps({"message": "Entry added!"})
+        return
+    
+    
+    # Delete an entry
+    if method == "DELETE":
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        rid = indata.get('rule')
+        doc = None
+        if re.match(r"^[a-f0-9]+$", rid):
+            if session.DB.ES.exists(index=session.DB.dbname, doc_type='ban', id = rid):
+                hit = session.DB.ES.get(index=session.DB.dbname, doc_type='ban', id = rid)
+                plugins.worker.addnote(session.DB, 'manual', "Ban for %s removed by %s" % (hit['_source'].get('ip', rid), submitter))
+                to_whitelist_temp(session.DB, hit)
+                session.DB.ES.delete(index=session.DB.dbname, doc_type='ban', id = rid, refresh = 'wait_for')
+            yield json.dumps({"message": "Entry removed"})
+            return
+        elif re.match(r"^[a-f0-9.:_]+$", rid):
+            if session.DB.ES.exists(index=session.DB.dbname, doc_type='ban', id = rid):
+                hit = session.DB.ES.get(index=session.DB.dbname, doc_type='ban', id = rid, refresh = 'wait_for')
+                plugins.worker.addnote(session.DB, 'manual', "Ban for %s removed by %s" % (hit['_source'].get('ip', rid), submitter))
+                to_whitelist_temp(session.DB, hit)
+                session.DB.ES.delete(index=session.DB.dbname, doc_type='ban', id = rid)
+            yield json.dumps({"message": "Entry removed"})
+            return
+        else:
+            raise API.exception(400, "Invalid rule ID specified!")
+    
+    # Display the current banlist entries
+    if method == "GET":
+        # Only re-fetch banlist every 30 secs, save processing power!
+        if BAN_TS < (time.time() - BAN_CACHE_TIME) or 'Mozilla' in environ.get('HTTP_USER_AGENT', 'python'):
+            res = session.DB.ES.search(
+                    index=session.DB.dbname,
+                    doc_type="ban",
+                    size = 10000,
+                    body = {
+                        'query': {
+                            'match_all': {}
+                        }
+                    }
+                )
+        
+            BANLIST = []
+            for hit in res['hits']['hits']:
+                doc = hit['_source']
+                ip = doc.get('ip')
+                if not ip:
+                    ip = hit['_id'].replace('_', '/') # backwards compat
+                if ip:
+                    doc['ip'] = ip.strip()
+                    doc['rid'] = hit['_id']
+                    BANLIST.append(doc)
+            BAN_TS = time.time()
+            
+        JSON_OUT = {
+            'bans': BANLIST
+        }
+        yield json.dumps(JSON_OUT)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/fail2ban.py b/server/api/pages/fail2ban.py
new file mode 100644
index 0000000..d8514b3
--- /dev/null
+++ b/server/api/pages/fail2ban.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/fail2ban
+########################################################################
+# post:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/SearchObject'
+#     description: Search query
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/SearchResult'
+#       description: Search result
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Search for whether a user was blocked via fail2ban
+# 
+########################################################################
+
+
+
+"""
+This is the search handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+import netaddr
+import datetime
+
+def find_rule(DB, doctype, ip):
+    """ Find a rule, either v1 or v2 style """
+    bid = plugins.worker.make_sha1(str(ip))
+    bid2 = plugins.worker.make_sha1(str(ip).replace('/32', '').replace('/128',''))
+    # Blocky/2 ban doc
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = bid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = bid)
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = bid2):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = bid2)
+    
+    # Blocky/1 ban doc
+    oid = str(ip).replace('/', '_').replace('_32', '').replace('_128', '')
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = oid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = oid)
+
+
+def run(API, environ, indata, session):
+    method = environ['REQUEST_METHOD']
+    
+    # Searching? :)
+    if method == "POST":
+        found = {
+            'whitelist': [],
+            'banlist': [],
+            'iptables': [],
+        }
+        user = indata.get('source')
+        
+        
+        # Prep list of indices to check against, for performance reasons
+        d = datetime.datetime.utcnow()
+        t = []
+        for i in range(0,7):
+            t.append(d.strftime("loggy-%Y.%m.%d"))
+            d -= datetime.timedelta(days = 1)
+        threes = ",".join(t) # Past seven days
+        
+        res = session.DB.ES.search(
+                    index=threes,
+                    size = 500,
+                    body = {
+                        "query": {
+                            "bool": {
+                                "must": [
+                                    {
+                                        "match": {
+                                            "message": "AH01617"
+                                        }                                        
+                                    },
+                                    {
+                                        "match": {
+                                            "message": user
+                                        }                                        
+                                    },
+                                    {
+                                    "term": {
+                                        "_type": 'apache_error'
+                                    }
+                                  },
+                                ]
+                            }
+                        }
+                    }
+                )
+        ips = {}
+        
+        for hit in res['hits']['hits']:
+            doc = hit['_source']
+            if doc.get('module') == 'auth_basic:error' and user in doc.get('message') and 'client_ip' in doc:
+                ips[doc['client_ip']] = doc['message']
+        
+                
+        #get whitelist and banlist, plus iptables rules
+        whitelist = plugins.worker.get_whitelist(session.DB)
+        banlist = plugins.worker.get_banlist(session.DB)
+        iptables = plugins.worker.get_iptables(session.DB)
+        
+                
+        for ip, msg in ips.items():
+            print(ip)
+            me = plugins.worker.to_block(ip) # queried IP as IPNetwork object
+            # Find all whitelist entries that touch on this
+            for block in whitelist:
+                if me in block or block in me or me == block:
+                    rule = find_rule(session.DB, 'whitelist', str(block))
+                    if rule:
+                        doc = rule['_source']
+                        doc['rid'] = rule['_id']
+                        found['whitelist'].append(doc)
+            
+            # Find all banlist entries that touch on this
+            for block in banlist:
+                if me in block or block in me or me == block:
+                    rule = find_rule(session.DB, 'ban', str(block))
+                    if rule:
+                        doc = rule['_source']
+                        doc['rid'] = rule['_id']
+                        if not 'ip' in doc:
+                            doc['ip'] = doc['rid'].replace('_', '/')
+                        found['banlist'].append(doc)
+            
+            # Find any iptables rules that may have it as well (max 10)
+            found_iptables = 0
+            anything = netaddr.IPNetwork("0.0.0.0/0")
+            for host in iptables:
+                for rule in host['rules']:
+                    block = rule['ip']
+                    if block and type(block) is netaddr.IPNetwork:
+                        if (me in block or block in me or me == block ) and (block != anything and me != anything):
+                            rule['hostname'] = host['hostname']
+                            rule['ip'] = str(rule['ip']) # stringify
+                            rule['msg'] = msg
+                            found['iptables'].append(rule)
+                            found_iptables += 1
+                        if found_iptables == 10:
+                            break
+                if found_iptables == 10:
+                    break
+
+        yield json.dumps({"results": found}, indent = 2)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/myrules.py b/server/api/pages/myrules.py
new file mode 100644
index 0000000..ee2b9b1
--- /dev/null
+++ b/server/api/pages/myrules.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/myrules
+########################################################################
+# put:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/IPTables'
+#     description: iptables ruleset blob
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: iptables entries updatd
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Set a host's iptables list
+# 
+########################################################################
+
+
+
+
+
+"""
+This is the iptables list handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+
+# Cached vars
+BANLIST = []
+BAN_TS = 0
+BAN_CACHE_TIME = 30
+
+def run(API, environ, indata, session):
+    global BANLIST, BAN_TS
+    method = environ['REQUEST_METHOD']
+    
+    # Adding a new entry?
+    if method == "PUT":
+        rules = indata.get('iptables')
+        hostname = indata.get('hostname')
+        if hostname and type(rules) is list: #just in case!
+            print("Got %u rules from %s" % (len(rules), hostname))
+            iid = plugins.worker.make_sha1(hostname)
+            ipdoc = {
+                'hostname': hostname,
+                'updated': int(time.time()),
+                'iptables': rules,
+            }
+            session.DB.ES.index(index=session.DB.dbname, doc_type = 'iptables', id = iid, body = ipdoc)
+        yield json.dumps({"message": "Iptables updated"})
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/rules.py b/server/api/pages/rules.py
new file mode 100644
index 0000000..1e129ed
--- /dev/null
+++ b/server/api/pages/rules.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/rules
+########################################################################
+# delete:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/Ruleset'
+#     description: Removes a rule entry
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Removal successful
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Remove a rule entry
+# get:
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Empty'
+#       description: 200 response
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Displays the current ruleset entries
+# put:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/Ruleset'
+#     description: Ruleset to add/modify
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Ruleset added
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Adds or overrides a ruleset
+# 
+########################################################################
+
+
+
+
+
+"""
+This is the ruleset handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+import uuid
+
+def run(API, environ, indata, session):
+    global WHITE_CACHE, WHITE_TS
+    method = environ['REQUEST_METHOD']
+    
+    # Adding a new entry?
+    if method == "PUT":
+        rid = indata.get('rid')
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        name = indata.get('name')
+        rtype = indata.get('type')
+        span = indata.get('span')
+        limit = indata.get('limit')
+        query = indata.get('query')
+        
+        # all good? Okay, add the entry then
+        entry = {
+            'name': name,
+            'type': rtype,
+            'query': query,
+            'span': span,
+            'limit': limit
+        }
+        if not rid:
+            rid = str(uuid.uuid4())
+            plugins.worker.addnote(session.DB, 'manual', "%s made a new ruleset %s (%s)" % (submitter, rid, name))
+        else:
+            plugins.worker.addnote(session.DB, 'manual', "%s updated ruleset %s (%s)" % (submitter, rid, name))
+        
+        session.DB.ES.index(index=session.DB.dbname, doc_type = 'rule', id = rid, body = entry, refresh = 'wait_for')
+        yield json.dumps({"message": "Ruleset added!"})
+        return
+        
+    # Delete an entry
+    if method == "DELETE":
+        rid = indata.get('rid')
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        if re.match(r"^[-a-f0-9]+$", rid):
+            if session.DB.ES.exists(index=session.DB.dbname, doc_type='rule', id = rid):
+                doc = session.DB.ES.get(index=session.DB.dbname, doc_type='rule', id = rid)['_source']
+                plugins.worker.addnote(session.DB, 'manual', "Ruleset %s (%s) removed by %s" % (rid, doc.get('name', '??'), submitter))
+                session.DB.ES.delete(index=session.DB.dbname, doc_type='rule', id = rid, refresh = 'wait_for')
+            yield json.dumps({"message": "Entry removed"})
+            return
+        yield API.exception(400, "Invalid rule ID passed!")
+        
+    # Display the current ruleset entries
+    if method == "GET":
+        rules = []
+        res = session.DB.ES.search(
+                index=session.DB.dbname,
+                doc_type="rule",
+                size = 5000,
+                body = {
+                    'query': {
+                        'match_all': {}
+                    }
+                }
+            )
+
+        for hit in res['hits']['hits']:
+            doc = hit['_source']
+            doc['rid'] = hit['_id']
+            rules.append(doc)
+        JSON_OUT = {
+            'rules': rules
+        }
+        yield json.dumps(JSON_OUT)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/search.py b/server/api/pages/search.py
new file mode 100644
index 0000000..9b0f08a
--- /dev/null
+++ b/server/api/pages/search.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/search
+########################################################################
+# post:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/SearchObject'
+#     description: Search query
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/SearchResult'
+#       description: Search result
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Search for whether an IP or block is banned or whitelisted
+# 
+########################################################################
+
+
+
+"""
+This is the search handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+import netaddr
+
+def find_rule(DB, doctype, ip):
+    """ Find a rule, either v1 or v2 style """
+    bid = plugins.worker.make_sha1(str(ip))
+    bid2 = plugins.worker.make_sha1(str(ip).replace('/32', '').replace('/128',''))
+    # Blocky/2 ban doc
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = bid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = bid)
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = bid2):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = bid2)
+    
+    # Blocky/1 ban doc
+    oid = str(ip).replace('/', '_').replace('_32', '').replace('_128', '')
+    if DB.ES.exists(index=DB.dbname, doc_type = doctype, id = oid):
+        return DB.ES.get(index=DB.dbname, doc_type = doctype, id = oid)
+
+
+def run(API, environ, indata, session):
+    method = environ['REQUEST_METHOD']
+    
+    # Searching? :)
+    if method == "POST":
+        found = {
+            'whitelist': [],
+            'banlist': [],
+            'iptables': [],
+        }
+        ip = indata['source']
+        docid = plugins.worker.make_sha1(ip)
+        
+        #get whitelist and banlist, plus iptables rules
+        whitelist = plugins.worker.get_whitelist(session.DB)
+        banlist = plugins.worker.get_banlist(session.DB)
+        iptables = plugins.worker.get_iptables(session.DB)
+        
+        me = plugins.worker.to_block(ip) # queried IP as IPNetwork object
+        
+        # Find all whitelist entries that touch on this
+        for block in whitelist:
+            if me in block or block in me or me == block:
+                rule = find_rule(session.DB, 'whitelist', str(block))
+                if rule:
+                    doc = rule['_source']
+                    doc['rid'] = rule['_id']
+                    found['whitelist'].append(doc)
+        
+        # Find all banlist entries that touch on this
+        for block in banlist:
+            if me in block or block in me or me == block:
+                rule = find_rule(session.DB, 'ban', str(block))
+                if rule:
+                    doc = rule['_source']
+                    doc['rid'] = rule['_id']
+                    if not 'ip' in doc:
+                        doc['ip'] = doc['rid'].replace('_', '/')
+                    found['banlist'].append(doc)
+        
+        # Find any iptables rules that may have it as well (max 10)
+        found_iptables = 0
+        anything = netaddr.IPNetwork("0.0.0.0/0")
+        for host in iptables:
+            for rule in host['rules']:
+                block = rule['ip']
+                if (me in block or block in me or me == block ) and (block != anything and me != anything):
+                    rule['hostname'] = host['hostname']
+                    rule['ip'] = str(rule['ip']) # stringify
+                    found['iptables'].append(rule)
+                    found_iptables += 1
+                if found_iptables == 10:
+                    break
+            if found_iptables == 10:
+                break
+
+        yield json.dumps({"results": found}, indent = 2)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/pages/whitelist.py b/server/api/pages/whitelist.py
new file mode 100644
index 0000000..68a7f20
--- /dev/null
+++ b/server/api/pages/whitelist.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/whitelist
+########################################################################
+# delete:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/IPAddress'
+#     description: Removes a whitelist entry
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Removal successful
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Remove a whitelist entry
+# get:
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Empty'
+#       description: 200 response
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   security:
+#   - cookieAuth: []
+#   summary: Displays the current whitelist entries
+# put:
+#   requestBody:
+#     content:
+#       application/json:
+#         schema:
+#           $ref: '#/components/schemas/IPAddress'
+#     description: IP address or block to whitelist
+#     required: true
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/ActionCompleted'
+#       description: Whitelist entry added
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Add a whitelist entry
+# 
+########################################################################
+
+
+
+
+
+"""
+This is the whitelist handler for Blocky/2
+"""
+
+import json
+import re
+import time
+import bcrypt
+import hashlib
+import plugins.worker
+
+WHITE_CACHE = []
+WHITE_TS = 0
+WHITE_CACHE_TIME = 30
+
+def remove_ban(session, ban):
+    """ Remove a ban, either v1 or v2 style """
+    plugins.worker.addnote(session.DB, 'system', "Removing banlist entry for %s due to forced whitelisting" % ban)
+    bid = plugins.worker.make_sha1(str(ban))
+    # Blocky/2 ban doc
+    if session.DB.ES.exists(index=session.DB.dbname, doc_type = 'ban', id = bid):
+        session.DB.ES.delete(index=session.DB.dbname, doc_type = 'ban', id = bid, refresh = 'wait_for')
+    # Blocky/1 ban doc
+    oid = str(ban).replace('/', '_').replace('_32', '')
+    if session.DB.ES.exists(index=session.DB.dbname, doc_type = 'ban', id = oid):
+        session.DB.ES.delete(index=session.DB.dbname, doc_type = 'ban', id = oid, refresh = 'wait_for')
+
+def run(API, environ, indata, session):
+    global WHITE_CACHE, WHITE_TS
+    method = environ['REQUEST_METHOD']
+    
+    # Adding a new entry?
+    if method == "PUT":
+        ip = indata['source']
+        reason = indata['reason']
+        target = indata.get('target', '*')
+        timeout = indata.get('timeout', 0)
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        force = indata.get('force', False)
+        reason = "Whitelisted by %s: %s" % (submitter, reason)
+        
+        # Check if this IP is within a banned space
+        block = plugins.worker.to_block(ip)
+        banlist = plugins.worker.get_banlist(session.DB)
+        for ban in banlist:
+            if block in ban:
+                if force:
+                    remove_ban(session, str(ban))
+                else:
+                    raise API.exception(403, "IP Address is currently banned as %s, please remove first or use force push!" % ban)
+            if ban in block:
+                if force:
+                    remove_ban(session, str(ban))
+                else:
+                    raise API.exception(403, "This whitelist would cancel ban entry for %s, cannot mix (use force push?)" % ban)
+        
+        # all good? Okay, add the entry then
+        entry = {
+            'ip': ip,
+            'reason': reason,
+            'target': target,
+            'epoch': int(time.time()),
+            'timeout': timeout
+        }
+        bid = plugins.worker.make_sha1(ip)
+        session.DB.ES.index(index=session.DB.dbname, doc_type = 'whitelist', id = bid, body = entry, refresh = 'wait_for')
+        plugins.worker.addnote(session.DB, 'manual', "Whitelisting %s per %s: %s" % (ip, submitter, reason))
+        yield json.dumps({"message": "Entry added!"})
+        return
+        
+    # Delete an entry
+    if method == "DELETE":
+        rid = indata.get('rule')
+        submitter = environ.get('HTTP_PROXY_USER', 'Admin')
+        if re.match(r"^[a-f0-9]+$", rid):
+            if session.DB.ES.exists(index=session.DB.dbname, doc_type='whitelist', id = rid):
+                doc = session.DB.ES.get(index=session.DB.dbname, doc_type='whitelist', id = rid)['_source']
+                plugins.worker.addnote(session.DB, 'manual', "Whitelisting rule %s for %s removed by %s" % (rid, doc.get('ip', '??'), submitter))
+                session.DB.ES.delete(index=session.DB.dbname, doc_type='whitelist', id = rid, refresh = 'wait_for')
+                
+            yield json.dumps({"message": "Entry removed"})
+            return
+        elif re.match(r"^[a-f0-9.:_]+$", rid):
+            if session.DB.ES.exists(index=session.DB.dbname, doc_type='whitelist', id = rid):
+                doc = session.DB.ES.get(index=session.DB.dbname, doc_type='whitelist', id = rid)['_source']
+                plugins.worker.addnote(session.DB, 'manual', "Whitelisting rule %s for %s removed by %s" % (rid, doc.get('ip', '??'), submitter))
+                session.DB.ES.delete(index=session.DB.dbname, doc_type='whitelist', id = rid, refresh = 'wait_for')
+            yield json.dumps({"message": "Entry removed"})
+            return
+        yield API.exception(400, "Invalid rule ID passed!")
+        
+    # Display the current whitelist entries
+    if method == "GET":
+        if WHITE_TS < (time.time() - WHITE_CACHE_TIME) or 'Mozilla' in environ.get('HTTP_USER_AGENT', 'python'):
+            res = session.DB.ES.search(
+                    index=session.DB.dbname,
+                    doc_type="whitelist",
+                    size = 5000,
+                    body = {
+                        'query': {
+                            'match_all': {}
+                        }
+                    }
+                )
+        
+            WHITE_CACHE = []
+            WHITE_TS = time.time()
+            for hit in res['hits']['hits']:
+                doc = hit['_source']
+                doc['rid'] = hit['_id']
+                doc['ip'] = doc['ip'].strip() # backwards compat fix
+                WHITE_CACHE.append(doc)
+            
+        JSON_OUT = {
+            'whitelist': WHITE_CACHE
+        }
+        yield json.dumps(JSON_OUT)
+        return
+    
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    
\ No newline at end of file
diff --git a/server/api/plugins/database.py b/server/api/plugins/database.py
new file mode 100644
index 0000000..f67c0b8
--- /dev/null
+++ b/server/api/plugins/database.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This is the ES library for Blocky.
+It stores the elasticsearch handler and config options.
+"""
+
+
+# Main imports
+import cgi
+import re
+import urllib3
+urllib3.disable_warnings()
+import elasticsearch
+
+class BlockyESWrapper(object):
+    """
+       Class for rewriting old-style queries to the new ones,
+       where doc_type is an integral part of the DB name
+    """
+    def __init__(self, ES):
+        self.ES = ES
+
+    def get(self, index, doc_type, id):
+        return self.ES.get(index = index+'_'+doc_type, doc_type = '_doc', id = id)
+    def exists(self, index, doc_type, id):
+        return self.ES.exists(index = index+'_'+doc_type, doc_type = '_doc', id = id)
+    def exists_doctype(self, index, doc_type):
+        return self.ES.indices.exists(index = index+'_'+doc_type)
+    def create(self, index, doc_type):
+        return self.ES.indices.create(index = index+'_'+doc_type)
+    def delete(self, index, doc_type, id):
+        return self.ES.delete(index = index+'_'+doc_type, doc_type = '_doc', id = id)
+    def index(self, index, doc_type, id, body, refresh = False):
+        return self.ES.index(index = index+'_'+doc_type, doc_type = '_doc', id = id, refresh = refresh, body = body)
+    def update(self, index, doc_type, id, body):
+        return self.ES.update(index = index+'_'+doc_type, doc_type = '_doc', id = id, body = body)
+    def search(self, index, doc_type, size = 100, _source_include = None, body = None):
+        return self.ES.search(
+            index = index+'_'+doc_type,
+            doc_type = '_doc',
+            size = size,
+            _source_include = _source_include,
+            body = body
+            )
+    def count(self, index, doc_type, body = None):
+        return self.ES.count(
+            index = index+'_'+doc_type,
+            doc_type = '_doc',
+            body = body
+            )
+
+
+class BlockyDatabase(object):
+    def __init__(self, config):
+        self.config = config
+        self.dbname = config['elasticsearch']['dbname']
+        self.ES = elasticsearch.Elasticsearch([{
+                'host': config['elasticsearch']['host'],
+                'port': int(config['elasticsearch']['port']),
+                'use_ssl': config['elasticsearch']['ssl'],
+                'verify_certs': False,
+                'url_prefix': config['elasticsearch']['uri'] if 'uri' in config['elasticsearch'] else '',
+                'http_auth': config['elasticsearch']['auth'] if 'auth' in config['elasticsearch'] else None
+            }],
+                max_retries=5,
+                retry_on_timeout=True
+            )
+
+        # IMPORTANT BIT: Figure out if this is ES 6.x or newer.
+        # If so, we're using the new ES DB mappings, and need to adjust ALL
+        # ES calls to match this.
+        es6 = True if int(self.ES.info()['version']['number'].split('.')[0]) >= 6 else False
+        if es6:
+            self.ES = BlockyESWrapper(self.ES)
diff --git a/server/api/plugins/openapi.py b/server/api/plugins/openapi.py
new file mode 100644
index 0000000..797c690
--- /dev/null
+++ b/server/api/plugins/openapi.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This is the OpenAPI validator library.
+Validates input using the OpenAPI specification version 3 from
+https://github.com/OAI/OpenAPI-Specification (a simplified version, ahem)
+"""
+
+import yaml
+import json
+import functools
+import operator
+import re
+
+
+class BlockyHTTPError(Exception):
+    def __init__(self, code, message):
+        self.code = code
+        self.message = message
+        
+
+class OpenAPIException(Exception):
+    def __init__(self, message):
+        self.message = message
+
+# Python type names to JSON type names
+py2JSON = {
+    'int':      'integer',
+    'float':    'float',
+    'str':      'string',
+    'list':     'array',
+    'dict':     'object',
+    'bool':     'boolean'
+}
+
+mcolors = {
+    'PUT':      '#fca130',
+    'DELETE':   '#f93e3e',
+    'GET':      '#61affe',
+    'POST':     '#49cc5c',
+    'PATCH':    '#d5a37e'
+}
+        
+class OpenAPI():
+    def __init__(self, APIFile):
+        """ Instantiates an OpenAPI validator given a YAML specification"""
+        if APIFile.endswith(".json") or APIFile.endswith(".js"):
+            self.API = json.load(open(APIFile))
+        else:
+            self.API = yaml.load(open(APIFile))
+        
+    def validateType(self, field, value, ftype):
+        """ Validate a single field value against an expected type """
+        
+        # Get type of value, convert to JSON name of type.
+        pyType = type(value).__name__
+        jsonType = py2JSON[pyType] if pyType in py2JSON else pyType
+        
+        # Check if type matches
+        if ftype != jsonType:
+            raise OpenAPIException("OpenAPI mismatch: Field '%s' was expected to be %s, but was really %s!" % (field, ftype, jsonType))
+        
+    def validateSchema(self, pdef, formdata, schema = None):
+        """ Validate (sub)parameters against OpenAPI specs """
+        
+        # allOf: list of schemas to validate against
+        if 'allOf' in pdef:
+            for subdef in pdef['allOf']:
+                self.validateSchema(subdef, formdata)
+        
+        where = "JSON body"
+        # Symbolic link??
+        if 'schema' in pdef:
+            schema = pdef['schema']['$ref']
+        if '$ref' in pdef:
+            schema = pdef['$ref']
+        if schema:
+            # #/foo/bar/baz --> dict['foo']['bar']['baz']
+            pdef = functools.reduce(operator.getitem, schema.split('/')[1:], self.API)
+            where = "item matching schema %s" % schema
+        
+        # Check that all required fields are present
+        if 'required' in pdef:
+            for field in pdef['required']:
+                if not field in formdata:
+                    raise OpenAPIException("OpenAPI mismatch: Missing input field '%s' in %s!" % (field, where))
+        
+        # Now check for valid format of input data
+        for field in formdata:
+            if 'properties' not in pdef or field not in pdef['properties'] :
+                raise OpenAPIException("Unknown input field '%s' in %s!" % (field, where))
+            if 'type' not in pdef['properties'][field]:
+                raise OpenAPIException("OpenAPI mismatch: Field '%s' was found in api.yaml, but no format was specified in specs!" % field)
+            ftype = pdef['properties'][field]['type']
+            self.validateType(field, formdata[field], ftype)
+            
+            # Validate sub-arrays
+            if ftype == 'array' and 'items' in pdef['properties'][field]:
+                for item in formdata[field]:
+                    if '$ref' in pdef['properties'][field]['items']:
+                        self.validateSchema(pdef['properties'][field]['items'], item)
+                    else:
+                        self.validateType(field, formdata[field], pdef['properties'][field]['items']['type'])
+            
+            # Validate sub-hashes
+            if ftype == 'hash' and 'schema' in pdef['properties'][field]:
+                self.validateSchema(pdef['properties'][field], formdata[field])
+    def validateParameters(self, defs, formdata):
+        #
+        pass
+        
+    def validate(self, method = "GET", path = "/foo", formdata = None):
+        """ Validate the request method and input data against the OpenAPI specification """
+        
+        # Make sure we're not dealing with a dynamic URL.
+        # If we find /foo/{key}, we fold that into the form data
+        # and process as if it's a json input field for now.
+        if not self.API['paths'].get(path):
+            for xpath in self.API['paths']:
+                pathRE = re.sub(r"\{(.+?)\}", r"(?P<\1>[^/]+)", xpath)
+                m = re.match(pathRE, path)
+                if m:
+                    for k,v  in m.groupdict().items():
+                        formdata[k] = v
+                    path = xpath
+                    break
+                
+        if self.API['paths'].get(path):
+            defs = self.API['paths'].get(path)
+            method = method.lower()
+            if method in defs:
+                mdefs = defs[method]
+                if formdata and 'parameters' in mdefs:
+                    self.validateParameters(mdefs['parameters'], formdata)
+                elif formdata and 'requestBody' not in mdefs:
+                    raise OpenAPIException("OpenAPI mismatch: JSON data is now allowed for this request type")
+                elif formdata and 'requestBody' in mdefs and 'content' in mdefs['requestBody']:
+                    
+                    # SHORTCUT: We only care about JSON input for Kibble! Disregard other types
+                    if not 'application/json' in mdefs['requestBody']['content']:
+                        raise OpenAPIException ("OpenAPI mismatch: API endpoint accepts input, but no application/json definitions found in api.yaml!")
+                    jdefs = mdefs['requestBody']['content']['application/json']
+                    
+                    # Check that required params are here
+                    self.validateSchema(jdefs, formdata)
+                
+            else:
+                raise OpenAPIException ("OpenAPI mismatch: Method %s is not registered for this API" % method)
+        else:
+            raise OpenAPIException("OpenAPI mismatch: Unknown API path '%s'!" % path)
+
+    def dumpExamples(self, pdef, array = False):
+        schema = None
+        if 'schema' in pdef:
+            if 'type' in pdef['schema'] and pdef['schema']['type'] == 'array':
+                array = True
+                schema = pdef['schema']['items']['$ref']
+            else:
+                schema = pdef['schema']['$ref']
+        if '$ref' in pdef:
+            schema = pdef['$ref']
+        if schema:
+            # #/foo/bar/baz --> dict['foo']['bar']['baz']
+            pdef = functools.reduce(operator.getitem, schema.split('/')[1:], self.API)
+        js = {}
+        desc = {}
+        if 'properties' in pdef:
+            for k, v in pdef['properties'].items():
+                if 'description' in v:
+                    desc[k] = [v['type'], v['description']]
+                if 'example' in v:
+                    js[k] = v['example']
+                elif 'items' in v:
+                    if v['type'] == 'array':
+                        js[k], foo = self.dumpExamples(v['items'], True)
+                    else:
+                        js[k], foo = self.dumpExamples(v['items'])
+        return [js if not array else [js], desc]
+        
+    def toHTML(self):
+        """ Blurps out the specs in a pretty HTML blob """
+        print("""
+<!DOCTYPE html>
+<html lang="en">
+<head>
+</head>
+<body>
+""")
+        li = "<h3>Overview:</h3><ul style='font-size: 12px; font-family: Open Sans, sans-serif;'>"
+        for path, spec in sorted(self.API['paths'].items()):
+            for method, mspec in sorted(spec.items()):
+                method = method.upper()
+                summary = mspec.get('summary', 'No summary available')
+                linkname = "%s%s" % (method.lower(), path.replace('/', '-'))
+                li += "<li><a href='#%s'>%s %s</a>: %s</li>\n" % (linkname, method, path, summary)
+        li += "</ul>"
+        print(li)
+        for path, spec in sorted(self.API['paths'].items()):
+            for method, mspec in sorted(spec.items()):
+                method = method.upper()
+                summary = mspec.get('summary', 'No summary available')
+                resp = ""
+                inp = ""
+                inpvars = ""
+                linkname = "%s%s" % (method.lower(), path.replace('/', '-'))
+                if 'responses' in mspec:
+                    for code, cresp in sorted(mspec['responses'].items()):
+                        for ctype, pdef in cresp['content'].items():
+                            xjs, desc = self.dumpExamples(pdef)
+                            js = json.dumps(xjs, indent = 4)
+                            resp += "<div style='float: left; width: 90%%;'><pre style='width: 600px;'><b>%s</b>:\n%s</pre>\n</div>\n" % (code, js)
+                
+                if 'requestBody' in mspec:
+                    for ctype, pdef in mspec['requestBody']['content'].items():
+                        xjs, desc = self.dumpExamples(pdef)
+                        if desc:
+                            for k, v in desc.items():
+                                inpvars += "<kbd><b>%s:</b></kbd> (%s) <span style='font-size: 12px; font-family: Open Sans, sans-serif;'>%s</span><br/>\n" % (k, v[0], v[1])
+                        js = json.dumps(xjs, indent = 4)
+                        inp += "<div style='float: left; width: 90%%;'><h4>Input examples:</h4><blockquote><pre style='width: 600px;'><b>%s</b>:\n%s</pre></blockquote>\n</div>" % (ctype, js)
+                    
+                if inpvars:
+                    inpvars = "<div style='float: left; width: 90%%;'><blockquote><pre style='width: 600px;'>%s</pre>\n</blockquote></div>" % inpvars
+
+                
+                print("""
+                      <div id="%s" style="margin: 20px; display: flex; box-sizing: border-box; width: 900px; border-radius: 6px; border: 1px solid %s; font-family: sans-serif; background: %s30;">
+                        <div style="min-height: 32px;">
+                          <!-- method -->
+                          
+                          <div style="float: left; align-items: center; margin: 4px; border-radius: 5px; text-align: center; padding-top: 4px; height: 20px; width: 100px; color: #FFF; font-weight: bold; background: %s;">%s</div>
+                          
+                          <!-- path and summary -->
+                          <span style="display: flex; padding-top: 6px;"><kbd><strong>%s</strong></kbd></span>
+                          <div style="box-sizing: border-box; flex: 1; font-size: 13px; font-family: Open Sans, sans-serif; float: left; padding-top: 6px; margin-left: 20px;">
+                          %s</div>
+                          <div style="float: left; width: 90%%;display: %s; ">
+                            <h4>JSON parameters:</h4>
+                            %s
+                            <br/>
+                            %s
+                          </div>
+                          <div style="float: left; width: 90%%; ">
+                            <h4>Response examples:</h4>
+                            <blockquote>%s</blockquote>
+                          </div>
+                        </div>
+                      </div>
+                      """ % (linkname, mcolors[method], mcolors[method], mcolors[method], method, path, summary, "block" if inp else "none", inpvars, inp, resp))
+                #print("%s %s: %s" % (method.upper(), path, mspec['summary']))
+        print("</body></html>")
\ No newline at end of file
diff --git a/server/api/plugins/session.py b/server/api/plugins/session.py
new file mode 100644
index 0000000..19f4c17
--- /dev/null
+++ b/server/api/plugins/session.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This is the session library for Blocky.
+It handles setting/getting cookies and user prefs
+"""
+
+
+# Main imports
+import cgi
+import re
+import sys
+import traceback
+import http.cookies
+import uuid
+import elasticsearch
+import time
+
+account_lookup_cache = {}
+
+NO_SESSIONS = True
+
+class BlockySession(object):
+
+    def logout(self):
+        """Log out user and wipe cookie"""
+        if self.user and self.cookie:
+            cookies = http.cookies.SimpleCookie()
+            cookies['blocky_session'] = "null"
+            self.headers.append(('Set-Cookie', cookies['blocky_session'].OutputString()))
+            try:
+                self.DB.ES.delete(index=self.DB.dbname, doc_type='uisession', id = self.cookie)
+                self.cookie = None
+                self.user = None
+            except:
+                pass
+    def newCookie(self):
+        cookie = uuid.uuid4()
+        self.sdoc = {
+            'timestamp': int(time.time())
+        }
+        if not NO_SESSIONS:
+            self.DB.ES.index(index=self.DB.dbname, doc_type='uisession', id = cookie, body = self.sdoc);
+        cookies = http.cookies.SimpleCookie()
+        cookies['blocky_session'] = cookie
+        cookies['blocky_session']['path'] = '/'
+        cookies['blocky_session']['secure'] = '1'
+        cookies['blocky_session']['expires'] = 86400 * 365 # Expire one year from now
+        self.headers.append(('Set-Cookie', cookies['blocky_session'].OutputString()))
+        #print("Making new cookie %s!" % cookie)
+        return cookie
+
+    def __init__(self, DB, environ, config):
+        """
+        Loads the current user session or initiates a new session if
+        none was found.
+        """
+        self.config = config
+        self.user = None
+        self.DB = DB
+        self.ip = environ.get('REMOTE_ADDR', '?.?.?.?')
+        if environ.get('HTTP_X_FORWARDED_FOR'):
+            self.ip = environ.get('HTTP_X_FORWARDED_FOR')
+        self.headers = [('Content-Type', 'application/json')]
+        self.cookie = None
+        self.environ = environ
+
+        # Construct the URL we're visiting
+        self.url = "https://asf.jamhosted.net"
+        self.url += environ.get('SCRIPT_NAME', '/')
+        self.sdoc = None
+
+        # Get Blocky cookie
+        cookie = None
+        cookies = None
+        if 'HTTP_COOKIE' in environ:
+            cookies = http.cookies.SimpleCookie(environ['HTTP_COOKIE'])
+        if cookies and 'blocky_session' in cookies:
+            cookie = cookies['blocky_session'].value
+            if not NO_SESSIONS:
+                try:
+                    if re.match(r"^[-a-f0-9]+$", cookie): # Validate cookie, must follow UUID4 specs
+                        doc = None
+                        sdoc = self.DB.ES.get(index=self.DB.dbname, doc_type='uisession', id = cookie)
+                        if sdoc:
+                            self.sdoc = sdoc['_source']
+                        if sdoc and 'cid' in sdoc['_source']:
+                            uid = sdoc['_source']['cid']
+                            if uid in account_lookup_cache:
+                                doc = account_lookup_cache[uid]
+                            else:
+                                doc = self.DB.ES.get(index=self.DB.dbname, doc_type='useraccount', id = uid)
+                                account_lookup_cache[uid] = doc
+                        if sdoc and '_source' in sdoc and sdoc['_source'].get('timestamp'):
+                            # Make sure this cookie has been used in the past 7 days, else nullify it.
+                            # Further more, run an update of the session if >1 hour ago since last update.
+                            age = time.time() - sdoc['_source']['timestamp']
+                            if age > (7*86400):
+                                self.DB.ES.delete(index=self.DB.dbname, doc_type='uisession', id = cookie)
+                                sdoc['_source'] = None # Wipe it!
+                                doc = None
+                            elif age > 3600:
+                                sdoc['_source']['timestamp'] = int(time.time()) # Update timestamp in session DB
+                                self.DB.ES.update(index=self.DB.dbname, doc_type='uisession', id = cookie, body = {'doc':sdoc['_source']})
+                        if doc:
+                            self.user = doc['_source']
+                        if not sdoc:
+                            cookie = None
+                    else:
+                        cookie = None
+                except Exception as err:
+                    print(err)
+        if not cookie:
+            cookie = self.newCookie()
+        self.cookie = cookie
diff --git a/server/api/plugins/worker.py b/server/api/plugins/worker.py
new file mode 100644
index 0000000..1c8fa80
--- /dev/null
+++ b/server/api/plugins/worker.py
@@ -0,0 +1,356 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+""" This is the background worker, that scans for abusive behavior and
+    logs it as new bans, according to the rule-sets. """
+
+
+import re
+import netaddr
+import json
+import time
+import datetime
+import plugins.database
+import plugins.openapi
+import os
+import hashlib
+import socket
+
+DEBUG = False # If True, no DB writes/deletes will be done
+
+def make_sha1(t, encoding = 'utf-8'):
+    return hashlib.sha1(t.encode(encoding)).hexdigest()
+
+def to_block(ipaddress):
+    """ Converts an IP address or CIDR block to an IPNetwork object """
+    block = None
+    try:
+        if '/' in ipaddress:
+            block = netaddr.IPNetwork(ipaddress)
+        else:
+            if ':' in ipaddress: # IPv6?
+                block = netaddr.IPNetwork("%s/128" % ipaddress)
+            else: # IPv4?
+                block = netaddr.IPNetwork("%s/32" % ipaddress)
+    except netaddr.AddrFormatError:
+        raise plugins.openapi.BlockyHTTPError(400, "Invalid IP address or network!")
+    return block
+
+def addnote(DB, ntype = 'automatic', what = "No message supplied"):
+    doc = {
+        'epoch': int(time.time()),
+        'ntype': ntype,
+        'text': what,
+    }
+    DB.ES.index(index=DB.dbname, doc_type = 'note', body = doc)
+
+def get_whitelist(DB):
+    """ Get the entire whitelist """
+    whitelist = []
+    res = DB.ES.search(
+            index=DB.dbname,
+            doc_type="whitelist",
+            size = 5000,
+            body = {
+                'query': {
+                    'match_all': {}
+                }
+            }
+        )
+    for hit in res['hits']['hits']:
+        doc = hit['_source']
+        ipaddress = doc.get('ip')
+        if ipaddress:
+            ipaddress = ipaddress.strip() # blocky/1 bug
+            timeout = doc.get('timeout')
+            # If there is a timeout in a whitelist object, check if we need to remove it
+            if timeout and timeout > 0:
+                if timeout < time.time():
+                    addnote(DB, 'cleanup', "Removing whitelist rule %s for %s (timed out)" % (hit['_id'], ipaddress))
+                    DB.ES.delete(index=DB.dbname, doc_type = 'whitelist', id = hit['_id'])
+            # convert to IPNetwork object
+            block = to_block(ipaddress)
+            if block:
+                whitelist.append(block)
+    return whitelist
+
+
+def get_banlist(DB):
+    """ Get the entire banlist """
+    banlist = []
+    res = DB.ES.search(
+            index=DB.dbname,
+            doc_type="ban",
+            size = 10000,
+            body = {
+                'query': {
+                    'match_all': {}
+                }
+            }
+        )
+    for hit in res['hits']['hits']:
+        doc = hit['_source']
+        ipaddress = doc.get('ip')
+        if not ipaddress:
+            ipaddress = hit['_id'].replace('_', '/') # Blocky/1 syntax, bah
+        if ipaddress:
+            ipaddress = ipaddress.strip() # blocky/1 bug
+            block = to_block(ipaddress)
+            if block:
+                banlist.append(block)
+    return banlist
+
+def get_iptables(DB):
+    """ Get the entire list of host iptable sets """
+    rulelist = []
+    res = DB.ES.search(
+            index=DB.dbname,
+            doc_type="iptables",
+            size = 5000,
+            body = {
+                'query': {
+                    'match_all': {}
+                }
+            }
+        )
+    for hit in res['hits']['hits']:
+        doc = hit['_source']
+        hostname = doc.get('hostname', 'unknown')
+        rules = doc.get('iptables', [])
+        for rule in rules:
+            rule['ip'] = to_block(rule['source'])
+        entry = {
+            'hostname': hostname,
+            'rules': rules,
+        }
+        rulelist.append(entry)
+    return rulelist
+
+
+def construct_query(doctype, query, initial_terms = []):
+    """ Construct an ES query based on doctype and ruleset """
+    terms = initial_terms
+    nterms = []
+    for term in query:
+        numm = re.match(r"(\S+)=([0-9.]+)", term)
+        strm = re.match(r"(\S+)=\"(.+)\"", term)
+        nstrm = re.match(r"(\S+)\!=\"(.+)\"", term)
+        if numm:
+            terms.append({"term": {numm.group(1): int(numm.group(2))}})
+        elif nstrm:
+            if '*' in nstrm.group(2):
+                nterms.append({"match": {nstrm.group(1): nstrm.group(2)}})
+            else:
+                nterms.append({"term": {nstrm.group(1): nstrm.group(2)}})
+        elif strm:
+            if '*' in strm.group(2):
+                terms.append({"match": {strm.group(1): strm.group(2)}})
+            else:
+                terms.append({"term": {strm.group(1): strm.group(2)}})
+
+    # Now construct the final query
+    q = None
+    if doctype == 'httpd_visits':
+        q = {
+            "aggregations": {
+                "byip": {
+                    "filter": {
+                        "bool": {
+                            "must": terms,
+                            "must_not": nterms
+                        }
+                    },
+                    "aggs": {
+                        "clients": {
+                            "terms": {
+                                "field": "clientip.keyword",
+                                "size": 50,
+                                "order": {
+                                    "_count": "desc"
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "size": 0
+        }
+    elif doctype == 'httpd_traffic':
+        q = {
+            "aggregations": {
+                "byip": {
+                    "filter": {
+                        "bool": {
+                            "must": terms,
+                            "must_not": nterms,
+                        }
+                    },
+                    "aggs": {
+                        "clients": {
+                            "terms": {
+                                "field": "clientip.keyword",
+                                "size": 50,
+                                "order": {
+                                    "traffic": "desc"
+                                }
+                            },
+                            "aggs": {
+                                "traffic": {
+                                    "sum": {
+                                        "field": "bytes"
+                                    }
+                                }
+                            }
+                        }
+                    } 
+                }
+            },
+            "size": 0
+            }
+    return q
+
+
+
+def start(DB, config, pidfile):
+    addnote(DB, 'system', "(re)starting Blocky/2...")
+    time.sleep(15)
+    # First, check that indices are present
+    # We're assuming ES6 here...
+    if type(DB.ES) is plugins.database.BlockyESWrapper:
+        for el in ['whitelist', 'banlist', 'rules', 'notes']:
+            if not DB.ES.exists_doctype(index=DB.dbname, doc_type=el):
+                print("DB index %s does not exist, creating.." % el)
+                DB.ES.create(index=DB.dbname, doc_type = el)
+
+    # Okay, now run the rule-set every once in a while
+    while os.path.exists(pidfile):
+        print("Running rulesets against database...")
+        now = time.time()
+        # First, fetch all rules
+        rules = []
+        res = DB.ES.search(
+                index=DB.dbname,
+                doc_type="rule",
+                size = 5000,
+                body = {
+                    'query': {
+                        'match_all': {}
+                    }
+                }
+            )
+        for hit in res['hits']['hits']:
+            doc = hit['_source']
+            doc['rid'] = hit['_id']
+            rules.append(doc)
+        
+        # Prep list of bad IPs to block
+        bad_ips = []
+        
+        # Prep list of indices to check against, for performance reasons
+        d = datetime.datetime.utcnow()
+        t = []
+        for i in range(0,3):
+            t.append(d.strftime("loggy-%Y.%m.%d"))
+            d -= datetime.timedelta(days = 1)
+        threes = ",".join(t) # Past three days
+        
+        
+        # Now, run each rule
+        for rule in rules:
+            limit = rule.get('limit')
+            span = rule.get('span')
+            doctype = rule.get('type')
+            name = rule.get('name', 'Generic rule')
+            query = rule.get('query')
+            rid = rule.get('rid', 'null')
+            if limit and span and query:
+                print("Running rule '%s'..." % name)
+
+                
+                # Start with timestamp terms
+                terms = []
+                terms.append({
+                    "range": {
+                        "@timestamp": {
+                            "gt": ("now-%uh" % span)
+                        }
+                    }
+                })
+                
+                # For each term in our blocky query, convert to ES terms
+                q = construct_query(doctype, query, terms)
+                # If valid query, run it and find bad IPs
+                if q:
+                    res = DB.ES.search(index=threes, request_timeout=90, body=q)
+                    if res and 'aggregations' in res:
+                        if doctype == 'httpd_visits':
+                            for suspect in res['aggregations']['byip']['clients']['buckets']:
+                                c = suspect['doc_count']
+                                i = suspect['key']
+                                if c > limit:
+                                    r = "%s (%u >= limit of %u)" % (name, c, limit)
+                                    bad_ips.append({'ip': i, 'reason': r, 'target': '*', 'rid': rid, 'epoch': int(time.time())})
+                                    print("Found offender: %s; %s" % (i, r))
+                        
+                        elif doctype == 'httpd_traffic':
+                            for suspect in res['aggregations']['byip']['clients']['buckets']:
+                                c = suspect['traffic']['value']
+                                i = suspect['key']
+                                if c > limit:
+                                    r = "%s (%u >= limit of %u)" % (name, c, limit)
+                                    bad_ips.append({'ip': i, 'reason': r, 'target': '*', 'rid': rid, 'epoch': int(time.time())})
+                                    print("Found offender: %s; %s" % (i, r))
+        
+        print("Done with rules after %u seconds, found %u offenders" % (time.time() - now, len(bad_ips)))
+        # Now we have a list of bad IPs.
+        # Compare against whitelist, filter out any that are there
+        # Block the rest
+        
+        # Fetch whitelist
+        whitelist = get_whitelist(DB)
+
+        # For each baddie, compare against whitelist
+        to_ban = []
+        for bad_ip in bad_ips:
+            ipaddress = netaddr.IPAddress(bad_ip['ip'])
+            whitelisted = False
+            for whiteblock in whitelist:
+                if ipaddress in whiteblock:
+                    print("%s is whitelisted as %s, ignoring offenses..." % (ipaddress, whiteblock))
+                    whitelisted = True
+                    break
+            if not whitelisted:
+                to_ban.append(bad_ip)
+        
+        # For each IP we should be banning, ban if not already banned
+        for bad_ip in to_ban:
+            banid = make_sha1(bad_ip['ip'])
+            if not DB.ES.exists(index=DB.dbname, doc_type = 'ban', id = banid):
+                rdns = bad_ip['ip']
+                try:
+                    rdns = socket.gethostbyaddr(bad_ip['ip'])[0]
+                except:
+                    pass # don't care, at all
+                bad_ip['dns'] = rdns if rdns else bad_ip['ip']
+                print("Banning %s as %s" % (bad_ip['ip'], banid))
+                if not DEBUG:
+                    addnote(DB, 'autoban', "Banning %s on %s as %s (%s)" % (bad_ip['ip'], bad_ip['target'], banid, bad_ip['reason']))
+                    DB.ES.index(index=DB.dbname, doc_type = 'ban', id = banid, body = bad_ip)
+        
+        # Now sleep for a minute
+        time.sleep(120)
+        
diff --git a/server/api/yaml/blocky.yaml b/server/api/yaml/blocky.yaml
new file mode 100644
index 0000000..0952abe
--- /dev/null
+++ b/server/api/yaml/blocky.yaml
@@ -0,0 +1,12 @@
+accounts:
+  allowSignup: true
+  verify: false
+elasticsearch:
+  dbname: blocky
+  host: localhost
+  port: 9200
+  ssl: false
+mail:
+  mailhost: localhost
+  mailport: '25'
+  sender: Blocky Server <blocky@apache.org>
diff --git a/server/api/yaml/openapi.yaml b/server/api/yaml/openapi.yaml
new file mode 100644
index 0000000..aad7dbd
--- /dev/null
+++ b/server/api/yaml/openapi.yaml
@@ -0,0 +1,436 @@
+
+# THIS IS PULLED FROM SCRIPTS AND AUTOGENERATED!
+# Please use openapi/combine.py to regenerate!
+openapi: 3.0.0
+info:
+  version: 1.0.0
+  description: This is the API specifications for interacting with the Blocky/2 UI.
+  title: Blocky/2
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+components:
+  schemas:
+    ActionCompleted:
+      properties:
+        message:
+          description: Acknowledgement message
+          example: Action completed
+          type: string
+      required:
+      - message
+    Empty:
+      properties:
+        id:
+          description: optional object ID
+          type: string
+        page:
+          description: optional page id
+          type: string
+      required: []
+    Error:
+      properties:
+        code:
+          description: HTTP Error Code
+          example: 403
+          format: int16
+          type: integer
+        reason:
+          description: Human readable error message
+          example: You need to be logged in to view this endpoint!
+          type: string
+      required:
+      - code
+      - reason
+    IPAddress:
+      properties:
+        force:
+          description: Force addition, even if it means removing conflicting rules
+            already in the DB
+          type: boolean
+        reason:
+          description: The reason for banning
+          type: string
+        rule:
+          description: the ID of an existing rule
+          type: string
+        source:
+          description: the source IP Address or Network
+          type: string
+        target:
+          description: Target machine to ban on (or * for all machines)
+          type: string
+        timeout:
+          description: If present and greater than 0, denotes the UNIX timestamp when
+            this rule stops working
+          type: integer
+    IPTables:
+      properties:
+        hostname:
+          type: string
+        iptables:
+          items:
+            $ref: '#/components/schemas/IPTablesObject'
+          type: array
+    IPTablesObject:
+      properties:
+        action:
+          description: What to do
+          example: DROP
+          type: string
+        chain:
+          description: iptables chain for this rule
+          example: INPUT
+          type: string
+        destination:
+          description: The destination IP address
+          example: 1.2.3.4
+          type: string
+        extensions:
+          description: Any extensions used for this rule
+          example: /* comment here */
+          type: string
+        linenumber:
+          description: Line-number for the rule
+          example: 26
+          type: string
+        option:
+          description: rule options (mods etc)
+          example: comment
+          type: string
+        protocol:
+          description: the protocol (tcp/udp/etc)
+          example: tcp
+          type: string
+        source:
+          description: The source IP address
+          example: 1.2.3.4
+          type: string
+      required:
+      - chain
+      - linenumber
+      - protocol
+      - source
+      - action
+      - destination
+    Ruleset:
+      properties:
+        limit:
+          description: The limit that causes a ban
+          type: integer
+        name:
+          description: The name of the rule
+          type: string
+        query:
+          description: The individual query args to look for
+          type: array
+        rid:
+          description: rule ID, if overriding a previous rule
+          type: string
+        span:
+          description: The span (in hours) within which to look for offenses
+          type: integer
+        type:
+          description: The type of rule
+          type: string
+    SearchObject:
+      properties:
+        source:
+          description: IP Address or Network to search for
+          type: string
+    SearchResult:
+      properties:
+        banlist:
+          description: Banned IP blocks found matching
+          type: array
+        iptables:
+          description: Banned IP blocks found matching in local iptables
+          type: array
+        whitelist:
+          description: Whitelisted IP blocks found matching
+          type: array
+  securitySchemes:
+    cookieAuth:
+      in: cookie
+      name: kibble_session
+      type: apiKey
+paths:
+  /api/activity:
+    get:
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Empty'
+          description: 200 response
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Displays the current activity entries
+  /api/bans:
+    delete:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/IPAddress'
+        description: Removes a banlist entry
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Removal successful
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Remove a whitelist entry
+    get:
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Empty'
+          description: 200 response
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Displays the current banlistr entries
+    put:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/IPAddress'
+        description: IP address or block to ban
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Ban entry added
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Add a banmlist entry
+  /api/fail2ban:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/SearchObject'
+        description: Search query
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SearchResult'
+          description: Search result
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Search for whether a user was blocked via fail2ban
+  /api/myrules:
+    put:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/IPTables'
+        description: iptables ruleset blob
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: iptables entries updatd
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Set a host's iptables list
+  /api/rules:
+    delete:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Ruleset'
+        description: Removes a rule entry
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Removal successful
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Remove a rule entry
+    get:
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Empty'
+          description: 200 response
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Displays the current ruleset entries
+    put:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Ruleset'
+        description: Ruleset to add/modify
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Ruleset added
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Adds or overrides a ruleset
+  /api/search:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/SearchObject'
+        description: Search query
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SearchResult'
+          description: Search result
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Search for whether an IP or block is banned or whitelisted
+  /api/whitelist:
+    delete:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/IPAddress'
+        description: Removes a whitelist entry
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Removal successful
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Remove a whitelist entry
+    get:
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Empty'
+          description: 200 response
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      security:
+      - cookieAuth: []
+      summary: Displays the current whitelist entries
+    put:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/IPAddress'
+        description: IP address or block to whitelist
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ActionCompleted'
+          description: Whitelist entry added
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Add a whitelist entry
diff --git a/server/api/yaml/openapi/combine.py b/server/api/yaml/openapi/combine.py
new file mode 100644
index 0000000..45afc40
--- /dev/null
+++ b/server/api/yaml/openapi/combine.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+
+import yaml
+import os
+import sys
+import re
+
+license = """#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+
+baseyaml = """
+# THIS IS PULLED FROM SCRIPTS AND AUTOGENERATED!
+# Please use openapi/combine.py to regenerate!
+openapi: 3.0.0
+info:
+  version: 1.0.0
+  description: This is the API specifications for interacting with the Blocky/2 UI.
+  title: Blocky/2
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+"""
+
+bpath = os.path.dirname(os.path.abspath(__file__))
+
+
+def deconstruct():
+    yml = yaml.load(open(bpath + "/../openapi.yaml"))
+    noDefs = 0
+    print("Dumping paths into pages...")
+    for endpoint, defs in yml['paths'].items():
+        noDefs += 1
+        xendpoint = endpoint.replace("/api/", "")
+        ypath = os.path.abspath("%s/../../pages/%s.py" % (bpath, xendpoint))
+        print(ypath)
+        if os.path.isfile(ypath):
+            print("Editing %s" % ypath)
+            contents = open(ypath, "r").read()
+            contents = re.sub(r"^([#\n](?!\s*\"\"\")[^\r\n]*\n?)+", "", contents, re.MULTILINE)
+            odefs = yaml.dump(defs, default_flow_style=False)
+            odefs = "\n".join(["# %s" % line for line in odefs.split("\n")])
+            with open(ypath, "w") as f:
+                f.write(license)
+                f.write("########################################################################\n")
+                f.write("# OPENAPI-URI: %s\n" % endpoint)
+                f.write("########################################################################\n")
+                f.write(odefs)
+                f.write("\n########################################################################\n")
+                f.write("\n\n")
+                f.write(contents)
+                f.close()
+        
+    print("Dumping security components...")
+    for basetype, bdefs in yml['components'].items():
+        for schema, defs in bdefs.items():
+            noDefs += 1
+            ypath = "%s/components/%s/%s.yaml" % (bpath, basetype, schema)
+            ydir = os.path.dirname(ypath)
+            if not os.path.isdir(ydir):
+                print("Making directory %s" % ydir)
+                os.makedirs(ydir, exist_ok = True)
+            with open(ypath, "w") as f:
+                f.write("########################################################################\n")
+                f.write("# %-68s #\n" % defs.get('summary', schema))
+                f.write("########################################################################\n")
+                f.write(yaml.dump(defs, default_flow_style=False))
+                f.close()
+    print("Dumped %u definitions." % noDefs)
+    
+def construct():
+    yml = {}
+    yml['paths'] = {}
+    yml['components'] = {}
+    apidir = os.path.abspath("%s/../../pages/" % bpath)
+    print("Scanning %s" % apidir)
+    for d in os.listdir(apidir):
+        cdir = os.path.abspath("%s/%s" % (apidir, d))
+        if os.path.isdir(cdir):
+            print("Scanning %s" % cdir)
+            for fname in os.listdir(cdir):
+                if fname.endswith(".py"):
+                    fpath = "%s/%s" % (cdir, fname)
+                    print("Scanning %s" % fpath)
+                    contents = open(fpath, "r").read()
+                    m = re.search(r"OPENAPI-URI: (\S+)\n##+\n([\s\S]+?)##+", contents)
+                    if m:
+                        apath = m.group(1)
+                        cyml = m.group(2)
+                        print("Weaving in API path %s" % apath)
+                        cyml = "\n".join([line[2:] for line in cyml.split("\n")])
+                        defs = yaml.load(cyml)
+                        yml['paths'][apath] = defs
+        else:
+            fname = d
+            if fname.endswith(".py"):
+                fpath = "%s/%s" % (apidir, fname)
+                print("Scanning %s" % fpath)
+                contents = open(fpath, "r").read()
+                m = re.search(r"OPENAPI-URI: (\S+)\n##+\n([\s\S]+?)##+", contents)
+                if m:
+                    apath = m.group(1)
+                    cyml = m.group(2)
+                    print("Weaving in API path %s" % apath)
+                    cyml = "\n".join([line[2:] for line in cyml.split("\n")])
+                    defs = yaml.load(cyml)
+                    yml['paths'][apath] = defs
+    apidir = os.path.abspath("%s/components" % bpath)
+    print("Scanning %s" % apidir)
+    for d in os.listdir(apidir):
+        cdir = os.path.abspath("%s/%s" % (apidir, d))
+        if os.path.isdir(cdir):
+            print("Scanning %s" % cdir)
+            for fname in os.listdir(cdir):
+                if fname.endswith(".yaml"):
+                    yml['components'][d] = yml['components'].get(d, {})
+                    fpath = "%s/%s" % (cdir, fname)
+                    print("Scanning %s" % fpath)
+                    defs = yaml.load(open(fpath))
+                    yml['components'][d][fname.replace(".yaml", "")] = defs
+    ypath = os.path.abspath("%s/../openapi.yaml" % bpath)
+    with open(ypath, "w") as f:
+        f.write(baseyaml)
+        f.write(yaml.dump(yml, default_flow_style=False))
+        f.close()
+    print("All done!")
+    
+if len(sys.argv) > 1 and sys.argv[1] == 'deconstruct':
+    deconstruct()
+else:
+    construct()
diff --git a/server/api/yaml/openapi/components/schemas/ActionCompleted.yaml b/server/api/yaml/openapi/components/schemas/ActionCompleted.yaml
new file mode 100644
index 0000000..41f4d54
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/ActionCompleted.yaml
@@ -0,0 +1,10 @@
+########################################################################
+# ActionCompleted                                                      #
+########################################################################
+properties:
+  message:
+    description: Acknowledgement message
+    example: Action completed
+    type: string
+required:
+- message
diff --git a/server/api/yaml/openapi/components/schemas/Empty.yaml b/server/api/yaml/openapi/components/schemas/Empty.yaml
new file mode 100644
index 0000000..5092518
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/Empty.yaml
@@ -0,0 +1,11 @@
+########################################################################
+# Empty                                                                #
+########################################################################
+properties:
+  id:
+    description: optional object ID
+    type: string
+  page:
+    description: optional page id
+    type: string
+required: []
diff --git a/server/api/yaml/openapi/components/schemas/Error.yaml b/server/api/yaml/openapi/components/schemas/Error.yaml
new file mode 100644
index 0000000..ed7c9d1
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/Error.yaml
@@ -0,0 +1,16 @@
+########################################################################
+# Error                                                                #
+########################################################################
+properties:
+  code:
+    description: HTTP Error Code
+    example: 403
+    format: int16
+    type: integer
+  reason:
+    description: Human readable error message
+    example: You need to be logged in to view this endpoint!
+    type: string
+required:
+- code
+- reason
diff --git a/server/api/yaml/openapi/components/schemas/IPAddress.yaml b/server/api/yaml/openapi/components/schemas/IPAddress.yaml
new file mode 100644
index 0000000..d859b28
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/IPAddress.yaml
@@ -0,0 +1,24 @@
+########################################################################
+# IPAddress                                                            #
+########################################################################
+properties:
+  source:
+    type: string
+    description: the source IP Address or Network
+  target:
+    type: string
+    description: Target machine to ban on (or * for all machines)
+  reason:
+    type: string
+    description: The reason for banning
+  timeout:
+    type: integer
+    description: If present and greater than 0, denotes the UNIX timestamp when this rule stops working
+  force:
+    type: boolean
+    description: Force addition, even if it means removing conflicting rules already in the DB
+  rule:
+    type: string
+    description: the ID of an existing rule
+
+  
diff --git a/server/api/yaml/openapi/components/schemas/IPTables.yaml b/server/api/yaml/openapi/components/schemas/IPTables.yaml
new file mode 100644
index 0000000..b260ea1
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/IPTables.yaml
@@ -0,0 +1,10 @@
+########################################################################
+# Timeseries                                                           #
+########################################################################
+properties:
+  hostname:
+    type: string
+  iptables:
+    items:
+      $ref: '#/components/schemas/IPTablesObject'
+    type: array
diff --git a/server/api/yaml/openapi/components/schemas/IPTablesObject.yaml b/server/api/yaml/openapi/components/schemas/IPTablesObject.yaml
new file mode 100644
index 0000000..49dc56b
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/IPTablesObject.yaml
@@ -0,0 +1,44 @@
+########################################################################
+# TimeseriesObject                                                     #
+########################################################################
+properties:
+  chain:
+    description: iptables chain for this rule
+    example: INPUT
+    type: string
+  linenumber:
+    description: Line-number for the rule
+    example: 26
+    type: string
+  action:
+    description: What to do
+    example: "DROP"
+    type: string
+  protocol:
+    description: the protocol (tcp/udp/etc)
+    example: tcp
+    type: string
+  option:
+    description: rule options (mods etc)
+    example: comment
+    type: string
+  source:
+    description: The source IP address
+    example: 1.2.3.4
+    type: string
+  destination:
+    description: The destination IP address
+    example: 1.2.3.4
+    type: string
+  extensions:
+    description: Any extensions used for this rule
+    example: "/* comment here */"
+    type: string
+required:
+- chain
+- linenumber
+- protocol
+- source
+- action
+- destination
+
diff --git a/server/api/yaml/openapi/components/schemas/Ruleset.yaml b/server/api/yaml/openapi/components/schemas/Ruleset.yaml
new file mode 100644
index 0000000..343e7c6
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/Ruleset.yaml
@@ -0,0 +1,23 @@
+########################################################################
+# Ruleset                                                              #
+########################################################################
+properties:
+  rid:
+    type: string
+    description: rule ID, if overriding a previous rule
+  name:
+    type: string
+    description: The name of the rule
+  type:
+    type: string
+    description: The type of rule
+  limit:
+    type: integer
+    description: The limit that causes a ban
+  span:
+    type: integer
+    description: The span (in hours) within which to look for offenses
+  query:
+    type: array
+    description: "The individual query args to look for"
+
diff --git a/server/api/yaml/openapi/components/schemas/SearchObject.yaml b/server/api/yaml/openapi/components/schemas/SearchObject.yaml
new file mode 100644
index 0000000..e301393
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/SearchObject.yaml
@@ -0,0 +1,8 @@
+########################################################################
+# SearchObject                                                         #
+########################################################################
+properties:
+  source:
+    type: string
+    description: IP Address or Network to search for
+
diff --git a/server/api/yaml/openapi/components/schemas/SearchResult.yaml b/server/api/yaml/openapi/components/schemas/SearchResult.yaml
new file mode 100644
index 0000000..f83f55f
--- /dev/null
+++ b/server/api/yaml/openapi/components/schemas/SearchResult.yaml
@@ -0,0 +1,14 @@
+########################################################################
+# SearchResult                                                         #
+########################################################################
+properties:
+  banlist:
+    type: array
+    description: Banned IP blocks found matching
+  whitelist:
+    type: array
+    description: Whitelisted IP blocks found matching
+  iptables:
+    type: array
+    description: Banned IP blocks found matching in local iptables
+    
diff --git a/server/site/activity.html b/server/site/activity.html
new file mode 100644
index 0000000..6dc441f
--- /dev/null
+++ b/server/site/activity.html
@@ -0,0 +1,37 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Activity log - Blocky/2</title>
+    <link rel="stylesheet" type="text/css" href="css/blocky.css">
+    <link rel="stylesheet" type="text/css" href="css/modal.css">
+    <link rel="stylesheet" type="text/css" href="css/pikaday.css">
+  </head>
+  <body onload="init_activity();">
+      <nav id="topmenu">
+        <img src="images/logo.svg" style="height: 54px; margin: 2px 20px; "/>
+        <ul>
+          <li><a href="./">Front page</a></li>
+          <li><a href="search.html">Search</a></li>
+          <li><a href="rules.html">Ban Rules</a></li>
+          <li><a href="whitelist.html">Whitelist</a></li>
+          <li><a href="addban.html">Add a ban</a></li>
+          <li><a href="activity.html">Activity Log</a></li>
+          <li><a href="fail2ban.html">Fail2Ban Search</a></li>
+        </ul>
+      </nav>
+      
+    <div id="wrapper">
+      <div id="activity">
+        
+      </div>
+    </div>
+    <script src="js/moment.js" type="text/javascript"></script>
+    <script src="js/timezones.js" type="text/javascript"></script>
+    <script src="js/blocky.js" type="text/javascript"></script>
+    <script src="js/pikaday.js" type="text/javascript"></script>
+   
+  </body>
+</html>
+
diff --git a/server/site/addban.html b/server/site/addban.html
new file mode 100644
index 0000000..c9aa982
--- /dev/null
+++ b/server/site/addban.html
@@ -0,0 +1,43 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Add a ban - Blocky/2</title>
+    <link rel="stylesheet" type="text/css" href="css/blocky.css">
+    <link rel="stylesheet" type="text/css" href="css/modal.css">
+    <link rel="stylesheet" type="text/css" href="css/pikaday.css">
+  </head>
+  <body>
+      <nav id="topmenu">
+        <img src="images/logo.svg" style="height: 54px; margin: 2px 20px; "/>
+        <ul>
+          <li><a href="./">Front page</a></li>
+          <li><a href="search.html">Search</a></li>
+          <li><a href="rules.html">Ban Rules</a></li>
+          <li><a href="whitelist.html">Whitelist</a></li>
+          <li><a href="addban.html">Add a ban</a></li>
+          <li><a href="activity.html">Activity Log</a></li>
+          <li><a href="fail2ban.html">Fail2Ban Search</a></li>
+        </ul>
+      </nav>
+      
+    <div id="wrapper">
+      <form onsubmit="add_banlist();return false;">
+        <h3 class="subtitle">Add a banlist entry:</h3>
+        IP/CIDR: <input type="text" id="source"/> &nbsp; <input type="checkbox" id="force"/><label for="force">Force banlisting (remove conflicting whitelistings)</label><br/>
+        Target: &nbsp; <input type="text" id="target" value="*"/> <i>Use <kbd>foo.apache.org or * for all boxes</kbd></i><br/>
+        Reason:&nbsp; <input type="text" id="reason" style="width: 320px;"/><br/>       
+        <input type="submit" value="Add ban"/>
+      </form>
+      <div id="whitelist">
+        
+      </div>
+    </div>
+    <script src="js/moment.js" type="text/javascript"></script>
+    <script src="js/timezones.js" type="text/javascript"></script>
+    <script src="js/blocky.js" type="text/javascript"></script>
+    <script src="js/pikaday.js" type="text/javascript"></script>
+  </body>
+</html>
+
diff --git a/server/site/css/blocky.css b/server/site/css/blocky.css
new file mode 100644
index 0000000..7105b76
--- /dev/null
+++ b/server/site/css/blocky.css
@@ -0,0 +1,645 @@
+html,body {
+    background-color: #CFCFCF;
+    min-height: 100%;
+    margin: 0px;
+    font-family: Selawik,Benton Sans,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Helvetica,Tahoma,Arial,sans-serif;
+    font-weight: 400;
+}
+
+#wrapper {
+    width: 1000px;
+    min-height: 500px;
+    height: 100%;
+    background-color: #FFF;
+    margin: 0 auto;
+    border: 8px solid #FFF;
+    border-radius: 4px;
+}
+
+.article {
+    width: 1000px;
+    position: relative;
+    min-height: calc(100vh - 60px); /* full screen height minus menu bar and footer */
+    background-color: #FFF;
+    margin: 16px auto;
+    font-family: Selawik,Benton Sans,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Helvetica,Tahoma,Arial,sans-serif;
+    font-weight: 400;
+    font-size: 1rem;
+
+}
+
+.form_submission_field {
+  position: 'relative';
+  width: '100%';
+  background: #90b2d1;
+  padding: 8px;
+ }
+
+ .form_submission_field b {
+   color: #222;
+ }
+
+.form_submission_info {
+  background: #eeeec6;
+  width: 600px;
+  border-radius: 4px;
+  margin: 10px auto;
+  margin-bottom: 4px;
+  padding: 4px;
+  font-size: 0.9rem;
+}
+
+.form_submission_field.consent, .form_submission_field.consent > * {
+  background: #598f03 !important;
+  color: #FFF !important;
+  text-transform: uppercase;
+}
+
+.form_submission_field.consent input {
+  transform: scale(2.5);
+}
+
+.form_hr {
+  margin: 0px;
+  background: #a4c3df;
+  color: #8888;
+}
+
+h1,h2,h3,h4,h5 {
+    font-family: sans-serif;
+    margin: 4px;
+}
+
+.article p {
+  padding-left: 10px;
+  padding-right: 10px;
+}
+p {
+    text-align: left;
+    margin: 0px;
+}
+
+.figure {
+    color: #e0cec2;
+    font-family: sans-serif;
+    margin: 0px;
+    padding: 0px;
+    text-align: center;
+    line-height: 90%;
+    font-size: 1.5rem;
+    text-transform: uppercase;
+}
+
+.minty {
+    background-color: #283F51;
+    border: none;
+    border-radius: 5px;
+    border: 1.25px solid #CCC;
+    height: 32px;
+    line-height: 30px;
+    color: #FFF;
+    margin: 8px;
+    text-transform: uppercase;
+}
+
+
+.minty_small {
+    background-color: #283F51;
+    border: none;
+    border-radius: 4px;
+    border: 1.25px solid #CCC;
+    height: 22px;
+    line-height: 20px;
+    color: #FFF;
+    margin: 2px;
+    text-transform: uppercase;
+    font-size: 75%;
+}
+
+.minty_large {
+    background-color: #283F51;
+    border: none;
+    border-radius: 4px;
+    border: 1.25px solid #CCC;
+    height: 48px;
+    line-height: 40px;
+    color: #FFF;
+    margin: 4px;
+    text-transform: uppercase;
+    font-size: 175%;
+}
+
+.minty:hover, .minty_small:hover {
+    background: #234966;
+    cursor: pointer;
+}
+
+
+
+.minty.red {
+  background-color: #4f2727;
+}
+
+.minty.red:hover {
+  background-color: #662323;
+}
+
+.minty.green {
+  background-color: #264c34;
+}
+
+.minty.green:hover {
+  background-color: #23662f;
+}
+
+
+.figure.big {
+    font-size: 2rem;
+}
+
+.figure.small {
+    font-size: 0.75rem;
+}
+
+#contents {
+    padding-left: 12px;
+    padding-right: 12px;
+    float: left;
+    width: calc(100% - 24px);
+}
+
+.splash {
+    background-image: url(../images/splash_bg.png);
+    background-opacity: 50%;
+    background: #283F51;
+    background-repeat: no-repeat;
+    text-align: center;
+}
+
+
+#splash_top {
+    z-index: 1;
+    margin-top: 50px;
+    float: left;
+    position: relative;
+    width: 100%;
+    height: 360px;
+}
+
+/* top menu */
+#topmenu:not(.splashy) {
+    background-color: #271a18;
+}
+#topmenu {
+    height: 30px;
+    z-index: 1;
+    position: relative;
+    width: 100%;
+    height: 60px;
+}
+#topmenu ul {
+    list-style: none;
+    margin: 0px;
+    float: right;
+    margin-right: 100px;
+}
+
+#topmenu ul > li {
+    color: #EEE;
+    display: inline-block;
+    border-radius: 0px;
+    margin: 0px;
+    margin-right: -1px;
+    height: 24px;
+    line-height: 25px;
+    font-size: 0.7rem;
+    font-family: sans-serif;
+    text-transform: uppercase;
+    max-width: 200px;
+    overflow: hidden;
+    white-space:nowrap;
+    text-overflow: ellipsis;
+    border-top: 2px solid #0000;
+}
+
+#topmenu li.active {
+    z-index: 1;
+    height: 24px;
+    color: #FFF;
+    border-top: 2px solid #369;
+}
+
+#topmenu li:not(.active):hover {
+    border-top: 2px solid #693;
+    cursor: pointer;
+    height: 24px;
+    color: #FFF;
+}
+
+#topmenu a {
+  padding: 4px;
+  padding-left: 12px;
+  padding-right: 12px;
+}
+
+.event_title {
+    background: #283F51;
+    color: #FFF;
+    margin: 0px;
+    padding: 20px;
+    text-align: center;
+    font-weight: 400;
+    border-top: 1px solid rgba(0,0,0,0.1);
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    text-transform: uppercase;
+}
+
+
+.subtitle {
+    background: #283F51;
+    color: #FFF;
+    margin: 0px;
+    padding: 4px;
+    text-align: center;
+    font-weight: 400;
+    width: 100%;
+    border-top: 1px solid rgba(0,0,0,0.1);
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    text-transform: uppercase;
+}
+
+.event_location {
+    color: #FFF;
+    margin: 15px;
+    font-size: 1rem;
+    text-align: center;
+}
+.event_header {
+    background: #283F51;
+    position: relative;
+    min-height: 320px;
+    z-index: 2;
+}
+
+
+.footer {
+    text-align: center;
+    position: absolute;
+    right: calc(50% + 500px);
+    bottom: 0;
+    left: calc(50% - 500px);
+    width: 1000px;
+    padding-top: 6px;
+    height: 30px;
+    color: #333;
+    font-family: sans-serif;
+    font-size: 1.15rem;
+    display: block;
+    font-size: 0.9rem;
+}
+
+.footer a {
+    color: #0099ff;
+
+}
+
+
+.map_title {
+    background: #283F51;
+    color: #FFF;
+    margin: 0px;
+    margin-bottom: 20px;
+    padding: 20px;
+    text-align: center;
+    font-weight: 300;
+    border-top: 1px solid rgba(0,0,0,0.15);
+    border-bottom: 1px solid rgba(0,0,0,0.15);
+}
+
+.event_intro {
+    width: 450px;
+    color: #FFF;
+    float: right;
+    text-align: left;
+    font-size: 1.4rem;
+    padding-top: 20px;
+    font-weight: 200;
+    font-family: SelawikLight,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Helvetica,Tahoma,Arial,sans-serif;
+    font-style: italic;
+}
+
+/* Selawik font */
+@font-face {
+  font-family: 'Selawik';
+  src: url('../fonts/selawk.ttf');
+}
+@font-face {
+  font-family: 'SelawikLight';
+  src: url('../fonts/selawkl.ttf');
+}
+
+/* USER BAR STUFF */
+#userbar {
+    background: #333;
+    color: #FFF;
+    margin: 0px;
+    padding: 4px;
+    text-align: center;
+    font-weight: 400;
+    height: 24px;
+    border-top: 1px solid rgba(0,0,0,0.1);
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    width: calc(100% - 8px);
+}
+
+.gravatar.small {
+  width: 20px;
+  height: 20px;
+  vertical-align: middle;
+  margin-right: 6px;
+  border-radius: 50%;
+}
+
+#userbar .gravatar {
+  width: 24px;
+  height: 24px;
+  vertical-align: middle;
+  margin-right: 6px;
+  border-radius: 50%;
+}
+
+a {
+  color: #429edb;
+}
+
+.oauth_button {
+  cursor: pointer;
+}
+
+
+/* EDITOR BAR */
+#editorbar {
+    background: #7c3029;
+    color: #FFF;
+    margin: 0px;
+    padding: 4px;
+    text-align: center;
+    font-weight: 400;
+    height: 24px;
+    border-top: 1px solid rgba(0,0,0,0.1);
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    width: calc(100% - 8px);
+}
+
+
+.greybar {
+    background: #333;
+    color: #FFF;
+    margin: 0px;
+    padding: 4px;
+    text-align: center;
+    font-weight: 400;
+    height: 24px;
+    border-top: 1px solid rgba(0,0,0,0.1);
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    width: calc(100% - 8px);
+}
+
+.greybar.large {
+    height: 52px;
+    padding: 2px;
+    width: calc(100% - 4px);
+}
+
+.greybar.small {
+    height: 20px;
+    padding: 1px;
+    width: calc(100% - 9px);
+    font-size: 0.9rem;
+    border: 0.75px solid #3338;
+    margin: 2px;
+    padding-right: 5px;
+}
+
+.greybar.blue {
+  background: #283F51 !important;
+}
+
+.greybar.light {
+  background: #d9d9d9 !important;
+  color: #333;
+  border-radius: 8px;
+}
+
+.greybar.green {
+  background: #28513F !important;
+}
+
+
+.subpage_entry {
+  text-align: center;
+  width: 220px;
+  margin: 12px;
+  height: 120px;
+  vertical-align: top;
+  display: inline-block;
+  position: relative;
+  color: #333;
+}
+
+.subpage_entry a {
+  color: #333;
+  text-decoration: none;
+}
+
+.subpage_entry a:hover {
+  color: #369;
+}
+
+.autocomplete-suggestions {
+    text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1);
+
+    /* core styles should not be changed */
+    position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box;
+}
+.autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; }
+.autocomplete-suggestion b { font-weight: normal; color: #1f8dd6; }
+.autocomplete-suggestion.selected { background: #f0f0f0; }
+
+
+.tag {
+  border: 1.5px solid #369;
+  border-radius: 5px;
+  padding: 3px;
+  background: #FFF;
+  display: inline-block;
+  margin: 6px;
+}
+
+.tag:hover {
+  cursor: pointer;
+}
+
+.tag.green {
+  border: 1.5px solid #493;
+}
+
+
+.messaging_header {
+  background: #f7efcf;
+  position: relative;
+  font-weight: bold;
+  border-bottom: 1.5px solid #333;
+}
+
+
+.messaging_row {
+  background: #ffffdf;
+  position: relative;
+  border-bottom: 1.5px solid #333;
+}
+
+
+.large_intro {
+  font-size: 1.2rem;
+  line-height: 1.3rem;
+  padding-top: 8px;
+  font-weight: 200;
+  font-family: SelawikLight,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Helvetica,Tahoma,Arial,sans-serif;
+  font-style: italic;
+  color: #333;
+  padding: 16px;
+}
+
+#article_menu {
+  z-index: 2;
+  text-align: center;
+  width: 100%;
+  position: relative;
+  background: #333;
+  border-bottom: 4px solid #ff691f;
+  font-family: SelawikLight,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Helvetica,Tahoma,Arial,sans-serif;
+}
+
+#article_menu li {
+  list-style: none;
+  font-size: 1.15rem;
+  height: 34px;
+  line-height: 32px;
+  color: #EEE;
+  padding-right: 12px;
+  display: inline-block;
+  padding-left: 10px;
+  border-left: 0.75px solid #AAAA;
+}
+
+#article_menu li.selected {
+  background: #ff691f;
+  border: none;
+}
+
+#article_menu li:first-child {
+  border-left: none;
+}
+
+
+
+#article_menu li a {
+  color: #CCCC;
+  text-decoration: none;
+  text-transform: uppercase;
+  font-weight: 200;
+}
+
+
+#article_menu li a:hover {
+  color: #FFE;
+  text-decoration: none;
+  text-transform: uppercase;
+}
+
+
+#article_menu li.selected a {
+  color: #FFF;
+  cursor: pointer;
+  text-decoration: none;
+  text-transform: uppercase;
+  font-weight: bold;
+}
+
+.article > h1:first-of-type {
+  margin-top: -54px;
+  padding-top: 54px;
+  z-index: -1;
+  background: none;
+}
+
+.article > img {
+  max-width: 80%;
+}
+
+
+.tag.pending {
+  background: #d9eaf7;
+  color: #444;
+  font-weight: bold;
+  padding: 2px;
+  cursor: auto;
+}
+
+
+.tag.accepted {
+  background: #62b264;
+  color: #222;
+  font-weight: bold;
+  padding: 2px;
+  cursor: auto;
+}
+
+
+.tag.rejected {
+  background: #d83c3c;
+  color: #222;
+  font-weight: bold;
+  padding: 2px;
+  cursor: auto;
+}
+
+
+.tag.hold {
+  background: #bba6dd;
+  color: #222;
+  font-weight: bold;
+  padding: 2px;
+  cursor: auto;
+}
+
+.ell * {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+
+.hoverbutton:hover {
+  opacity: 75%;
+}
+
+
+table {
+ border: 1px solid #3339;
+ border-collapse: collapse;
+ width: 900px;
+ margin: 0px auto;
+ font-size: 0.9rem;
+}
+
+tr > td {
+ padding: 2px;
+ border: 1px solid #3339;
+}
+
+td > kbd {
+ font-weight: bold;
+ color: #369;
+}
\ No newline at end of file
diff --git a/server/site/css/c3.css b/server/site/css/c3.css
new file mode 100644
index 0000000..3799626
--- /dev/null
+++ b/server/site/css/c3.css
@@ -0,0 +1 @@
+.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc rect{stroke:#fff;stroke-width:1}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:1;fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #ccc}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#fff}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:#fff}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max{fill:#777}.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}.c3-chart-arc.c3-target g path{opacity:1}.c3-chart-arc.c3-target.c3-focused g path{opacity:1}
diff --git a/server/site/css/formal.css b/server/site/css/formal.css
new file mode 100644
index 0000000..599ca36
--- /dev/null
+++ b/server/site/css/formal.css
@@ -0,0 +1,184 @@
+/*
+ * Start Bootstrap - Landing Page (http://startbootstrap.com/)
+ * Copyright 2013-2016 Start Bootstrap
+ * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE)
+ */
+
+body,
+html {
+    width: 100%;
+    height: 100%;
+}
+
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+    font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif;
+    font-weight: 700;
+}
+
+.topnav {
+    font-size: 14px;
+}
+
+.lead {
+    font-size: 18px;
+    font-weight: 400;
+}
+
+.intro-header {
+    padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
+    padding-bottom: 50px;
+    text-align: center;
+    color: #f8f8f8;
+    background: url(../images/demo/landingpage.png) no-repeat center center;
+    background-size: contain;
+}
+
+.intro-message {
+    position: relative;
+    padding-top: 20%;
+    padding-bottom: 20%;
+}
+
+.intro-message > h1 {
+    margin: 0;
+    text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
+    font-size: 5em;
+    color: #000;
+}
+
+.intro-divider {
+    width: 400px;
+    border-top: 1px solid #f8f8f8;
+    border-bottom: 1px solid rgba(0,0,0,0.2);
+}
+
+.intro-message > h3 {
+    text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
+    color: #000;
+}
+
+@media(max-width:767px) {
+    .intro-message {
+        padding-bottom: 15%;
+    }
+
+    .intro-message > h1 {
+        font-size: 3em;
+    }
+
+    ul.intro-social-buttons > li {
+        display: block;
+        margin-bottom: 20px;
+        padding: 0;
+    }
+
+    ul.intro-social-buttons > li:last-child {
+        margin-bottom: 0;
+    }
+
+    .intro-divider {
+        width: 100%;
+    }
+}
+
+.network-name {
+    text-transform: uppercase;
+    font-size: 14px;
+    font-weight: 400;
+    letter-spacing: 2px;
+}
+
+.content-section-a {
+    padding: 50px 0;
+    background-color: #f8f8f8;
+}
+
+.content-section-b {
+    padding: 50px 0;
+    border-top: 1px solid #e7e7e7;
+    border-bottom: 1px solid #e7e7e7;
+}
+
+.section-heading {
+    margin-bottom: 30px;
+}
+
+.section-heading-spacer {
+    float: left;
+    width: 200px;
+    border-top: 3px solid #e7e7e7;
+}
+
+.banner {
+    padding: 100px 0;
+    color: #f8f8f8;
+    background: #99A;
+    background-size: cover;
+}
+
+.banner h2 {
+    margin: 0;
+    text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
+    font-size: 3em;
+    color: #000;
+}
+
+.banner ul {
+    margin-bottom: 0;
+}
+
+.banner-social-buttons {
+    float: right;
+    margin-top: 0;
+}
+
+@media(max-width:1199px) {
+    ul.banner-social-buttons {
+        float: left;
+        margin-top: 15px;
+    }
+}
+
+@media(max-width:767px) {
+    .banner h2 {
+        margin: 0;
+        text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
+        font-size: 3em;
+    }
+
+    ul.banner-social-buttons > li {
+        display: block;
+        margin-bottom: 20px;
+        padding: 0;
+    }
+
+    ul.banner-social-buttons > li:last-child {
+        margin-bottom: 0;
+    }
+}
+
+footer {
+    padding: 50px 0;
+    background-color: #f8f8f8;
+}
+
+p.copyright {
+    margin: 15px 0 0;
+}
+
+
+.container {
+  width: 900px;
+  padding: 16px;
+  margin: 0 auto;
+  border: 1.25px solid #444;
+  border-radius: 6px;
+  background: #EEE;
+  font-weight: 300;
+}
diff --git a/server/site/css/modal.css b/server/site/css/modal.css
new file mode 100644
index 0000000..4867044
--- /dev/null
+++ b/server/site/css/modal.css
@@ -0,0 +1,72 @@
+#modal {
+    display: none;
+    position: fixed;
+    z-index: 1;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    background-color: rgb(0,0,0);
+    background-color: rgba(0,0,0,0.5);
+}
+
+#modal_content {
+    background-color: #fefefe;
+    margin: 15% auto;
+    padding: 20px;
+    border: 1px solid #888;
+    border-radius: 4px;
+    border-top: 8px solid #c04331;
+    width: 80%;
+    max-width: 720px;
+}
+
+#modal.modal_help > #modal_content {
+    background-color: #fefefe;
+    margin: 15% auto;
+    padding: 20px;
+    border: 1px solid #888;
+    border-radius: 4px;
+    border-top: 8px solid #43c031;
+    width: 80%;
+    max-width: 720px;
+}
+
+
+#modal.modal_error > #modal_content {
+    background-color: #fefefe;
+    margin: 15% auto;
+    padding: 20px;
+    border: 1px solid #888;
+    border-radius: 4px;
+    border-top: 8px solid #c04331;
+    width: 80%;
+    max-width: 720px;
+}
+
+
+#modal_content > h2 {
+    margin-top: -10px;
+    font-size: 3rem;
+}
+
+#modal_close {
+    color: #aaa;
+    float: right;
+    font-size: 28px;
+    margin-top: -16px;
+    font-weight: bold;
+}
+
+#modal_close:hover,
+#modal_close:focus {
+    color: black;
+    text-decoration: none;
+    cursor: pointer;
+}
+
+
+#modal_content > div > p {
+    margin-top: 40px;
+}
\ No newline at end of file
diff --git a/server/site/css/pikaday.css b/server/site/css/pikaday.css
new file mode 100644
index 0000000..1759061
--- /dev/null
+++ b/server/site/css/pikaday.css
@@ -0,0 +1,239 @@
+@charset "UTF-8";
+
+/*!
+ * Pikaday
+ * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
+ */
+
+.pika-single {
+    z-index: 9999;
+    display: block;
+    position: relative;
+    color: #333;
+    background: #fff;
+    border: 1px solid #ccc;
+    border-bottom-color: #bbb;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+/*
+clear child float (pika-lendar), using the famous micro clearfix hack
+http://nicolasgallagher.com/micro-clearfix-hack/
+*/
+.pika-single:before,
+.pika-single:after {
+    content: " ";
+    display: table;
+}
+.pika-single:after { clear: both }
+.pika-single { *zoom: 1 }
+
+.pika-single.is-hidden {
+    display: none;
+}
+
+.pika-single.is-bound {
+    position: absolute;
+    box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
+}
+
+.pika-lendar {
+    float: left;
+    width: 240px;
+    margin: 8px;
+}
+
+.pika-title {
+    position: relative;
+    text-align: center;
+}
+
+.pika-label {
+    display: inline-block;
+    *display: inline;
+    position: relative;
+    z-index: 9999;
+    overflow: hidden;
+    margin: 0;
+    padding: 5px 3px;
+    font-size: 14px;
+    line-height: 20px;
+    font-weight: bold;
+    background-color: #fff;
+}
+.pika-title select {
+    cursor: pointer;
+    position: absolute;
+    z-index: 9998;
+    margin: 0;
+    left: 0;
+    top: 5px;
+    filter: alpha(opacity=0);
+    opacity: 0;
+}
+
+.pika-prev,
+.pika-next {
+    display: block;
+    cursor: pointer;
+    position: relative;
+    outline: none;
+    border: 0;
+    padding: 0;
+    width: 20px;
+    height: 30px;
+    /* hide text using text-indent trick, using width value (it's enough) */
+    text-indent: 20px;
+    white-space: nowrap;
+    overflow: hidden;
+    background-color: transparent;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: 75% 75%;
+    opacity: .5;
+    *position: absolute;
+    *top: 0;
+}
+
+.pika-prev:hover,
+.pika-next:hover {
+    opacity: 1;
+}
+
+.pika-prev,
+.is-rtl .pika-next {
+    float: left;
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
+    *left: 0;
+}
+
+.pika-next,
+.is-rtl .pika-prev {
+    float: right;
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
+    *right: 0;
+}
+
+.pika-prev.is-disabled,
+.pika-next.is-disabled {
+    cursor: default;
+    opacity: .2;
+}
+
+.pika-select {
+    display: inline-block;
+    *display: inline;
+}
+
+.pika-table {
+    width: 100%;
+    border-collapse: collapse;
+    border-spacing: 0;
+    border: 0;
+}
+
+.pika-table th,
+.pika-table td {
+    width: 14.285714285714286%;
+    padding: 0;
+}
+
+.pika-table th {
+    color: #999;
+    font-size: 12px;
+    line-height: 25px;
+    font-weight: bold;
+    text-align: center;
+}
+
+.pika-button {
+    cursor: pointer;
+    display: block;
+    box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    outline: none;
+    border: 0;
+    margin: 0;
+    width: 100%;
+    padding: 5px;
+    color: #666;
+    font-size: 12px;
+    line-height: 15px;
+    text-align: right;
+    background: #f5f5f5;
+}
+
+.pika-week {
+    font-size: 11px;
+    color: #999;
+}
+
+.is-today .pika-button {
+    color: #33aaff;
+    font-weight: bold;
+}
+
+.is-selected .pika-button,
+.has-event .pika-button {
+    color: #fff;
+    font-weight: bold;
+    background: #33aaff;
+    box-shadow: inset 0 1px 3px #178fe5;
+    border-radius: 3px;
+}
+
+.has-event .pika-button {
+    background: #005da9;
+    box-shadow: inset 0 1px 3px #0076c9;
+}
+
+.is-disabled .pika-button,
+.is-inrange .pika-button {
+    background: #D5E9F7;
+}
+
+.is-startrange .pika-button {
+    color: #fff;
+    background: #6CB31D;
+    box-shadow: none;
+    border-radius: 3px;
+}
+
+.is-endrange .pika-button {
+    color: #fff;
+    background: #33aaff;
+    box-shadow: none;
+    border-radius: 3px;
+}
+
+.is-disabled .pika-button {
+    pointer-events: none;
+    cursor: default;
+    color: #999;
+    opacity: .3;
+}
+
+.is-outside-current-month .pika-button {
+    color: #999;
+    opacity: .3;
+}
+
+.is-selection-disabled {
+    pointer-events: none;
+    cursor: default;
+}
+
+.pika-button:hover,
+.pika-row.pick-whole-week:hover .pika-button {
+    color: #fff;
+    background: #ff8000;
+    box-shadow: none;
+    border-radius: 3px;
+}
+
+/* styling for abbr */
+.pika-table abbr {
+    border-bottom: none;
+    cursor: help;
+}
+
diff --git a/server/site/fail2ban.html b/server/site/fail2ban.html
new file mode 100644
index 0000000..53be67a
--- /dev/null
+++ b/server/site/fail2ban.html
@@ -0,0 +1,40 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Fail2Ban Search - Blocky/2</title>
+    <link rel="stylesheet" type="text/css" href="css/blocky.css">
+    <link rel="stylesheet" type="text/css" href="css/modal.css">
+  </head>
+  <body onload="fail2ban(location.search.substr(1));">
+      <nav id="topmenu">
+        <img src="images/logo.svg" style="height: 54px; margin: 2px 20px; "/>
+        <ul>
+          <li><a href="./">Front page</a></li>
+          <li><a href="search.html">Search</a></li>
+          <li><a href="rules.html">Ban Rules</a></li>
+          <li><a href="whitelist.html">Whitelist</a></li>
+          <li><a href="addban.html">Add a ban</a></li>
+          <li><a href="activity.html">Activity Log</a></li>
+          <li><a href="fail2ban.html">Fail2Ban Search</a></li>
+        </ul>
+      </nav>
+      
+    <div id="wrapper">
+      <form onsubmit="fail2ban(document.getElementById('source').value); return false;">
+      User ID: <input type="text" id="source" style="width: 190px;" placeholder="Username"/>
+      <input type="submit" value="Search"/>
+      </form>
+      <div id="results">
+        <br/>
+        Here you can search for auth failures that triggered fail2ban. Just enter a username!
+        
+      </div>
+    </div>
+    <script src="js/moment.js" type="text/javascript"></script>
+    <script src="js/timezones.js" type="text/javascript"></script>
+    <script src="js/blocky.js" type="text/javascript"></script>
+  </body>
+</html>
+
diff --git a/server/site/favicon.ico b/server/site/favicon.ico
new file mode 100644
index 0000000..8554dd5
--- /dev/null
+++ b/server/site/favicon.ico
Binary files differ
diff --git a/server/site/fonts/glyphicons-halflings-regular.eot b/server/site/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..b93a495
--- /dev/null
+++ b/server/site/fonts/glyphicons-halflings-regular.eot
Binary files differ
diff --git a/server/site/fonts/glyphicons-halflings-regular.svg b/server/site/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..94fb549
--- /dev/null
+++ b/server/site/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg> 
\ No newline at end of file
diff --git a/server/site/fonts/glyphicons-halflings-regular.ttf b/server/site/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..1413fc6
--- /dev/null
+++ b/server/site/fonts/glyphicons-halflings-regular.ttf
Binary files differ
diff --git a/server/site/fonts/glyphicons-halflings-regular.woff b/server/site/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..9e61285
--- /dev/null
+++ b/server/site/fonts/glyphicons-halflings-regular.woff
Binary files differ
diff --git a/server/site/fonts/glyphicons-halflings-regular.woff2 b/server/site/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 0000000..64539b5
--- /dev/null
+++ b/server/site/fonts/glyphicons-halflings-regular.woff2
Binary files differ
diff --git a/server/site/fonts/selawk.ttf b/server/site/fonts/selawk.ttf
new file mode 100644
index 0000000..a920279
--- /dev/null
+++ b/server/site/fonts/selawk.ttf
Binary files differ
diff --git a/server/site/fonts/selawkl.ttf b/server/site/fonts/selawkl.ttf
new file mode 100644
index 0000000..cbfaaf9
--- /dev/null
+++ b/server/site/fonts/selawkl.ttf
Binary files differ
diff --git a/server/site/images/logo.svg b/server/site/images/logo.svg
new file mode 100644
index 0000000..8810515
--- /dev/null
+++ b/server/site/images/logo.svg
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="389.24017mm"
+   height="83.92263mm"
+   viewBox="0 0 389.24017 83.92263"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="logo.svg">
+  <defs
+     id="defs2">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient893">
+      <stop
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0"
+         id="stop889" />
+      <stop
+         id="stop897"
+         offset="0.159346"
+         style="stop-color:#d6e9f8;stop-opacity:1" />
+      <stop
+         style="stop-color:#bfdeea;stop-opacity:1"
+         offset="1"
+         id="stop891" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient893"
+       id="linearGradient895"
+       x1="200.51823"
+       y1="514.87665"
+       x2="200.51823"
+       y2="480.59097"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="748.81378"
+     inkscape:cy="97.603508"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="3726"
+     inkscape:window-height="2049"
+     inkscape:window-x="114"
+     inkscape:window-y="55"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-9.0871658,-71.836155)">
+    <circle
+       style="opacity:1;fill:#e6a842;fill-opacity:1;stroke:none;stroke-width:1.37587154;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path938"
+       cx="51.048481"
+       cy="113.79747"
+       r="41.961315" />
+    <path
+       style="fill:url(#linearGradient895);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.92605817px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 194.26758,475.125 v 0.0352 l -36.51953,0.65429 c 0,0 8.94165,38.51953 -19.94922,38.51953 -28.89088,0 -26.13867,13.75782 -26.13867,13.75782 0,0 -0.68886,15.82226 12.38086,15.82226 h 70.22656 2 70.22851 c 13.06972,0 12.38086,-15.82226 12.38086,-15.82226 0,0 2.75221,-13.75782 -26.13867,-13.75782 -28.89087,0 -19.94922,-38.51953 -19.94922,-38.51953 l -36.52148,-0.65429 v -0.0352 l -1,0.0176 z"
+       transform="scale(0.26458333)"
+       id="path870"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="opacity:1;fill:#000000;fill-opacity:0.16784871;stroke:none;stroke-width:1.29699993;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect848-3"
+       width="61.421131"
+       height="39.215027"
+       x="20.505209"
+       y="90.143066"
+       rx="4.99999"
+       ry="4.99999" />
+    <g
+       id="g826"
+       transform="translate(66.807292,-3.4962798)">
+      <rect
+         style="opacity:1;fill:#dee3de;fill-opacity:1;stroke:none;stroke-width:1.29699993;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect848"
+         width="61.421127"
+         height="39.215027"
+         x="-46.396576"
+         y="92.042412"
+         rx="4.99999"
+         ry="4.99999" />
+      <path
+         style="fill:#828282;fill-opacity:1;stroke-width:0.436434"
+         d="m -42.44561,128.16741 c -0.394573,-0.14285 -0.875525,-0.45676 -1.068782,-0.69772 -0.19326,-0.24096 -0.354674,-7.36992 -0.358704,-15.84215 -0.0063,-13.490396 0.08622,-15.481961 0.745665,-16.031083 0.674045,-0.561269 3.56979,-0.602513 27.620067,-0.393301 14.77689316,0.128546 27.136108,0.302671 27.464918,0.386949 0.781727,0.20036 0.880509,31.846375 0.100649,32.247735 -0.663882,0.34165 -53.586101,0.66167 -54.503818,0.32957 z"
+         id="path830-3"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:#515458;fill-opacity:1;stroke-width:0.436434"
+         d="m -42.828226,127.54932 c -0.824017,-0.68616 -0.824017,-31.015182 0,-31.70134 0.64347,-0.535815 17.891119,-0.691712 18.874782,-0.170602 0.30638,0.162308 5.498165,7.407382 11.537299,16.100152 8.2336105,11.85151 10.8061993,15.86065 10.2844604,16.02737 -0.3826831,0.12223 -9.5668234,0.22233 -20.4092004,0.22233 -14.694966,0 -19.859521,-0.12163 -20.287341,-0.47791 z"
+         id="path828-6"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       id="g826-5"
+       transform="translate(85.42262,14.221354)" />
+    <g
+       id="g951"
+       transform="translate(121.8749,4.2763124)" />
+    <g
+       id="g961"
+       transform="matrix(0.11488582,0,0,0.11488582,53.845068,89.207015)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path955"
+         d="m -32.389983,279.68152 c -0.509323,-0.0568 -2.235729,-0.2335 -3.836458,-0.39256 -9.259093,-0.9201 -19.966614,-3.54769 -29.107981,-7.14299 -14.056695,-5.52851 -26.672962,-13.70139 -37.529138,-24.31158 -20.56058,-20.0947 -32.51388,-46.32423 -34.00752,-74.62384 -0.30095,-5.70209 -0.15496,-22.49957 0.23455,-26.98749 2.7099,-31.22318 17.64994,-59.110072 42.201758,-78.773291 12.498985,-10.010269 27.482558,-17.389677 43.127082,-21.240088 22.681212,-5.582271 46.6347784,-4.03152 68.262496,4.419304 26.126465,10.208667 47.792858,30.190367 60.189814,55.509695 6.27563,12.81724 9.893955,25.82317 11.116061,39.95626 0.386095,4.46501 0.536956,21.57417 0.239096,27.11561 -1.360475,25.31041 -11.023372,48.93803 -27.91706,68.2625 -3.174783,3.6316 -9.028024,9.33876 -12.629657,12.31447 -13.063767,10.79341 -27.598456,18.27653 -43.6558959,22.47604 -4.51425978,1.18062 -9.3808493,2.13587 -14.3298561,2.81276 -3.176641,0.43448 -5.711391,0.55936 -12.7,0.62568 -4.802187,0.0456 -9.147968,0.0364 -9.657291,-0.0205 z m 16.139583,-45.3789 c 5.674264,-0.55439 12.5624024,-2.07594 17.795543,-3.93094 4.2243982,-1.49743 8.2573544,-3.31161 8.3422989,-3.75268 0.051393,-0.26687 -18.4906223,-18.92814 -50.3380589,-50.66182 -27.730681,-27.63163 -50.521553,-50.27391 -50.64638,-50.31617 -0.53139,-0.17992 -4.586592,7.27003 -6.450698,11.8508 -3.501155,8.60355 -5.865965,20.58106 -5.223865,26.45833 3.382892,30.96464 24.162557,56.89298 53.448245,66.69136 10.721159,3.58707 21.555396,4.78641 33.072915,3.66112 z m 59.627216,-37.77309 c 5.76888,-9.32608 9.537165,-20.353 10.891827,-31.87213 0.42191,-3.58765 0.352845,-4.88361 -0.566457,-10.62914 C 49.142576,125.53128 28.645722,101.39916 1.2120987,92.228664 -7.3607924,89.362925 -14.827784,88.16672 -24.055608,88.180796 c -9.936289,0.01516 -16.707233,1.080463 -25.99596,4.090061 -3.448267,1.117256 -9.180938,3.449773 -10.015124,4.074964 -0.467156,0.350118 2.987532,3.865589 50.5629096,51.452559 28.0853764,28.09221 51.1465254,51.07676 51.2469954,51.07676 0.100467,0 0.835591,-1.05553 1.633603,-2.34561 z"
+         style="fill:#e54a3a;stroke-width:0.26458332" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path953"
+         d="m -31.33165,279.27174 c -19.078603,-1.20047 -37.912072,-7.4337 -53.898352,-17.83849 -29.318188,-19.08197 -47.940108,-50.08329 -51.007108,-84.9154 -0.46344,-5.26329 -0.50054,-7.27605 -0.13412,-7.27605 0.14552,0 0.26459,0.34989 0.26459,0.77754 0,0.42764 0.30115,2.541 0.66923,4.69635 5.69059,33.32228 25.97582,62.24439 55.554721,79.20838 15.07877,8.64792 32.783141,13.73355 50.469268,14.49745 19.13166,0.82633 37.0571249,-2.79858 54.041144,-10.92825 30.178527,-14.44543 52.762433,-42.16408 60.693646,-74.49313 1.085154,-4.42329 2.409475,-11.55793 2.409475,-12.9808 0,-0.42765 0.119063,-0.77754 0.264584,-0.77754 0.92071,0 -0.618009,14.13102 -2.420067,22.225 -10.232207,45.95817 -48.437909,80.94833 -95.0419165,87.04282 -7.5627705,0.989 -14.4463125,1.22893 -21.8650945,0.76212 z m -8.73839,-103.53825 c -27.927455,-27.89953 -50.929546,-50.67818 -51.115754,-50.61922 -0.54119,0.17136 -2.167644,2.72243 -4.011581,6.29212 -3.368471,6.52104 -5.690455,13.39226 -7.058185,20.88659 -0.81534,4.46759 -1.02313,4.78422 -0.87829,1.33841 0.4416,-10.50558 2.18428,-18.65208 5.971216,-27.91354 2.048296,-5.00938 5.433534,-11.24479 6.104861,-11.24479 0.18021,0 24.603958,24.27652 54.274995,53.94782 37.37406638,37.37441 53.900314,54.07039 53.794263,54.34675 -0.136686,0.3562 -4.617196,3.05723 -5.826339,3.51236 -0.380211,0.14311 -10.86549208,-10.19719 -51.255186,-50.5465 z m 94.726527,-19.25783 c -0.0057,-0.26159 -1.366644,-7.02083 -1.691409,-8.40052 C 49.4561,133.16807 40.632984,118.37684 29.127441,108.11329 17.167402,97.4443 3.442806,90.964842 -12.413942,88.501365 c -4.40432,-0.684247 -15.160908,-0.964099 -19.512065,-0.50764 -9.838684,1.032129 -18.079457,3.234468 -26.436136,7.065028 l -2.663957,1.221113 -3.145475,-3.137278 c -1.730013,-1.725504 -3.105714,-3.256563 -3.057118,-3.402354 0.305324,-0.915971 10.917809,-6.340289 16.082319,-8.22009 5.763104,-2.097683 12.46366,-3.655989 18.756391,-4.362056 3.595402,-0.403418 12.846388,-0.412681 16.404166,-0.01643 14.5036453,1.615376 27.842518,6.860759 39.158331,15.39863 16.140205,12.177912 26.794525,29.426302 30.406805,49.225892 0.75384,4.13195 1.644922,14.77562 1.237003,14.77562 -0.08712,0 -0.159041,-0.0298 -0.159835,-0.0661 z"
+         style="fill:#bf382a;stroke-width:0.26458332" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:46.76734924px;line-height:125%;font-family:Montserrat;-inkscape-font-specification:'Montserrat Bold';letter-spacing:0px;word-spacing:0px;fill:#d7d7d7;fill-opacity:1;stroke:none;stroke-width:3.50755143px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="136.26172"
+       y="120.07915"
+       id="text996"><tspan
+         sodipodi:role="line"
+         x="136.26172"
+         y="120.07915"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:46.76734924px;line-height:125%;font-family:Montserrat;-inkscape-font-specification:Montserrat;fill:#d7d7d7;fill-opacity:1;stroke-width:3.50755143px"
+         id="tspan1014">Bl<tspan
+   style="line-height:103.32000256%;stroke-width:3.50755143px"
+   id="tspan1016">ocky/2</tspan></tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:11.93349075px;line-height:125%;font-family:Montserrat;-inkscape-font-specification:'Montserrat Bold';letter-spacing:0px;word-spacing:0px;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:0.89501178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="104.5108"
+       y="143.41994"
+       id="text1027"><tspan
+         sodipodi:role="line"
+         id="tspan1025"
+         x="104.5108"
+         y="143.41994"
+         style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Light';fill:#c8c8c8;fill-opacity:1;stroke-width:0.89501178px">Automated Global Ban System for UNIX Platforms</tspan></text>
+  </g>
+</svg>
diff --git a/server/site/images/pdf.png b/server/site/images/pdf.png
new file mode 100644
index 0000000..67d099a
--- /dev/null
+++ b/server/site/images/pdf.png
Binary files differ
diff --git a/server/site/index.html b/server/site/index.html
new file mode 100644
index 0000000..a9bdee1
--- /dev/null
+++ b/server/site/index.html
@@ -0,0 +1,35 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Blocky/2</title>
+    <link rel="stylesheet" type="text/css" href="css/blocky.css">
+    <link rel="stylesheet" type="text/css" href="css/modal.css">
+  </head>
+  <body onload="init_banlist();">
+      <nav id="topmenu">
+        <img src="images/logo.svg" style="height: 54px; margin: 2px 20px; "/>
+        <ul>
+          <li><a href="./">Front page</a></li>
+          <li><a href="search.html">Search</a></li>
+          <li><a href="rules.html">Ban Rules</a></li>
+          <li><a href="whitelist.html">Whitelist</a></li>
+          <li><a href="addban.html">Add a ban</a></li>
+          <li><a href="activity.html">Activity Log</a></li>
+          <li><a href="fail2ban.html">Fail2Ban Search</a></li>
+        </ul>
+      </nav>
+      
+    <div id="wrapper">
+      
+      <div id="banlist">
+        
+      </div>
+    </div>
+    <script src="js/moment.js" type="text/javascript"></script>
+    <script src="js/timezones.js" type="text/javascript"></script>
+    <script src="js/blocky.js" type="text/javascript"></script>
+  </body>
+</html>
+
diff --git a/server/site/js/autocomplete.js b/server/site/js/autocomplete.js
new file mode 100644
index 0000000..0b46054
--- /dev/null
+++ b/server/site/js/autocomplete.js
@@ -0,0 +1,3 @@
+// JavaScript autoComplete v1.0.4
+// https://github.com/Pixabay/JavaScript-autoComplete
+var autoComplete=function(){function e(e){function t(e,t){return e.classList?e.classList.contains(t):new RegExp("\\b"+t+"\\b").test(e.className)}function o(e,t,o){e.attachEvent?e.attachEvent("on"+t,o):e.addEventListener(t,o)}function s(e,t,o){e.detachEvent?e.detachEvent("on"+t,o):e.removeEventListener(t,o)}function n(e,s,n,l){o(l||document,s,function(o){for(var s,l=o.target||o.srcElement;l&&!(s=t(l,e));)l=l.parentElement;s&&n.call(l,o)})}if(document.querySelector){var l={selector:0,source:0,minChars:3,delay:150,offsetLeft:0,offsetTop:1,cache:1,menuClass:"",renderItem:function(e,t){t=t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");var o=new RegExp("("+t.split(" ").join("|")+")","gi");return'<div class="autocomplete-suggestion" data-val="'+e+'">'+e.replace(o,"<b>$1</b>")+"</div>"},onSelect:function(){}};for(var c in e)e.hasOwnProperty(c)&&(l[c]=e[c]);for(var a="object"==typeof l.selector?[l.selector]:document.querySelectorAll(l.selector),u=0;u<a.length;u++){var i=a[u];i.sc=document.createElement("div"),i.sc.className="autocomplete-suggestions "+l.menuClass,i.autocompleteAttr=i.getAttribute("autocomplete"),i.setAttribute("autocomplete","off"),i.cache={},i.last_val="",i.updateSC=function(e,t){var o=i.getBoundingClientRect();if(i.sc.style.left=Math.round(o.left+(window.pageXOffset||document.documentElement.scrollLeft)+l.offsetLeft)+"px",i.sc.style.top=Math.round(o.bottom+(window.pageYOffset||document.documentElement.scrollTop)+l.offsetTop)+"px",i.sc.style.width=Math.round(o.right-o.left)+"px",!e&&(i.sc.style.display="block",i.sc.maxHeight||(i.sc.maxHeight=parseInt((window.getComputedStyle?getComputedStyle(i.sc,null):i.sc.currentStyle).maxHeight)),i.sc.suggestionHeight||(i.sc.suggestionHeight=i.sc.querySelector(".autocomplete-suggestion").offsetHeight),i.sc.suggestionHeight))if(t){var s=i.sc.scrollTop,n=t.getBoundingClientRect().top-i.sc.getBoundingClientRect().top;n+i.sc.suggestionHeight-i.sc.maxHeight>0?i.sc.scrollTop=n+i.sc.suggestionHeight+s-i.sc.maxHeight:0>n&&(i.sc.scrollTop=n+s)}else i.sc.scrollTop=0},o(window,"resize",i.updateSC),document.body.appendChild(i.sc),n("autocomplete-suggestion","mouseleave",function(){var e=i.sc.querySelector(".autocomplete-suggestion.selected");e&&setTimeout(function(){e.className=e.className.replace("selected","")},20)},i.sc),n("autocomplete-suggestion","mouseover",function(){var e=i.sc.querySelector(".autocomplete-suggestion.selected");e&&(e.className=e.className.replace("selected","")),this.className+=" selected"},i.sc),n("autocomplete-suggestion","mousedown",function(e){if(t(this,"autocomplete-suggestion")){var o=this.getAttribute("data-val");i.value=o,l.onSelect(e,o,this),i.sc.style.display="none"}},i.sc),i.blurHandler=function(){try{var e=document.querySelector(".autocomplete-suggestions:hover")}catch(t){var e=0}e?i!==document.activeElement&&setTimeout(function(){i.focus()},20):(i.last_val=i.value,i.sc.style.display="none",setTimeout(function(){i.sc.style.display="none"},350))},o(i,"blur",i.blurHandler);var r=function(e){var t=i.value;if(i.cache[t]=e,e.length&&t.length>=l.minChars){for(var o="",s=0;s<e.length;s++)o+=l.renderItem(e[s],t);i.sc.innerHTML=o,i.updateSC(0)}else i.sc.style.display="none"};i.keydownHandler=function(e){var t=window.event?e.keyCode:e.which;if((40==t||38==t)&&i.sc.innerHTML){var o,s=i.sc.querySelector(".autocomplete-suggestion.selected");return s?(o=40==t?s.nextSibling:s.previousSibling,o?(s.className=s.className.replace("selected",""),o.className+=" selected",i.value=o.getAttribute("data-val")):(s.className=s.className.replace("selected",""),i.value=i.last_val,o=0)):(o=40==t?i.sc.querySelector(".autocomplete-suggestion"):i.sc.childNodes[i.sc.childNodes.length-1],o.className+=" selected",i.value=o.getAttribute("data-val")),i.updateSC(0,o),!1}if(27==t)i.value=i.last_val,i.sc.style.display="none";else if(13==t||9==t){var s=i.sc.querySelector(".autocomplete-suggestion.selected");s&&"none"!=i.sc.style.display&&(l.onSelect(e,s.getAttribute("data-val"),s),setTimeout(function(){i.sc.style.display="none"},20))}},o(i,"keydown",i.keydownHandler),i.keyupHandler=function(e){var t=window.event?e.keyCode:e.which;if(!t||(35>t||t>40)&&13!=t&&27!=t){var o=i.value;if(o.length>=l.minChars){if(o!=i.last_val){if(i.last_val=o,clearTimeout(i.timer),l.cache){if(o in i.cache)return void r(i.cache[o]);for(var s=1;s<o.length-l.minChars;s++){var n=o.slice(0,o.length-s);if(n in i.cache&&!i.cache[n].length)return void r([])}}i.timer=setTimeout(function(){l.source(o,r)},l.delay)}}else i.last_val=o,i.sc.style.display="none"}},o(i,"keyup",i.keyupHandler),i.focusHandler=function(e){i.last_val="\n",i.keyupHandler(e)},l.minChars||o(i,"focus",i.focusHandler)}this.destroy=function(){for(var e=0;e<a.length;e++){var t=a[e];s(window,"resize",t.updateSC),s(t,"blur",t.blurHandler),s(t,"focus",t.focusHandler),s(t,"keydown",t.keydownHandler),s(t,"keyup",t.keyupHandler),t.autocompleteAttr?t.setAttribute("autocomplete",t.autocompleteAttr):t.removeAttribute("autocomplete"),document.body.removeChild(t.sc),t=null}}}}return e}();!function(){"function"==typeof define&&define.amd?define("autoComplete",function(){return autoComplete}):"undefined"!=typeof module&&module.exports?module.exports=autoComplete:window.autoComplete=autoComplete}();
diff --git a/server/site/js/blocky.js b/server/site/js/blocky.js
new file mode 100644
index 0000000..7f7af20
--- /dev/null
+++ b/server/site/js/blocky.js
@@ -0,0 +1,2513 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+// THIS IS AN AUTOMATICALLY COMBINED FILE. PLEASE EDIT source/*.js!!
+
+
+
+/******************************************
+ Fetched from source/activity.js
+******************************************/
+
+
+function list_activity(state, json) {
+    let obj = document.getElementById('activity');
+    obj.innerHTML = ''; // clear object
+    if (json.activity && json.activity.length > 0) {
+        json.activity = json.activity.sort((a,b) => b.epoch - a.epoch);
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Activity log entries (%u):".format(json.activity.length));
+        div.inject(wheader);
+        let tbl = new HTML('table', { style: {fontSize: '0.8rem'}});
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Type'),
+            new HTML('th', {style: {width: '110px'}},'Added'),
+            new HTML('th', 'Text'),
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.activity.length; i++) {
+            let res = json.activity[i];
+            let when = 'Never';
+            if (res.epoch) {
+                when = moment(res.epoch*1000.0).fromNow();
+            }
+            res.text = res.text.replace(/\b([a-f0-9]{8})[a-f0-9]{32}\b/g, (a, b) => b + "..");
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(res.ntype)),
+                new HTML('td', moment(res.epoch*1000.0).fromNow()),
+                new HTML('td', res.text)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+}
+
+
+function init_activity(source) {
+    let obj = document.getElementById('activity');
+    obj.innerText = "Fetching activity, hang on...";
+    GET('./api/activity', list_activity, manage_error, {});
+}
+
+
+/******************************************
+ Fetched from source/banlist.js
+******************************************/
+
+function manage_error(state, json) {
+  if (!json) json = state;
+  modal("An error occured", json.message || json.reason, "error");
+}
+
+function list_bans(state, json) {
+    let obj = document.getElementById('banlist');
+    obj.innerHTML = ''; // clear object
+    if (json.bans && json.bans.length > 0) {
+        json.bans = json.bans.sort((a,b) => b.epoch - a.epoch);
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Currently banned IP addresses/networks (%u):".format(json.bans.length));
+        div.inject(wheader);
+        
+        let tbl = new HTML('table', { style: {fontSize: '0.8rem'}});
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', {style: {width: '120px'}},'Last updated'),
+            new HTML('th', 'Reason for ban'),
+            new HTML('th', {style: {width: '100px'}},'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        tbl.inject(tbody);
+        for (var i = 0; i < json.bans.length; i++) {
+            if (i > 50) {
+              div.inject(_hr());
+              div.inject(_i("Only the 50 latest results are shown here. Use the search feature to find more IPs."));
+              break
+            }
+            let res = json.bans[i];
+            let timeout = 'unknown?';
+            if (res.epoch) {
+                timeout = moment(res.epoch*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_banlist('%s');".format(res.rid)}, "Remove ban")
+            ];
+            let name = res.ip;
+            if (res.dns && res.dns.length > 0 && res.dns != res.ip && res.dns.match) {
+                let m = res.dns.match(/(([^.]+\.((com?|net|org|edu|)\.)?)[^.]+)$/);
+                lastbit = m ? m[1] : '';
+                name = "%s (%s)".format(res.ip, lastbit);
+            }
+            res.reason = res.reason.replace(/\b(\d{4,16})\b/g, (a,b) => parseInt(b).pretty());
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(name)),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        
+        obj.inject(div);
+    }
+}
+
+
+function init_banlist(source) {
+    let obj = document.getElementById('banlist');
+    obj.innerText = "Fetching ban list, hang on...";
+    GET('./api/bans', list_bans, manage_error, {});
+}
+
+
+
+function banlist_added(state, json) {
+  alert("Banlist entry added!");
+  location.reload();
+}
+
+
+function add_banlist() {
+    let source = document.getElementById('source').value;
+    let target = document.getElementById('target').value;
+    let reason = document.getElementById('reason').value;
+    let force = document.getElementById('force').checked;
+    let m = source.match(/([a-f0-9:.\/]+)/);
+    if (m) {
+        source = m[0];
+        PUT('./api/bans', banlist_added, {}, manage_error, {
+          source: source,
+          target: target,
+          reason: reason,
+          force: force
+          });
+    } else {
+      alert("Invalid source address entered!");
+    }
+    return false
+}
+
+
+/******************************************
+ Fetched from source/base-http-extensions.js
+******************************************/
+
+// URL calls currently 'in escrow'. This controls the spinny wheel animation
+var async_escrow = {}
+var async_maxwait = 250; // ms to wait before displaying spinner
+var async_status = 'clear';
+var async_cache = {}
+
+// Escrow spinner check
+async function escrow_check() {
+    let now = new Date();
+    let show_spinner = false;
+    for (var k in async_escrow) {
+        if ( (now - async_escrow[k]) > async_maxwait ) {
+            show_spinner = true;
+            break;
+        }
+    }
+    // Fetch or create the spinner
+    let spinner = document.getElementById('spinner');
+    if (!spinner) {
+        spinner = new HTML('div', { id: 'spinner', class: 'spinner'});
+        spinwheel = new HTML('div', {id: 'spinwheel', class: 'spinwheel'});
+        spinner.inject(spinwheel);
+        spinner.inject(new HTML('h2', {}, "Loading, please wait.."));
+        document.body.appendChild(spinner);
+    }
+    // Show or don't show spinner?
+    if (show_spinner) {
+        spinner.style.display = 'block';
+        if (async_status === 'clear') {
+            console.log("Waiting for JSON resource, deploying spinner");
+            async_status = 'waiting';
+        }
+    } else {
+        spinner.style.display = 'none';
+        if (async_status === 'waiting') {
+            console.log("All URLs out of escrow, dropping spinner");
+            async_status = 'clear';
+        }
+    }
+}
+
+async function async_snap(error) {
+    msg = await error.text();
+    msg = (msg||"An unknown error occured, possibly an internal browser issue").replace(/<.*?>/g, ""); // strip HTML tags
+    modal("An error occured", "An error code %u occured while trying to fetch %s:\n%s".format(error.status, error.url, msg), "error");
+}
+
+
+// Asynchronous GET call
+async function GET(url, callback, state, snap, method, body) {
+    method = method || 'get'
+    console.log("Fetching JSON resource at %s".format(url))
+    let pkey = "GET-%s-%s".format(callback, url);
+    let res = undefined;
+    let res_json = undefined;
+    state = state || {};
+    state.url = url;
+    if (state && state.cached === true && async_cache[url]) {
+        console.log("Fetching %s from cache".format(url));
+        res_json = async_cache[url];
+    }
+    else {
+        try {
+            let meta = {method: method, credentials: 'include', referrerPolicy: 'unsafe-url', headers: {'x-original-referral': document.referrer}};
+            if (body) {
+                meta.body = body;
+            }
+            console.log("putting %s in escrow...".format(url));
+            async_escrow[pkey] = new Date(); // Log start of request in escrow dict
+            const rv = await fetch(url, meta); // Wait for resource...
+
+            // Since this is an async request, the request may have been canceled
+            // by the time we get a response. Only do callback if not.
+            if (async_escrow[pkey] !== undefined) {
+                delete async_escrow[pkey]; // move out of escrow when fetched
+                res = rv;
+            }
+        }
+        catch (e) {
+            delete async_escrow[pkey]; // move out of escrow if failed
+            console.log("The URL %s could not be fetched: %s".format(url, e));
+            if (snap) snap({}, {reason: e});
+            else {
+                modal("An error occured", "An error occured while trying to fetch %s:\n%s".format(url, e), "error");
+            }
+        }
+    }
+    if (res !== undefined || res_json !== undefined) {
+        // We expect a 2xx return code (usually 200 or 201), snap otherwise
+        if ((res_json) || (res.status >= 200 && res.status < 300)) {
+            console.log("Successfully fetched %s".format(url))
+            if (res_json) {
+                js = res_json;
+            } else {
+                js = await res.json();
+                async_cache[url] = js;
+            }
+            if (callback) {
+                callback(state, js);
+            } else {
+                console.log("No callback function was registered for %s, ignoring result.".format(url));
+            }
+        } else {
+            console.log("URL %s returned HTTP code %u, snapping!".format(url, res.status));
+            try {
+                js = await res.json();
+                snap(state, js);
+                return;
+            } catch (e) {}
+            if (snap) snap(res);
+            else async_snap(res);
+        }
+    }
+}
+
+
+// DELETE wrapper
+async function DELETE(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'delete', JSON.stringify(json));
+}
+
+// POST wrapper
+async function POST(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'post', JSON.stringify(json));
+}
+
+// PUT wrapper
+async function PUT(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'put', JSON.stringify(json));
+}
+
+// PATCH wrapper
+async function PATCH(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'PATCH', JSON.stringify(json));
+}
+
+// whatwg fetch for IE
+(function(self) {
+  'use strict';
+
+  if (self.fetch) {
+    return
+  }
+
+  var support = {
+    searchParams: 'URLSearchParams' in self,
+    iterable: 'Symbol' in self && 'iterator' in Symbol,
+    blob: 'FileReader' in self && 'Blob' in self && (function() {
+      try {
+        new Blob()
+        return true
+      } catch(e) {
+        return false
+      }
+    })(),
+    formData: 'FormData' in self,
+    arrayBuffer: 'ArrayBuffer' in self
+  }
+
+  if (support.arrayBuffer) {
+    var viewClasses = [
+      '[object Int8Array]',
+      '[object Uint8Array]',
+      '[object Uint8ClampedArray]',
+      '[object Int16Array]',
+      '[object Uint16Array]',
+      '[object Int32Array]',
+      '[object Uint32Array]',
+      '[object Float32Array]',
+      '[object Float64Array]'
+    ]
+
+    var isDataView = function(obj) {
+      return obj && DataView.prototype.isPrototypeOf(obj)
+    }
+
+    var isArrayBufferView = ArrayBuffer.isView || function(obj) {
+      return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
+    }
+  }
+
+  function normalizeName(name) {
+    if (typeof name !== 'string') {
+      name = String(name)
+    }
+    if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
+      throw new TypeError('Invalid character in header field name')
+    }
+    return name.toLowerCase()
+  }
+
+  function normalizeValue(value) {
+    if (typeof value !== 'string') {
+      value = String(value)
+    }
+    return value
+  }
+
+  // Build a destructive iterator for the value list
+  function iteratorFor(items) {
+    var iterator = {
+      next: function() {
+        var value = items.shift()
+        return {done: value === undefined, value: value}
+      }
+    }
+
+    if (support.iterable) {
+      iterator[Symbol.iterator] = function() {
+        return iterator
+      }
+    }
+
+    return iterator
+  }
+
+  function Headers(headers) {
+    this.map = {}
+
+    if (headers instanceof Headers) {
+      headers.forEach(function(value, name) {
+        this.append(name, value)
+      }, this)
+    } else if (Array.isArray(headers)) {
+      headers.forEach(function(header) {
+        this.append(header[0], header[1])
+      }, this)
+    } else if (headers) {
+      Object.getOwnPropertyNames(headers).forEach(function(name) {
+        this.append(name, headers[name])
+      }, this)
+    }
+  }
+
+  Headers.prototype.append = function(name, value) {
+    name = normalizeName(name)
+    value = normalizeValue(value)
+    var oldValue = this.map[name]
+    this.map[name] = oldValue ? oldValue+','+value : value
+  }
+
+  Headers.prototype['delete'] = function(name) {
+    delete this.map[normalizeName(name)]
+  }
+
+  Headers.prototype.get = function(name) {
+    name = normalizeName(name)
+    return this.has(name) ? this.map[name] : null
+  }
+
+  Headers.prototype.has = function(name) {
+    return this.map.hasOwnProperty(normalizeName(name))
+  }
+
+  Headers.prototype.set = function(name, value) {
+    this.map[normalizeName(name)] = normalizeValue(value)
+  }
+
+  Headers.prototype.forEach = function(callback, thisArg) {
+    for (var name in this.map) {
+      if (this.map.hasOwnProperty(name)) {
+        callback.call(thisArg, this.map[name], name, this)
+      }
+    }
+  }
+
+  Headers.prototype.keys = function() {
+    var items = []
+    this.forEach(function(value, name) { items.push(name) })
+    return iteratorFor(items)
+  }
+
+  Headers.prototype.values = function() {
+    var items = []
+    this.forEach(function(value) { items.push(value) })
+    return iteratorFor(items)
+  }
+
+  Headers.prototype.entries = function() {
+    var items = []
+    this.forEach(function(value, name) { items.push([name, value]) })
+    return iteratorFor(items)
+  }
+
+  if (support.iterable) {
+    Headers.prototype[Symbol.iterator] = Headers.prototype.entries
+  }
+
+  function consumed(body) {
+    if (body.bodyUsed) {
+      return Promise.reject(new TypeError('Already read'))
+    }
+    body.bodyUsed = true
+  }
+
+  function fileReaderReady(reader) {
+    return new Promise(function(resolve, reject) {
+      reader.onload = function() {
+        resolve(reader.result)
+      }
+      reader.onerror = function() {
+        reject(reader.error)
+      }
+    })
+  }
+
+  function readBlobAsArrayBuffer(blob) {
+    var reader = new FileReader()
+    var promise = fileReaderReady(reader)
+    reader.readAsArrayBuffer(blob)
+    return promise
+  }
+
+  function readBlobAsText(blob) {
+    var reader = new FileReader()
+    var promise = fileReaderReady(reader)
+    reader.readAsText(blob)
+    return promise
+  }
+
+  function readArrayBufferAsText(buf) {
+    var view = new Uint8Array(buf)
+    var chars = new Array(view.length)
+
+    for (var i = 0; i < view.length; i++) {
+      chars[i] = String.fromCharCode(view[i])
+    }
+    return chars.join('')
+  }
+
+  function bufferClone(buf) {
+    if (buf.slice) {
+      return buf.slice(0)
+    } else {
+      var view = new Uint8Array(buf.byteLength)
+      view.set(new Uint8Array(buf))
+      return view.buffer
+    }
+  }
+
+  function Body() {
+    this.bodyUsed = false
+
+    this._initBody = function(body) {
+      this._bodyInit = body
+      if (!body) {
+        this._bodyText = ''
+      } else if (typeof body === 'string') {
+        this._bodyText = body
+      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
+        this._bodyBlob = body
+      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
+        this._bodyFormData = body
+      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
+        this._bodyText = body.toString()
+      } else if (support.arrayBuffer && support.blob && isDataView(body)) {
+        this._bodyArrayBuffer = bufferClone(body.buffer)
+        // IE 10-11 can't handle a DataView body.
+        this._bodyInit = new Blob([this._bodyArrayBuffer])
+      } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
+        this._bodyArrayBuffer = bufferClone(body)
+      } else {
+        throw new Error('unsupported BodyInit type')
+      }
+
+      if (!this.headers.get('content-type')) {
+        if (typeof body === 'string') {
+          this.headers.set('content-type', 'text/plain;charset=UTF-8')
+        } else if (this._bodyBlob && this._bodyBlob.type) {
+          this.headers.set('content-type', this._bodyBlob.type)
+        } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
+          this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
+        }
+      }
+    }
+
+    if (support.blob) {
+      this.blob = function() {
+        var rejected = consumed(this)
+        if (rejected) {
+          return rejected
+        }
+
+        if (this._bodyBlob) {
+          return Promise.resolve(this._bodyBlob)
+        } else if (this._bodyArrayBuffer) {
+          return Promise.resolve(new Blob([this._bodyArrayBuffer]))
+        } else if (this._bodyFormData) {
+          throw new Error('could not read FormData body as blob')
+        } else {
+          return Promise.resolve(new Blob([this._bodyText]))
+        }
+      }
+
+      this.arrayBuffer = function() {
+        if (this._bodyArrayBuffer) {
+          return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
+        } else {
+          return this.blob().then(readBlobAsArrayBuffer)
+        }
+      }
+    }
+
+    this.text = function() {
+      var rejected = consumed(this)
+      if (rejected) {
+        return rejected
+      }
+
+      if (this._bodyBlob) {
+        return readBlobAsText(this._bodyBlob)
+      } else if (this._bodyArrayBuffer) {
+        return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
+      } else if (this._bodyFormData) {
+        throw new Error('could not read FormData body as text')
+      } else {
+        return Promise.resolve(this._bodyText)
+      }
+    }
+
+    if (support.formData) {
+      this.formData = function() {
+        return this.text().then(decode)
+      }
+    }
+
+    this.json = function() {
+      return this.text().then(JSON.parse)
+    }
+
+    return this
+  }
+
+  // HTTP methods whose capitalization should be normalized
+  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
+
+  function normalizeMethod(method) {
+    var upcased = method.toUpperCase()
+    return (methods.indexOf(upcased) > -1) ? upcased : method
+  }
+
+  function Request(input, options) {
+    options = options || {}
+    var body = options.body
+
+    if (input instanceof Request) {
+      if (input.bodyUsed) {
+        throw new TypeError('Already read')
+      }
+      this.url = input.url
+      this.credentials = input.credentials
+      if (!options.headers) {
+        this.headers = new Headers(input.headers)
+      }
+      this.method = input.method
+      this.mode = input.mode
+      if (!body && input._bodyInit != null) {
+        body = input._bodyInit
+        input.bodyUsed = true
+      }
+    } else {
+      this.url = String(input)
+    }
+
+    this.credentials = options.credentials || this.credentials || 'omit'
+    if (options.headers || !this.headers) {
+      this.headers = new Headers(options.headers)
+    }
+    this.method = normalizeMethod(options.method || this.method || 'GET')
+    this.mode = options.mode || this.mode || null
+    this.referrer = null
+
+    if ((this.method === 'GET' || this.method === 'HEAD') && body) {
+      throw new TypeError('Body not allowed for GET or HEAD requests')
+    }
+    this._initBody(body)
+  }
+
+  Request.prototype.clone = function() {
+    return new Request(this, { body: this._bodyInit })
+  }
+
+  function decode(body) {
+    var form = new FormData()
+    body.trim().split('&').forEach(function(bytes) {
+      if (bytes) {
+        var split = bytes.split('=')
+        var name = split.shift().replace(/\+/g, ' ')
+        var value = split.join('=').replace(/\+/g, ' ')
+        form.append(decodeURIComponent(name), decodeURIComponent(value))
+      }
+    })
+    return form
+  }
+
+  function parseHeaders(rawHeaders) {
+    var headers = new Headers()
+    // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
+    // https://tools.ietf.org/html/rfc7230#section-3.2
+    var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
+    preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
+      var parts = line.split(':')
+      var key = parts.shift().trim()
+      if (key) {
+        var value = parts.join(':').trim()
+        headers.append(key, value)
+      }
+    })
+    return headers
+  }
+
+  Body.call(Request.prototype)
+
+  function Response(bodyInit, options) {
+    if (!options) {
+      options = {}
+    }
+
+    this.type = 'default'
+    this.status = options.status === undefined ? 200 : options.status
+    this.ok = this.status >= 200 && this.status < 300
+    this.statusText = 'statusText' in options ? options.statusText : 'OK'
+    this.headers = new Headers(options.headers)
+    this.url = options.url || ''
+    this._initBody(bodyInit)
+  }
+
+  Body.call(Response.prototype)
+
+  Response.prototype.clone = function() {
+    return new Response(this._bodyInit, {
+      status: this.status,
+      statusText: this.statusText,
+      headers: new Headers(this.headers),
+      url: this.url
+    })
+  }
+
+  Response.error = function() {
+    var response = new Response(null, {status: 0, statusText: ''})
+    response.type = 'error'
+    return response
+  }
+
+  var redirectStatuses = [301, 302, 303, 307, 308]
+
+  Response.redirect = function(url, status) {
+    if (redirectStatuses.indexOf(status) === -1) {
+      throw new RangeError('Invalid status code')
+    }
+
+    return new Response(null, {status: status, headers: {location: url}})
+  }
+
+  self.Headers = Headers
+  self.Request = Request
+  self.Response = Response
+
+  self.fetch = function(input, init) {
+    return new Promise(function(resolve, reject) {
+      var request = new Request(input, init)
+      var xhr = new XMLHttpRequest()
+
+      xhr.onload = function() {
+        var options = {
+          status: xhr.status,
+          statusText: xhr.statusText,
+          headers: parseHeaders(xhr.getAllResponseHeaders() || '')
+        }
+        options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
+        var body = 'response' in xhr ? xhr.response : xhr.responseText
+        resolve(new Response(body, options))
+      }
+
+      xhr.onerror = function() {
+        reject(new TypeError('Network request failed'))
+      }
+
+      xhr.ontimeout = function() {
+        reject(new TypeError('Network request failed'))
+      }
+
+      xhr.open(request.method, request.url, true)
+
+      if (request.credentials === 'include') {
+        xhr.withCredentials = true
+      } else if (request.credentials === 'omit') {
+        xhr.withCredentials = false
+      }
+
+      if ('responseType' in xhr && support.blob) {
+        xhr.responseType = 'blob'
+      }
+
+      request.headers.forEach(function(value, name) {
+        xhr.setRequestHeader(name, value)
+      })
+
+      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
+    })
+  }
+  self.fetch.polyfill = true
+})(typeof self !== 'undefined' ? self : this);
+
+
+/******************************************
+ Fetched from source/base-js-extensions.js
+******************************************/
+
+/**
+ * String formatting prototype
+ * A'la printf
+ */
+
+String.prototype.format = function() {
+  let args = arguments;
+  let n = 0;
+  let t = this;
+  let rtn = this.replace(/(?!%)?%([-+]*)([0-9.]*)([a-zA-Z])/g, function(m, pm, len, fmt) {
+      len = parseInt(len || '1');
+      // We need the correct number of args, balk otherwise, using ourselves to format the error!
+      if (args.length <= n) {
+        let err = "Error interpolating string '%s': Expected at least %u argments, only got %u!".format(t, n+1, args.length);
+        console.log(err);
+        throw err;
+      }
+      let varg = args[n];
+      n++;
+      switch (fmt) {
+        case 's':
+          if (typeof(varg) == 'function') {
+            varg = '(function)';
+          }
+          return varg;
+        // For now, let u, d and i do the same thing
+        case 'd':
+        case 'i':
+        case 'u':
+          varg = parseInt(varg).pad(len); // truncate to Integer, pad if needed
+          return varg;
+      }
+    });
+  return rtn;
+}
+
+
+/**
+ * Number prettification prototype:
+ * Converts 1234567 into 1,234,567 etc
+ */
+
+Number.prototype.pretty = function(fix) {
+  if (fix) {
+    return String(this.toFixed(fix)).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
+  }
+  return String(this.toFixed(0)).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
+};
+
+
+/**
+ * Number padding
+ * usage: 123.pad(6) -> 000123
+ */
+
+Number.prototype.pad = function(n) {
+  var str;
+  str = String(this);
+
+  /* Do we need to pad? if so, do it using String.repeat */
+  if (str.length < n) {
+    str = "0".repeat(n - str.length) + str;
+  }
+  return str;
+};
+
+
+/* Func for converting a date to YYYY-MM-DD HH:MM */
+
+Date.prototype.ISOBare = function() {
+  var M, d, h, m, y;
+  y = this.getFullYear();
+  m = (this.getMonth() + 1).pad(2);
+  d = this.getDate().pad(2);
+  h = this.getHours().pad(2);
+  M = this.getMinutes().pad(2);
+  return y + "-" + m + "-" + d + " " + h + ":" + M;
+};
+
+
+/* isArray: function to detect if an object is an array */
+
+isArray = function(value) {
+  return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length'));
+};
+
+
+/* isHash: function to detect if an object is a hash */
+
+isHash = function(value) {
+  return value && typeof value === 'object' && !isArray(value);
+};
+
+
+/* Remove an array element by value */
+
+Array.prototype.remove = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      this.splice(i, 1);
+      return this;
+    }
+  }
+  return this;
+};
+
+
+/* Check if array has value */
+Array.prototype.has = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      return true;
+    }
+  }
+  return false;
+};
+
+
+
+
+/******************************************
+ Fetched from source/datepicker.js
+******************************************/
+
+var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+var datepicker_spawner = null
+var calendarpicker_spawner = null
+var units = {
+    w: 'week',
+    d: 'day',
+    M: 'month',
+    y: 'year'
+}
+
+function fixupPicker(obj) {
+    obj.addEventListener("focus", function(event){
+        $('html').on('hide.bs.dropdown', function (e) {
+            return false;
+        });
+    });
+    obj.addEventListener("blur", function(event){
+        $('html').unbind('hide.bs.dropdown')
+    });
+}
+// makeSelect: Creates a <select> object with options
+function makeSelect(options, id, selval) {
+    var sel = document.createElement('select')
+    sel.addEventListener("focus", function(event){
+        $('html').on('hide.bs.dropdown', function (e) {
+            return false;
+        });
+    });
+    sel.addEventListener("blur", function(event){
+        $('html').unbind('hide.bs.dropdown')
+    });
+    sel.setAttribute("name", id)
+    sel.setAttribute("id", id)
+    // For each options element, create it in the DOM
+    for (var key in options) {
+        var opt = document.createElement('option')
+        // Hash or array?
+        if (typeof key == "string") {
+            opt.setAttribute("value", key)
+            // Option is selected by default?
+            if (key == selval) {
+                opt.setAttribute("selected", "selected")
+            }
+        } else {
+            // Option is selected by default?
+            if (options[key] == selval) {
+                opt.setAttribute("selected", "selected")
+            }
+        }
+        opt.text = options[key]
+        sel.appendChild(opt)
+    }
+    return sel
+}
+
+// splitDiv: Makes a split div with 2 elements,
+// and puts div2 into the right column,
+// and 'name' as text in the left one.
+function splitDiv(id, name, div2) {
+    var div = document.createElement('div')
+    var subdiv = document.createElement('div')
+    var radio = document.createElement('input')
+    radio.setAttribute("type", "radio")
+    radio.setAttribute("name", "datepicker_radio")
+    radio.setAttribute("value", name)
+    radio.setAttribute("id", "datepicker_radio_" + id)
+    radio.setAttribute("onclick", "calcTimespan('"+ id + "')")
+    var label = document.createElement('label')
+    label.innerHTML = "&nbsp; " + name + ": "
+    label.setAttribute("for", "datepicker_radio_" + id)
+    
+    
+    subdiv.appendChild(radio)
+    subdiv.appendChild(label)
+    
+    
+    subdiv.style.float = "left"
+    div2.style.float = "left"
+    
+    subdiv.style.width = "120px"
+    subdiv.style.height = "48px"
+    div2.style.height = "48px"
+    div2.style.width = "250px"
+    
+    div.appendChild(subdiv)
+    div.appendChild(div2)
+    return div
+}
+
+// calcTimespan: Calculates the value and representational text
+// for the datepicker choice and puts it in the datepicker's
+// spawning input/select element.
+function calcTimespan(what) {
+    var wat = ""
+    var tval = ""
+    
+    // Less than N units ago?
+    if (what == 'lt') {
+        // Get unit and how many units
+        var N = document.getElementById('datepicker_lti').value
+        var unit = document.getElementById('datepicker_lts').value
+        var unitt = units[unit]
+        if (parseInt(N) != 1) {
+            unitt += "s"
+        }
+        
+        // If this makes sense, construct a humanly readable and a computer version
+        // of the timespan
+        if (N.length > 0) {
+            wat = "Less than " + N + " " + unitt + " ago"
+            tval = "lte=" + N + unit
+        }
+    }
+    
+    // More than N units ago?
+    if (what == 'mt') {
+        // As above, get unit and no of units.
+        var N = document.getElementById('datepicker_mti').value
+        var unit = document.getElementById('datepicker_mts').value
+        var unitt = units[unit]
+        if (parseInt(N) != 1) {
+            unitt += "s"
+        }
+        
+        // construct timespan val + description
+        if (N.length > 0) {
+            wat = "More than " + N + " " + unitt + " ago"
+            tval = "gte=" + N + unit
+        }
+    }
+    
+    // Date range?
+    if (what == 'cd') {
+        // Get From and To values
+        var f = document.getElementById('datepicker_cfrom').value
+        var t = document.getElementById('datepicker_cto').value
+        // construct timespan val + description if both from and to are valid
+        if (f.length > 0 && t.length > 0) {
+            wat = "From " + f + " to " + t
+            tval = "dfr=" + f + "|dto=" + t
+        }
+    }
+    
+    // If we calc'ed a value and spawner exists, update its key/val
+    if (datepicker_spawner && what && wat.length > 0) {
+        document.getElementById('datepicker_radio_' + what).checked = true
+        if (datepicker_spawner.options) {
+            datepicker_spawner.options[0].value = tval
+            datepicker_spawner.options[0].text = wat
+        } else if (datepicker_spawner.value) {
+            datepicker_spawner.value = wat
+            datepicker_spawner.setAttribute("data", tval)
+        }
+        
+    }
+}
+
+// datePicker: spawns a date picker with various
+// timespan options right next to the parent caller.
+function datePicker(parent, seedPeriod) {
+    datepicker_spawner = parent
+    var div = document.getElementById('datepicker_popup')
+    
+    // If the datepicker object doesn't exist, spawn it
+    if (!div) {
+        div = document.createElement('div')
+        var id = parseInt(Math.random() * 10000).toString(16)
+        div.setAttribute("id", "datepicker_popup")
+        div.setAttribute("class", "datepicker")
+    }
+    
+    // Reset the contents of the datepicker object
+    div.innerHTML = ""
+    div.style.display = "block"
+    
+    // Position the datepicker next to whatever called it
+    var bb = parent.getBoundingClientRect()
+    div.style.top = (bb.bottom + 8) + "px"
+    div.style.left = (bb.left + 32) + "px"
+    
+    
+    // -- Less than N $units ago
+    var ltdiv = document.createElement('div')
+    var lti = document.createElement('input')
+    lti.setAttribute("id", "datepicker_lti")
+    lti.style.width = "48px"
+    lti.setAttribute("onkeyup", "calcTimespan('lt')")
+    lti.setAttribute("onblur", "calcTimespan('lt')")
+    ltdiv.appendChild(lti)
+    
+    var lts = makeSelect({
+        'd': "Day(s)",
+        'w': 'Week(s)',
+        'M': "Month(s)",
+        'y': "Year(s)"
+    }, 'datepicker_lts', 'm')
+    lts.setAttribute("onchange", "calcTimespan('lt')")
+    ltdiv.appendChild(lts)
+    ltdiv.appendChild(document.createTextNode(' ago'))
+    
+    div.appendChild(splitDiv('lt', 'Less than', ltdiv))
+    
+    
+    // -- More than N $units ago
+    var mtdiv = document.createElement('div')
+    
+    var mti = document.createElement('input')
+    mti.style.width = "48px"
+    mti.setAttribute("id", "datepicker_mti")
+    mti.setAttribute("onkeyup", "calcTimespan('mt')")
+    mti.setAttribute("onblur", "calcTimespan('mt')")
+    mtdiv.appendChild(mti)
+    
+    
+    var mts = makeSelect({
+        'd': "Day(s)",
+        'w': 'Week(s)',
+        'M': "Month(s)",
+        'y': "Year(s)"
+    }, 'datepicker_mts', 'm')
+    mtdiv.appendChild(mts)
+    mts.setAttribute("onchange", "calcTimespan('mt')")
+    mtdiv.appendChild(document.createTextNode(' ago'))
+    div.appendChild(splitDiv('mt', 'More than', mtdiv))
+    
+    
+    
+    // -- Calendar timespan
+    // This is just two text fields, the calendarPicker sub-plugin populates them
+    var cdiv = document.createElement('div')
+    
+    var cfrom = document.createElement('input')
+    cfrom.style.width = "90px"
+    cfrom.setAttribute("id", "datepicker_cfrom")
+    cfrom.setAttribute("onfocus", "showCalendarPicker(this)")
+    cfrom.setAttribute("onchange", "calcTimespan('cd')")
+    cdiv.appendChild(document.createTextNode('From: '))
+    cdiv.appendChild(cfrom)
+    
+    var cto = document.createElement('input')
+    cto.style.width = "90px"
+    cto.setAttribute("id", "datepicker_cto")
+    cto.setAttribute("onfocus", "showCalendarPicker(this)")
+    cto.setAttribute("onchange", "calcTimespan('cd')")
+    cdiv.appendChild(document.createTextNode('To: '))
+    cdiv.appendChild(cto)
+    
+    div.appendChild(splitDiv('cd', 'Date range', cdiv))
+    
+    
+    
+    // -- Magic button that sends the timespan back to the caller
+    var okay = document.createElement('input')
+    okay.setAttribute("type", "button")
+    okay.setAttribute("value", "Okay")
+    okay.setAttribute("onclick", "setDatepickerDate()")
+    div.appendChild(okay)
+    parent.parentNode.appendChild(div)
+    document.body.setAttribute("onclick", "")
+    window.setTimeout(function() { document.body.setAttribute("onclick", "blurDatePicker(event)") }, 200)
+    lti.focus()
+    
+    // This is for recalcing the set options if spawned from a
+    // select/input box with an existing value derived from an
+    // earlier call to datePicker
+    var ptype = ""
+    var pvalue = parent.hasAttribute("data") ? parent.getAttribute("data") : parent.value
+    if (pvalue.search(/=|-/) != -1) {
+        
+        // Less than N units ago?
+        if (pvalue.match(/lte/)) {
+            var m = pvalue.match(/lte=(\d+)([dMyw])/)
+            ptype = 'lt'
+            if (m) {
+                document.getElementById('datepicker_lti').value = m[1]
+                var sel = document.getElementById('datepicker_lts')
+                for (var i in sel.options) {
+                    if (parseInt(i) >= 0) {
+                        if (sel.options[i].value == m[2]) {
+                            sel.options[i].selected = "selected"
+                        } else {
+                            sel.options[i].selected = null
+                        }
+                    }
+                }
+            }
+            
+        }
+        
+        // More than N units ago?
+        if (pvalue.match(/gte/)) {
+            ptype = 'mt'
+            var m = pvalue.match(/gte=(\d+)([dMyw])/)
+            if (m) {
+                document.getElementById('datepicker_mti').value = m[1]
+                var sel = document.getElementById('datepicker_mts')
+                // Go through the unit values, select the one we use
+                for (var i in sel.options) {
+                    if (parseInt(i) >= 0) {
+                        if (sel.options[i].value == m[2]) {
+                            sel.options[i].selected = "selected"
+                        } else {
+                            sel.options[i].selected = null
+                        }
+                    }
+                }
+            }
+        }
+        
+        // Date range?
+        if (pvalue.match(/dfr/)) {
+            ptype = 'cd'
+            // Make sure we have both a dfr and a dto here, catch them
+            var mf = pvalue.match(/dfr=(\d+-\d+-\d+)/)
+            var mt = pvalue.match(/dto=(\d+-\d+-\d+)/)
+            if (mf && mt) {
+                // easy peasy, just set two text fields!
+                document.getElementById('datepicker_cfrom').value = mf[1]
+                document.getElementById('datepicker_cto').value = mt[1]
+            }
+        }
+        // Month??
+        if (pvalue.match(/(\d{4})-(\d+)/)) {
+            ptype = 'cd'
+            // Make sure we have both a dfr and a dto here, catch them
+            var m = pvalue.match(/(\d{4})-(\d+)/)
+            if (m.length == 3) {
+                // easy peasy, just set two text fields!
+                var dfrom = new Date(parseInt(m[1]),parseInt(m[2])-1,1, 0, 0, 0)
+                var dto = new Date(parseInt(m[1]),parseInt(m[2]),0, 23, 59, 59)
+                document.getElementById('datepicker_cfrom').value = m[0] + "-" + dfrom.getDate()
+                document.getElementById('datepicker_cto').value = m[0] + "-" + dto.getDate()
+            }
+        }
+        calcTimespan(ptype)
+    }
+}
+
+
+function datePickerValue(seedPeriod) {
+    // This is for recalcing the set options if spawned from a
+    // select/input box with an existing value derived from an
+    // earlier call to datePicker
+    var ptype = ""
+    var rv = seedPeriod
+    if (seedPeriod && seedPeriod.search && seedPeriod.search(/=|-/) != -1) {
+        
+        // Less than N units ago?
+        if (seedPeriod.match(/lte/)) {
+            var m = seedPeriod.match(/lte=(\d+)([dMyw])/)
+            ptype = 'lt'
+            var unitt = units[m[2]]
+            if (parseInt(m[1]) != 1) {
+                unitt += "s"
+            }
+            rv = "Less than " + m[1] + " " + unitt + " ago"
+        }
+        
+        // More than N units ago?
+        if (seedPeriod.match(/gte/)) {
+            ptype = 'mt'
+            var m = seedPeriod.match(/gte=(\d+)([dMyw])/)
+            var unitt = units[m[2]]
+            if (parseInt(m[1]) != 1) {
+                unitt += "s"
+            }
+            rv = "More than " + m[1] + " " + unitt + " ago"
+        }
+        
+        // Date range?
+        if (seedPeriod.match(/dfr/)) {
+            ptype = 'cd'
+            var mf = seedPeriod.match(/dfr=(\d+-\d+-\d+)/)
+            var mt = seedPeriod.match(/dto=(\d+-\d+-\d+)/)
+            if (mf && mt) {
+                rv = "From " + mf[1] + " to " + mt[1]
+            }
+        }
+        
+        // Month??
+        if (seedPeriod.match(/^(\d+)-(\d+)$/)) {
+            ptype = 'mr' // just a made up thing...(month range)
+            var mr = seedPeriod.match(/(\d+)-(\d+)/)
+            if (mr) {
+                dfrom = new Date(parseInt(mr[1]),parseInt(mr[2])-1,1, 0, 0, 0)
+                rv = months[dfrom.getMonth()] + ', ' + mr[1]
+            }
+        }
+        
+    }
+    return rv
+}
+
+function datePickerDouble(seedPeriod) {
+    // This basically takes a date-arg and doubles it backwards
+    // so >=3M becomes =>6M etc. Also returns the cutoff for
+    // the original date and the span in days of the original
+    var ptype = ""
+    var rv = seedPeriod
+    var dbl = seedPeriod
+    var tspan = 1
+    var dfrom = new Date()
+    var dto = new Date()
+    
+    // datepicker range?
+    if (seedPeriod && seedPeriod.search && seedPeriod.search(/=/) != -1) {
+        
+        // Less than N units ago?
+        if (seedPeriod.match(/lte/)) {
+            var m = seedPeriod.match(/lte=(\d+)([dMyw])/)
+            ptype = 'lt'
+            rv = "<" + m[1] + m[2] + " ago"
+            dbl = "lte=" + (parseInt(m[1])*2) + m[2]
+            
+            // N months ago
+            if (m[2] == "M") {
+                dfrom.setMonth(dfrom.getMonth()-parseInt(m[1]), dfrom.getDate())
+            }
+            
+            // N days ago
+            if (m[2] == "d") {
+                dfrom.setDate(dfrom.getDate()-parseInt(m[1]))
+            }
+            
+            // N years ago
+            if (m[2] == "y") {
+                dfrom.setYear(dfrom.getFullYear()-parseInt(m[1]))
+            }
+            
+            // N weeks ago
+            if (m[2] == "w") {
+                dfrom.setDate(dfrom.getDate()-(parseInt(m[1])*7))
+            }
+            
+            // Calc total duration in days for this time span
+            tspan = parseInt((dto.getTime() - dfrom.getTime() + 5000) / (1000*86400))
+        }
+        
+        // More than N units ago?
+        if (seedPeriod.match(/gte/)) {
+            ptype = 'mt'
+            var m = seedPeriod.match(/gte=(\d+)([dMyw])/)
+            rv = ">" + m[1] + m[2] + " ago"
+            dbl = "gte=" + (parseInt(m[1])*2) + m[2]
+            tspan = parseInt(parseInt(m[1]) * 30.4)
+            dfrom = null
+            
+            // Months
+            if (m[2] == "M") {
+                dto.setMonth(dto.getMonth()-parseInt(m[1]), dto.getDate())
+            }
+            
+            // Days
+            if (m[2] == "d") {
+                dto.setDate(dto.getDate()-parseInt(m[1]))
+            }
+            
+            // Years
+            if (m[2] == "y") {
+                dto.setYear(dto.getFullYear()-parseInt(m[1]))
+            }
+            
+            // Weeks
+            if (m[2] == "w") {
+                dto.setDate(dto.getDate()-(parseInt(m[1])*7))
+            }
+            
+            // Can't really figure out a timespan for this, so...null!
+            // This also sort of invalidates use on the trend page, but meh..
+            tspan = null
+        }
+        
+        // Date range?
+        if (seedPeriod.match(/dfr/)) {
+            ptype = 'cd'
+            // Find from and to
+            var mf = seedPeriod.match(/dfr=(\d+)-(\d+)-(\d+)/)
+            var mt = seedPeriod.match(/dto=(\d+)-(\d+)-(\d+)/)
+            if (mf && mt) {
+                rv = "from " + mf[1] + " to " + mt[1]
+                // Starts at 00:00:00 on from date
+                dfrom = new Date(parseInt(mf[1]),parseInt(mf[2])-1,parseInt(mf[3]), 0, 0, 0)
+                
+                // Ends at 23:59:59 on to date
+                dto = new Date(parseInt(mt[1]),parseInt(mt[2])-1,parseInt(mt[3]), 23, 59, 59)
+                
+                // Get duration in days, add 5 seconds to we can floor the value and get an integer
+                tspan = parseInt((dto.getTime() - dfrom.getTime() + 5000) / (1000*86400))
+                
+                // double the distance
+                var dpast = new Date(dfrom)
+                dpast.setDate(dpast.getDate() - tspan)
+                dbl = seedPeriod.replace(/dfr=[^|]+/, "dfr=" + (dpast.getFullYear()) + '-' + (dpast.getMonth()+1) + '-' + dpast.getDate())
+            } else {
+                tspan = 0
+            }
+        }
+    }
+    
+    // just N days?
+    else if (parseInt(seedPeriod).toString() == seedPeriod.toString()) {
+        tspan = parseInt(seedPeriod)
+        dfrom.setDate(dfrom.getDate() - tspan)
+        dbl = "lte=" + (tspan*2) + "d"
+    }
+    
+    // Specific month?
+    else if (seedPeriod.match(/^(\d+)-(\d+)$/)) {
+        // just a made up thing...(month range)
+        ptype = 'mr' 
+        var mr = seedPeriod.match(/(\d+)-(\d+)/)
+        if (mr) {
+            rv = seedPeriod
+            // Same as before, start at 00:00:00
+            dfrom = new Date(parseInt(mr[1]),parseInt(mr[2])-1,1, 0, 0, 0)
+            // end at 23:59:59
+            dto = new Date(parseInt(mr[1]),parseInt(mr[2]),0, 23, 59, 59)
+            
+            // B-A, add 5 seconds so we can floor the no. of days into an integer neatly
+            tspan = parseInt((dto.getTime() - dfrom.getTime() + 5000) / (1000*86400))
+            
+            // Double timespan
+            var dpast = new Date(dfrom)
+            dpast.setDate(dpast.getDate() - tspan)
+            dbl = "dfr=" + (dpast.getFullYear()) + '-' + (dpast.getMonth()+1) + '-' + dpast.getDate() + "|dto=" + (dto.getFullYear()) + '-' + (dto.getMonth()+1) + '-' + dto.getDate()
+        } else {
+            tspan = 0
+        }
+    }
+    
+    return [dbl, dfrom, dto, tspan]
+}
+
+// set date in caller and hide datepicker again.
+function setDatepickerDate() {
+    calcTimespan()
+    blurDatePicker()
+}
+
+// findParent: traverse DOM and see if we can find a parent to 'el'
+// called 'name'. This is used for figuring out whether 'el' has
+// lost focus or not.
+function findParent(el, name) {
+    if (el.getAttribute && el.getAttribute("id") == name) {
+        return true
+    }
+    if (el.parentNode && el.parentNode.getAttribute) {
+        if (el.parentNode.getAttribute("id") != name) {
+            return findParent(el.parentNode, name)
+        } else {
+            return true
+        }
+    } else {
+        return false;
+    }
+}
+
+// function for hiding the date picker
+function blurDatePicker(evt) {
+    var es = evt ? (evt.target || evt.srcElement) : null;
+    if ((!es || !es.parentNode || (!findParent(es, "datepicker_popup") && !findParent(es, "calendarpicker_popup"))) && !(es ? es : "null").toString().match(/javascript:void/)) {
+        document.getElementById('datepicker_popup').style.display = "none"
+        $('html').trigger('hide.bs.dropdown')
+    }
+}
+
+// draws the actual calendar inside a calendarPicker object
+function drawCalendarPicker(obj, date) {
+    
+    
+    obj.focus()
+    
+    // Default to NOW for calendar.
+    var now = new Date()
+    
+    // if called with an existing date (YYYY-MM-DD),
+    // convert it to a JS date object and use that for
+    // rendering the calendar
+    if (date) {
+        var ar = date.split(/-/)
+        now = new Date(ar[0],parseInt(ar[1])-1,ar[2])
+    }
+    var days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
+    var mat = now
+    
+    // Go to first day of the month
+    mat.setDate(1)
+    
+    obj.innerHTML = "<h3>" + months[mat.getMonth()] + ", " + mat.getFullYear() + ":</h3>"
+    var tm = mat.getMonth()
+    
+    // -- Nav buttons --
+    
+    // back-a-year button
+    var a = document.createElement('a')
+    fixupPicker(a)
+    a.setAttribute("onclick", "drawCalendarPicker(this.parentNode, '" + (mat.getFullYear()-1) + '-' + (mat.getMonth()+1) + '-' + mat.getDate() + "');")
+    a.setAttribute("href", "javascript:void(0);")
+    a.innerHTML = "≪"
+    obj.appendChild(a)
+    
+    // back-a-month button
+    a = document.createElement('a')
+    fixupPicker(a)
+    a.setAttribute("onclick", "drawCalendarPicker(this.parentNode, '" + mat.getFullYear() + '-' + (mat.getMonth()) + '-' + mat.getDate() + "');")
+    a.setAttribute("href", "javascript:void(0);")
+    a.innerHTML = "&lt;"
+    obj.appendChild(a)
+    
+    // forward-a-month button
+    a = document.createElement('a')
+    fixupPicker(a)
+    a.setAttribute("onclick", "drawCalendarPicker(this.parentNode, '" + mat.getFullYear() + '-' + (mat.getMonth()+2) + '-' + mat.getDate() + "');")
+    a.setAttribute("href", "javascript:void(0);")
+    a.innerHTML = "&gt;"
+    obj.appendChild(a)
+    
+    // forward-a-year button
+    a = document.createElement('a')
+    fixupPicker(a)
+    a.setAttribute("onclick", "drawCalendarPicker(this.parentNode, '" + (mat.getFullYear()+1) + '-' + (mat.getMonth()+1) + '-' + mat.getDate() + "');")
+    a.setAttribute("href", "javascript:void(0);")
+    a.innerHTML = "≫"
+    obj.appendChild(a)
+    obj.appendChild(document.createElement('br'))
+    
+    
+    // Table containing the dates of the selected month
+    var table = document.createElement('table')
+    
+    table.setAttribute("border", "1")
+    table.style.margin = "0 auto"
+    
+    // Add header day names
+    var tr = document.createElement('tr');
+    for (var m = 0; m < 7; m++) {
+        var td = document.createElement('th')
+        td.innerHTML = days[m]
+        tr.appendChild(td)
+    }
+    table.appendChild(tr)
+    
+    // Until we hit the first day in a month, add blank days
+    tr = document.createElement('tr');
+    var weekday = mat.getDay()
+    if (weekday == 0) {
+        weekday = 7
+    }
+    weekday--;
+    for (var i = 0; i < weekday; i++) {
+        var td = document.createElement('td')
+        tr.appendChild(td)
+    }
+    
+    // While still in this month, add day then increment date by 1 day.
+    while (mat.getMonth() == tm) {
+        weekday = mat.getDay()
+        if (weekday == 0) {
+            weekday = 7
+        }
+        weekday--;
+        if (weekday == 0) {
+            table.appendChild(tr)
+            tr = document.createElement('tr');
+        }
+        td = document.createElement('td')
+        // onclick for setting the calendarPicker's parent to this val.
+        td.setAttribute("onclick", "setCalendarDate('" + mat.getFullYear() + '-' + (mat.getMonth()+1) + '-' + mat.getDate() + "');")
+        td.innerHTML = mat.getDate()
+        mat.setDate(mat.getDate()+1)
+        tr.appendChild(td)
+    }
+    
+    table.appendChild(tr)
+    obj.appendChild(table)
+}
+
+// callback for datePicker; sets the cd value to what date was picked
+function setCalendarDate(what) {
+    $('html').on('hide.bs.dropdown', function (e) {
+        return false;
+    });
+    setTimeout(function() { $('html').unbind('hide.bs.dropdown');}, 250);
+    
+    
+    calendarpicker_spawner.value = what
+    var div = document.getElementById('calendarpicker_popup')
+    div.parentNode.focus()
+    div.style.display = "none"
+    calcTimespan('cd')
+}
+
+// caller for when someone clicks on a calendarPicker enabled field
+function showCalendarPicker(parent, seedDate) {
+    calendarpicker_spawner = parent
+    
+    // If supplied with a YYYY-MM-DD date, use this to seed the calendar
+    if (!seedDate) {
+        var m = parent.value.match(/(\d+-\d+(-\d+)?)/)
+        if (m) {
+            seedDate = m[1]
+        }
+    }
+    
+    // Show or create the calendar object
+    var div = document.getElementById('calendarpicker_popup')
+    if (!div) {
+        div = document.createElement('div')
+        div.setAttribute("id", "calendarpicker_popup")
+        div.setAttribute("class", "calendarpicker")
+        document.getElementById('datepicker_popup').appendChild(div)
+        div.innerHTML = "Calendar goes here..."
+    }
+    div.style.display = "block"
+    var bb = parent.getBoundingClientRect()
+    
+    // Align with the calling object, slightly below
+    div.style.top = (bb.bottom + 8) + "px"
+    div.style.left = (bb.right - 32) + "px"
+    
+    drawCalendarPicker(div, seedDate)    
+}
+
+/******************************************
+ Fetched from source/datetime-picker.js
+******************************************/
+
+var datetimes = {};
+
+function validate_datetime(what, slug) {
+  let val = document.getElementById('%s_%s'.format(slug, what)).value;
+  let span = document.getElementById('%s_%s_span'.format(slug, what));
+  if (what == 'tz') {
+    datetimes[slug].tz = val;
+    validate_datetime('start', slug);
+    validate_datetime('stop', slug);
+  }
+  let spaninfo = '';
+  if (what == 'start' || what == 'stop') {
+    let dt = moment.tz(val, datetimes[slug].tz||"Etc/UTC").format();
+    if (val == '' || dt == 'Invalid date') {
+      if (datetimes[slug]['%s_require_valid'.format(what)] === true) {
+        spaninfo = "Invalid date!";
+        span.style.color = 'red';
+      } else {
+        dt = null;
+      }
+    } else {
+      spaninfo = dt;
+      span.style.color = 'green';
+    }
+    span.innerText = spaninfo;
+    datetimes[slug][what] = dt;
+  }
+}
+
+function datetime(slug, options) {
+  // defaults
+  let tz = options.tz || "Etc/UTC";
+  let start = options.start;
+  let stop = options.stop;
+  let title = options.title;
+  let start_require_valid = (options.requireStart === false) ? false: true; //default to true
+  let stop_require_valid = (options.requireStop === false) ? false: true; //default to true
+
+  datetimes[slug] = {
+    tz: tz,
+    start: start,
+    stop: stop,
+    title: title,
+    start_require_valid: start_require_valid,
+    stop_require_valid: stop_require_valid
+  };
+
+  let div = new HTML('div', { style: {textAlign: 'center'}});
+
+  // TZ
+  let tzdiv = new HTML('div', {style: {width: '180px', margin: '1%', position: 'relative', display: 'inline-block'}});
+  tzdiv.inject(new HTML('big', {}, "Timezone:"));
+  tzdiv.inject(br());
+
+  let zones = moment.tz.names();
+  let tzfield = new HTML('select', { id: '%s_tz'.format(slug), type: 'text', style: {width: '160px'}, onchange: 'validate_datetime("tz", "%s");'.format(slug)});
+  tzdiv.inject(tzfield);
+  for (var i = 0; i < zones.length; i++) {
+    let zn = String(zones[i]);
+    let opt = new HTML('option', { selected: zn == (tz || 'Etc/UTC') ? 'selected' : null}, zn);
+    tzfield.inject(opt);
+  }
+  tzdiv.inject(new HTML('span', {style: {display: 'inline-block', textIndent: '24px', width: '100%', height: '20px'},id: '%s_tz_span'.format(slug)}));
+  div.inject(tzdiv);
+
+  // START TIME
+  let stdiv = new HTML('div', {style: {width: '180px', margin: '1%', position: 'relative', display: 'inline-block'}});
+  stdiv.inject(new HTML('big',{},"From:"));
+  stdiv.inject(br());
+  let stfield = new HTML('input', { id: '%s_start'.format(slug), type: 'text', style: {width: '160px'}, value: start, onchange: 'validate_datetime("start", "%s");'.format(slug)});
+  stdiv.inject(stfield);
+  stdiv.inject(new HTML('span', {style: {display: 'inline-block', fontSize: '0.8rem', width: '100%', height: '20px'},id: '%s_start_span'.format(slug)}));
+  //starttimediv.inject(new HTML('p', {style: {padding: '12px'}}, "The date and time when the Call for Papers is open to submissions, relative to the local timezone above."));
+  div.inject(stdiv);
+  flatpickr(stfield, {enableTime: true});
+
+  // STOP TIME
+  let stopdiv = new HTML('div', {style: {width: '180px', margin: '1%', position: 'relative', display: 'inline-block'}});
+  stopdiv.inject(new HTML('big',{}, "To:"));
+  stopdiv.inject(br());
+  let stopfield = new HTML('input', { id: '%s_stop'.format(slug), type: 'text', style: {width: '160px'}, value: stop, onchange: 'validate_datetime("stop", "%s");'.format(slug)});
+  stopdiv.inject(stopfield);
+  stopdiv.inject(new HTML('span', {style: {display: 'inline-block', fontSize: '0.8rem', width: '100%', height: '20px'},id: '%s_stop_span'.format(slug)}));
+  //starttimediv.inject(new HTML('p', {style: {padding: '12px'}}, "The date and time when the Call for Papers is open to submissions, relative to the local timezone above."));
+  div.inject(stopdiv);
+  flatpickr(stopfield, {enableTime: true});
+
+  return div;
+}
+
+
+/******************************************
+ Fetched from source/fail2ban.js
+******************************************/
+
+
+function fail2ban_callback(state, json) {
+    let obj = document.getElementById('results');
+    let resno = json.results.whitelist.length + json.results.banlist.length + json.results.iptables.length;
+    let header = _h2("Found %u results for %s".format(resno, state.source));
+    obj.innerHTML = '';
+    obj.inject(header);
+    
+    // whitelist results?
+    if (json.results.whitelist.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Whitelist results (%u):".format(json.results.whitelist.length));
+        div.inject(wheader);
+        let tbl = new HTML('table');
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', 'Times out'),
+            new HTML('th', 'Description'),
+            new HTML('th', 'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.results.whitelist.length; i++) {
+            let res = json.results.whitelist[i];
+            let timeout = 'never';
+            if (res.timeout) {
+                timeout = moment(res.timeout*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_whitelist('%s');".format(res.rid)}, "Remove whitelisting")
+            ];
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(res.ip)),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+    
+    // banlist results?
+    if (json.results.banlist.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Banlist results (%u):".format(json.results.banlist.length));
+        div.inject(wheader);
+        let tbl = new HTML('table');
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', 'Last updated'),
+            new HTML('th', 'Reason for ban'),
+            new HTML('th', 'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.results.banlist.length; i++) {
+            let res = json.results.banlist[i];
+            let timeout = 'unknown?';
+            if (res.epoch) {
+                timeout = moment(res.epoch*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_banlist('%s');".format(res.rid)}, "Remove ban")
+            ];
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(res.ip)),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+    
+    // iptables stuff?
+    if (json.results.iptables.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        obj.inject(div);
+        let iheader = _h3({class:'subtitle'},"Local iptables results (%u):".format(json.results.iptables.length));
+        div.inject(iheader);
+        
+        if (json.results.iptables.length == 10) {
+            div.inject(_i("We're only showing the first ten results, there may be many more!"));
+        }
+        
+        for (var i = 0; i < json.results.iptables.length; i++) {
+            let res = json.results.iptables[i];
+            let txt = _div("Found on %s, iptables line %u in the %s chain, as %s".format(res.hostname, res.linenumber, res.chain, res.source));
+            let link = new HTML('a', {style: {marginLeft: '10px'}, href:"whitelist.html?%s".format(res.source)}, "Create whitelist rule")
+            div.inject(txt);
+            txt.inject(link);
+            if (res.msg) {
+                txt.inject(br());
+                txt.inject(_i({style: {color: '#963'}}, res.msg));
+                txt.style.marginBottom = '5px';
+            }
+        }
+    }
+}
+
+function old_fail2ban(e) {
+    if (e && e.state && e.state.what == 'search') {
+        search(e.state.source, true);
+    }
+}
+
+function fail2ban(source, nopush) {
+    let obj = document.getElementById('results');
+    
+    let m = source.match(/([a-z0-9:.\/]+)/);
+    if (m) {
+        source = m[0];
+        obj.innerText = "Searching, hang on...";
+        if (!nopush) history.pushState({what: 'search', source: source}, "Search: " + source, '?' + source);
+        window.onpopstate = old_search;
+        let s = document.getElementById('source');
+        if (s) s.value = source;
+        POST('./api/fail2ban', fail2ban_callback, {source: source}, manage_error, {source: source});
+    } else if (source.length > 0) {
+        alert("Invalid IP or CIDR notation entered!")
+    }
+    return false
+}
+
+
+
+/******************************************
+ Fetched from source/ruleset.js
+******************************************/
+
+
+var rule_json = {}
+
+function create_rule_form(form, rid) {
+  if (!rid || rid == '') {
+    rid = 'new';
+    rule_json['new'] = {
+      name: '',
+      type: '',
+      span: 24,
+      limit: '',
+      query: [],
+    }
+  }
+  let n = _input({type: 'text', style: {width: '300px'}, id: "%s_name".format(rid), value: rule_json[rid].name});
+  form.inject("Name of rule: ");
+  form.inject(n);
+  form.inject(br());
+  
+  // type
+  let s = _select({id: "%s_type".format(rid)});
+  s.inject(_option({value: 'httpd_traffic', selected: rule_json[rid].type == 'httpd_traffic' ? 'selected' : null}, 'HTTPd traffic (bytes)'));
+  s.inject(_option({value: 'httpd_visits', selected: rule_json[rid].type == 'httpd_visits' ? 'selected' : null}, 'HTTPd visits (requests)'));
+  form.inject("Type of rule: ");
+  form.inject(s);
+  form.inject(br());
+  
+  // span
+  let sp = _select({id: "%s_span".format(rid)});
+  sp.inject(_option({value: 1, selected: rule_json[rid].span == 1 ? 'selected' : null}, '1 hour'));
+  sp.inject(_option({value: 6, selected: rule_json[rid].span == 6 ? 'selected' : null}, '6 hours'));
+  sp.inject(_option({value: 12, selected: rule_json[rid].span == 12 ? 'selected' : null}, '12 hours'));
+  sp.inject(_option({value: 24, selected: rule_json[rid].span == 24 ? 'selected' : null}, '24 hours'));
+  sp.inject(_option({value: (24*7), selected: rule_json[rid].span == (24*7) ? 'selected' : null}, 'one week'));
+  sp.inject(_option({value: (24*30), selected: rule_json[rid].span == (24*30) ? 'selected' : null}, 'one month'));
+  
+  form.inject("Time span: ");
+  form.inject(sp);
+  form.inject(br());
+  
+  let l = _input({type: 'number', style: {width: '120px'}, id: "%s_limit".format(rid), value: rule_json[rid].limit || 0});
+  form.inject("Traffic/Request limit: ");
+  form.inject(l);
+  form.inject(br());
+  
+  let q = _textarea({style: {width: '300px', height: '120px'},id: "%s_query".format(rid)}, rule_json[rid].query.join("\n"));
+  form.inject("Query parameters: ");
+  form.inject(q);
+  form.inject(br());
+  
+  form.inject(_input({type: 'submit', value: 'Save rule'}))
+  
+}
+
+function list_rules(state, json) {
+    let obj = document.getElementById('rules');
+    obj.innerHTML = ''; // clear object
+    if (json.rules && json.rules.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Current ban rules (%u):".format(json.rules.length));
+        div.inject(wheader);
+        let tbl = new HTML('table', { style: {fontSize: '0.8rem'}});
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Ruleset'),
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.rules.length; i++) {
+            let res = json.rules[i];
+            rule_json[res.rid] = res;
+            let innards = _div();
+            
+            let form = _form({onsubmit: "add_rule('%s'); return false;".format(res.rid)});
+            create_rule_form(form, res.rid);
+            innards.inject(form);
+
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {style: {padding: '5px'}}, [
+                                     _a({href: 'javascript:void();', onclick:"showrule('%s');".format(res.rid)}, _kbd(res.name)),
+                                     "   ",
+                                     _a({style: {color: '#930', float: 'right'}, href: 'javascript:void();', onclick:"remove_rule('%s');".format(res.rid)}, "Remove ruleset"),
+                                     _div({id: res.rid, style: {margin: '6px', background: '#3692', border: '1.5px  solid #3339', padding: '3px', display: 'none'}}, innards)
+                  ])
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        
+        obj.inject(div);
+        
+        obj.inject(_hr());
+        obj.inject(_h3("Create a new rule:"));
+        let form = _form({onsubmit: "add_rule('new'); return false;"});
+        create_rule_form(form, '');
+        obj.inject(form);
+        
+    }
+}
+
+function showrule(rid) {
+  let obj = document.getElementById(rid)
+  obj.style.display = obj.style.display == 'none' ? 'block' : 'none';
+}
+
+function init_rules(source) {
+    let obj = document.getElementById('rules');
+    obj.innerText = "Fetching rules, hang on...";
+    GET('./api/rules', list_rules, manage_error, {});
+}
+
+function remove_rule(rid) {
+    DELETE('./api/rules', rule_removed, {rid: rid}, manage_error, {rid: rid});
+}
+
+function rule_added(state, json) {
+  alert("Rule entry added!");
+  location.reload();
+}
+
+function rule_removed(state, json) {
+  alert("Rule entry removed!");
+  location.reload();
+}
+
+function add_rule(rid) {
+  let name = document.getElementById('%s_name'.format(rid)).value;
+  let rtype = document.getElementById('%s_type'.format(rid)).value;
+  let span = parseInt(document.getElementById('%s_span'.format(rid)).value);
+  let limit = parseInt(document.getElementById('%s_limit'.format(rid)).value);
+  let query = document.getElementById('%s_query'.format(rid)).value.split(/\r?\n/);
+  
+  let entry = {
+    name: name,
+    type: rtype,
+    span: span,
+    limit: limit,
+    query: query
+  }
+  if (rid && rid != 'new') {
+    entry.rid = rid
+  }
+  if (span > 0 && limit > 0 && query.length > 0) {
+      PUT('./api/rules', rule_added, {}, manage_error, entry);
+  } else {
+    alert("Span, query lines, and limits must be greater than 0!");
+  }
+  return false
+}
+
+
+/******************************************
+ Fetched from source/scaffolding-html.js
+******************************************/
+
+/**
+ * HTML: DOM creator class
+ * args:
+ * - type: HTML element type (div, table, p etc) to produce
+ * - params: hash of element params to add (class, style etc)
+ * - children: optional child or children objects to insert into the new element
+ * Example:
+ * div = new HTML('div', {
+ *    class: "footer",
+ *    style: {
+ *        fontWeight: "bold"
+ *    }
+#}, "Some text inside a div")
+ */
+
+var txt = (msg) => document.createTextNode(msg);
+
+var HTML = (function() {
+  function HTML(type, params, children) {
+
+    /* create the raw element, or clone if passed an existing element */
+    var child, j, len, val;
+    if (typeof type === 'object') {
+      this.element = type.cloneNode();
+    } else {
+      this.element = document.createElement(type);
+    }
+
+    /* If params have been passed, set them */
+    if (isHash(params)) {
+      for (var key in params) {
+        val = params[key];
+
+        /* Standard string value? */
+        if (typeof val === "string" || typeof val === 'number') {
+          this.element.setAttribute(key, val);
+        } else if (isArray(val)) {
+
+          /* Are we passing a list of data to set? concatenate then */
+          this.element.setAttribute(key, val.join(" "));
+        } else if (isHash(val)) {
+
+          /* Are we trying to set multiple sub elements, like a style? */
+          for (var subkey in val) {
+            let subval = val[subkey];
+            if (!this.element[key]) {
+              throw "No such attribute, " + key + "!";
+            }
+            this.element[key][subkey] = subval;
+          }
+        }
+      }
+    } else {
+      if (!children) { children = params } // shortcut!
+    }
+
+    /* If any children have been passed, add them to the element */
+    if (children) {
+
+      /* If string, convert to textNode using txt() */
+      if (typeof children === "string") {
+        this.element.inject(txt(children));
+      } else {
+
+        /* If children is an array of elems, iterate and add */
+        if (isArray(children)) {
+          for (j = 0, len = children.length; j < len; j++) {
+            child = children[j];
+
+            /* String? Convert via txt() then */
+            if (typeof child === "string") {
+              this.element.inject(txt(child));
+            } else {
+
+              /* Plain element, add normally */
+              this.element.inject(child);
+            }
+          }
+        } else {
+
+          /* Just a single element, add it */
+          this.element.inject(children);
+        }
+      }
+    }
+    return this.element;
+  }
+
+  return HTML;
+
+})();
+
+/**
+ * prototype injector for HTML elements:
+ * Example: mydiv.inject(otherdiv)
+ */
+
+HTMLElement.prototype.inject = function(child) {
+  var item, j, len;
+  if (isArray(child)) {
+    for (j = 0, len = child.length; j < len; j++) {
+      item = child[j];
+      if (typeof item === 'string') {
+        item = txt(item);
+      }
+      this.appendChild(item);
+    }
+  } else {
+    if (typeof child === 'string') {
+      child = txt(child);
+    }
+    this.appendChild(child);
+  }
+  return child;
+};
+
+
+
+/**
+ * prototype for emptying an html element
+ */
+
+HTMLElement.prototype.empty = function() {
+  var ndiv;
+  ndiv = this.cloneNode();
+  this.parentNode.replaceChild(ndiv, this);
+  return ndiv;
+};
+
+function toggleView(id) {
+  let obj = document.getElementById(id);
+  if (obj) {
+    obj.style.display = (obj.style.display == 'block') ? 'none' : 'block';
+  }
+}
+
+function br() {
+  return new HTML('br');
+}
+
+// construction shortcuts for various elements
+let _a = (a,b) => new HTML('a', a,b);
+let _b = (a,b) => new HTML('b', a,b);
+let _p = (a,b) => new HTML('p', a,b);
+let _i = (a,b) => new HTML('i', a, b);
+let _div = (a,b) => new HTML('div', a, b);
+let _input = (a,b) => new HTML('input', a, b);
+let _select = (a,b) => new HTML('select', a, b);
+let _option = (a,b) => new HTML('option', a, b);
+let _h1 = (a,b) => new HTML('h1', a, b);
+let _h2 = (a,b) => new HTML('h2', a, b);
+let _h3 = (a,b) => new HTML('h3', a, b);
+let _h4 = (a,b) => new HTML('h4', a, b);
+let _h5 = (a,b) => new HTML('h5', a, b);
+let _kbd = (a,b) => new HTML('kbd', a, b);
+let _pre = (a,b) => new HTML('pre', a, b);
+let _form = (a,b) => new HTML('form', a, b);
+let _hr = (a,b) => new HTML('hr', a, b);
+let _span = (a,b) => new HTML('span', a, b);
+let _textarea = (a,b) => new HTML('textarea', a, b);
+let _get = (a) => document.getElementById(a);
+
+
+function billitem(a,b,c, total) {
+  let d = _div({style: {position: 'relative', display: 'block'}});
+  if (total) { d.style.fontWeight = 'bold'; d.style.borderTop = '2px solid #444'; }
+  let da = _div({style: {position: 'relative', width: '600px', display: 'inline-block'}}, a);
+  let db = _div({style: {position: 'relative', width: '60px', display: 'inline-block'}}, b);
+  let dc = _div({style: {position: 'relative', width: '80px', textAlign: 'right', display: 'inline-block'}}, c);
+  d.inject(da);
+  d.inject(db);
+  d.inject(dc);
+  return d;
+}
+
+
+// Generic modal function
+function modal(title, msg, type, isHTML) {
+    let modal = document.getElementById('modal');
+    let text = document.getElementById('modal_text');
+    if (modal == undefined) {
+        text = new HTML('p', {id: 'modal_text'}, "");
+        modal = new HTML('div', { id: 'modal'}, [
+            new HTML('div', {id: 'modal_content'}, [
+                    new HTML('span', {id: 'modal_close', onclick: 'document.getElementById("modal").style.display = "none";'}, 'X'),
+                    new HTML('h2', {id: 'modal_title'}, title),
+                    new HTML('div', {}, text)
+                    ])
+            ]);
+        document.body.appendChild(modal);
+
+    }
+    if (type) {
+        modal.setAttribute("class", "modal_" + type);
+    } else {
+        modal.setAttribute("class", undefined);
+    }
+    modal.style.display = 'block';
+    document.getElementById('modal_title').innerText = title;
+    // If we trust HTML, use it. Otherwise only show as textNode.
+    if (isHTML) {
+        text.innerHTML = msg;
+    } else {
+        msg = (typeof(msg) == "string") ? msg : "An internal browser error occurred.";
+        msg = msg.replace(/<.*?>/g, ""); // strip HTML tags
+        text.innerText = msg;
+    }
+}
+
+/******************************************
+ Fetched from source/search.js
+******************************************/
+
+function ban_removed(state, json) {
+    alert("Ban removed! IP whitelisted for one hour to flush bans.");
+    location.reload();
+}
+
+function remove_banlist(rid) {
+    DELETE('./api/bans', ban_removed, {rule: rid}, manage_error, {rule: rid});
+}
+
+function whitelist_removed(state, json) {
+    alert("Whitelisting removed!");
+    location.reload();
+}
+
+function remove_whitelist(rid) {
+    DELETE('./api/whitelist', whitelist_removed, {rule: rid}, manage_error, {rule: rid});
+}
+
+function search_callback(state, json) {
+    let obj = document.getElementById('results');
+    let resno = json.results.whitelist.length + json.results.banlist.length + json.results.iptables.length;
+    let header = _h2("Found %u results".format(resno));
+    obj.innerHTML = '';
+    obj.inject(header);
+    
+    // whitelist results?
+    if (json.results.whitelist.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Whitelist results (%u):".format(json.results.whitelist.length));
+        div.inject(wheader);
+        let tbl = new HTML('table');
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', 'Times out'),
+            new HTML('th', 'Description'),
+            new HTML('th', 'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.results.whitelist.length; i++) {
+            let res = json.results.whitelist[i];
+            let timeout = 'never';
+            if (res.timeout) {
+                timeout = moment(res.timeout*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_whitelist('%s');".format(res.rid)}, "Remove whitelisting")
+            ];
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(res.ip)),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+    
+    // banlist results?
+    if (json.results.banlist.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Banlist results (%u):".format(json.results.banlist.length));
+        div.inject(wheader);
+        let tbl = new HTML('table');
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', 'Last updated'),
+            new HTML('th', 'Reason for ban'),
+            new HTML('th', 'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.results.banlist.length; i++) {
+            let res = json.results.banlist[i];
+            let timeout = 'unknown?';
+            if (res.epoch) {
+                timeout = moment(res.epoch*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_banlist('%s');".format(res.rid)}, "Remove ban")
+            ];
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(res.ip)),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+    
+    // iptables stuff?
+    if (json.results.iptables.length > 0) {
+        let div = _div();
+        div.inject(_hr());
+        obj.inject(div);
+        let iheader = _h3({class:'subtitle'},"Local iptables results (%u):".format(json.results.iptables.length));
+        div.inject(iheader);
+        
+        if (json.results.iptables.length == 10) {
+            div.inject(_i("We're only showing the first ten results, there may be many more!"));
+        }
+        
+        for (var i = 0; i < json.results.iptables.length; i++) {
+            let res = json.results.iptables[i];
+            let txt = _div("Found on %s, iptables line %u in the %s chain, as %s".format(res.hostname, res.linenumber, res.chain, res.source));
+            let link = new HTML('a', {style: {marginLeft: '10px'}, href:"whitelist.html?%s".format(res.source)}, "Create whitelist rule")
+            div.inject(txt);
+            txt.inject(link);
+        }
+    }
+}
+
+function old_search(e) {
+    if (e && e.state && e.state.what == 'search') {
+        search(e.state.source, true);
+    }
+}
+
+function search(source, nopush) {
+    let obj = document.getElementById('results');
+    
+    let m = source.match(/([a-f0-9:.\/]+)/);
+    if (m) {
+        source = m[0];
+        obj.innerText = "Searching, hang on...";
+        if (!nopush) history.pushState({what: 'search', source: source}, "Search: " + source, '?' + source);
+        window.onpopstate = old_search;
+        let s = document.getElementById('source');
+        if (s) s.value = source;
+        POST('./api/search', search_callback, {source: source}, manage_error, {source: source});
+    } else if (source.length > 0) {
+        alert("Invalid IP or CIDR notation entered!")
+    }
+    return false
+}
+
+
+
+/******************************************
+ Fetched from source/whitelist.js
+******************************************/
+
+
+function list_whites(state, json) {
+    let obj = document.getElementById('whitelist');
+    obj.innerHTML = ''; // clear object
+    if (json.whitelist && json.whitelist.length > 0) {
+        json.whitelist = json.whitelist.sort((a,b) => b.epoch - a.epoch);
+        let div = _div();
+        div.inject(_hr());
+        let wheader = _h3({class:'subtitle'},"Current whitelist entries (%u):".format(json.whitelist.length));
+        div.inject(wheader);
+        let tbl = new HTML('table', { style: {fontSize: '0.8rem'}});
+        let tbh = new HTML('thead');
+        let tbody = new HTML('tbody');
+        
+        tbh.inject(new HTML('tr', {}, [
+            new HTML('th', 'Source'),
+            new HTML('th', {style: {width: '110px'}},'Added'),
+            new HTML('th', {style: {width: '110px'}},'Times out'),
+            new HTML('th', 'Reason for whitelisting'),
+            new HTML('th', {style: {width: '140px'}},'Actions')
+        ]));
+        tbl.inject(tbh);
+        
+        div.inject(tbl);
+        for (var i = 0; i < json.whitelist.length; i++) {
+            let res = json.whitelist[i];
+            let timeout = 'Never';
+            if (res.timeout) {
+                timeout = moment(res.timeout*1000.0).fromNow();
+            }
+            let actions = [
+                new HTML('a', {href:'javascript:void();', onclick: "remove_whitelist('%s');".format(res.rid)}, "Remove whitelisting")
+            ];
+            let name = res.ip;
+            if (res.dns && res.dns.length > 0) {
+                let m = res.dns.match(/([^.]+\.[^.]+)$/);
+                lastbit = m ? m[1] : '';
+                name = "%s (%s)".format(res.ip, lastbit);
+            }
+            let tr = new HTML('tr', {}, [
+                new HTML('td', {}, _kbd(name)),
+                new HTML('td', moment(res.epoch*1000.0).fromNow()),
+                new HTML('td', timeout),
+                new HTML('td', res.reason),
+                new HTML('td', {}, actions)
+            ]);
+            tbody.inject(tr);
+        }
+        tbl.inject(tbody);
+        obj.inject(div);
+    }
+}
+
+
+function init_whitelist(source) {
+    let obj = document.getElementById('whitelist');
+    obj.innerText = "Fetching whitelist, hang on...";
+    GET('./api/whitelist', list_whites, manage_error, {});
+    
+    if (source && source.length > 0) {
+      document.getElementById('source').value = source;
+      document.getElementById('force').checked = true;
+      document.getElementById('reason').value = "Manual whitelist for 1 hour to unban from iptables.";
+      document.getElementById('timeout').value = parseInt((new Date().getTime()/1000) + 3600);
+    }
+}
+
+function whitelist_added(state, json) {
+  alert("Whitelist entry added!");
+  location.reload();
+}
+
+
+function add_whitelist() {
+    let source = document.getElementById('source').value;
+    let target = document.getElementById('target').value;
+    let reason = document.getElementById('reason').value;
+    let timeout = document.getElementById('timeout').value;
+    let force = document.getElementById('force').checked;
+    if (timeout != 'never') {
+      timeout = parseInt(timeout);
+    } else {
+      timeout = 0;
+    }
+    
+    let m = source.match(/([a-f0-9:.\/]+)/);
+    if (m) {
+        source = m[0];
+        PUT('./api/whitelist', whitelist_added, {}, manage_error, {
+          source: source,
+          target: target,
+          reason: reason,
+          timeout: timeout,
+          force: force
+          });
+    } else {
+      alert("Invalid source address entered!");
+    }
+    return false
+}
diff --git a/server/site/js/c3.js b/server/site/js/c3.js
new file mode 100644
index 0000000..2db23b1
--- /dev/null
+++ b/server/site/js/c3.js
@@ -0,0 +1,2 @@
+/* @license C3.js v0.5.3 | (c) C3 Team and other contributors | http://c3js.org/ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.c3=e()}(this,function(){"use strict";var t,e,i={target:"c3-target",chart:"c3-chart",chartLine:"c3-chart-line",chartLines:"c3-chart-lines",chartBar:"c3-chart-bar",chartBars:"c3-chart-bars",chartText:"c3-chart-text",chartTexts:"c3-chart-texts",chartArc:"c3-chart-arc",chartArcs:"c3-chart-arcs",chartArcsTitle:"c3-chart-arcs-title",chartArcsBackground:"c3-chart-arcs-background",chartArcsGaugeUnit:"c3-chart-arcs-gauge-unit",chartArcsGaugeMax:"c3-chart-arcs-gauge-max",chartArcsGaugeMin:"c3-chart-arcs-gauge-min",selectedCircle:"c3-selected-circle",selectedCircles:"c3-selected-circles",eventRect:"c3-event-rect",eventRects:"c3-event-rects",eventRectsSingle:"c3-event-rects-single",eventRectsMultiple:"c3-event-rects-multiple",zoomRect:"c3-zoom-rect",brush:"c3-brush",focused:"c3-focused",defocused:"c3-defocused",region:"c3-region",regions:"c3-regions",title:"c3-title",tooltipContainer:"c3-tooltip-container",tooltip:"c3-tooltip",tooltipName:"c3-tooltip-name",shape:"c3-shape",shapes:"c3-shapes",line:"c3-line",lines:"c3-lines",bar:"c3-bar",bars:"c3-bars",circle:"c3-circle",circles:"c3-circles",arc:"c3-arc",arcLabelLine:"c3-arc-label-line",arcs:"c3-arcs",area:"c3-area",areas:"c3-areas",empty:"c3-empty",text:"c3-text",texts:"c3-texts",gaugeValue:"c3-gauge-value",grid:"c3-grid",gridLines:"c3-grid-lines",xgrid:"c3-xgrid",xgrids:"c3-xgrids",xgridLine:"c3-xgrid-line",xgridLines:"c3-xgrid-lines",xgridFocus:"c3-xgrid-focus",ygrid:"c3-ygrid",ygrids:"c3-ygrids",ygridLine:"c3-ygrid-line",ygridLines:"c3-ygrid-lines",axis:"c3-axis",axisX:"c3-axis-x",axisXLabel:"c3-axis-x-label",axisY:"c3-axis-y",axisYLabel:"c3-axis-y-label",axisY2:"c3-axis-y2",axisY2Label:"c3-axis-y2-label",legendBackground:"c3-legend-background",legendItem:"c3-legend-item",legendItemEvent:"c3-legend-item-event",legendItemTile:"c3-legend-item-tile",legendItemHidden:"c3-legend-item-hidden",legendItemFocused:"c3-legend-item-focused",dragarea:"c3-dragarea",EXPANDED:"_expanded_",SELECTED:"_selected_",INCLUDED:"_included_"},n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},a=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},r=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e},s=function(t){return t||0===t},o=function(t){return"function"==typeof t},c=function(t){return Array.isArray(t)},d=function(t){return"string"==typeof t},u=function(t){return void 0===t},l=function(t){return void 0!==t},h=function(t){return 10*Math.ceil(t/10)},g=function(t){return Math.ceil(t)+.5},f=function(t){return t[1]-t[0]},p=function(t){return null==t||d(t)&&0===t.length||"object"===(void 0===t?"undefined":n(t))&&0===Object.keys(t).length},_=function(t){return!T.isEmpty(t)},x=function(t,e,i){return l(t[e])?t[e]:i},y=function(t,e){var i=!1;return Object.keys(t).forEach(function(n){t[n]===e&&(i=!0)}),i},m=function(t){return"string"==typeof t?t.replace(/</g,"&lt;").replace(/>/g,"&gt;"):t},S=function(t){var e=t.getBoundingClientRect(),i=[t.pathSegList.getItem(0),t.pathSegList.getItem(1)];return{x:i[0].x,y:Math.min(i[0].y,i[1].y),width:e.width,height:e.height}};function w(t,e){var i=this;i.component=t,i.params=e||{},i.d3=t.d3,i.scale=i.d3.scaleLinear(),i.range,i.orient="bottom",i.innerTickSize=6,i.outerTickSize=this.params.withOuterTick?6:0,i.tickPadding=3,i.tickValues=null,i.tickFormat,i.tickArguments,i.tickOffset=0,i.tickCulling=!0,i.tickCentered,i.tickTextCharSize,i.tickTextRotate=i.params.tickTextRotate,i.tickLength,i.axis=i.generateAxis()}(e=w.prototype).axisX=function(t,e,i){t.attr("transform",function(t){return"translate("+Math.ceil(e(t)+i)+", 0)"})},e.axisY=function(t,e){t.attr("transform",function(t){return"translate(0,"+Math.ceil(e(t))+")"})},e.scaleExtent=function(t){var e=t[0],i=t[t.length-1];return e<i?[e,i]:[i,e]},e.generateTicks=function(t){var e,i,n=[];if(t.ticks)return t.ticks.apply(t,this.tickArguments);for(i=t.domain(),e=Math.ceil(i[0]);e<i[1];e++)n.push(e);return n.length>0&&n[0]>0&&n.unshift(n[0]-(n[1]-n[0])),n},e.copyScale=function(){var t,e=this.scale.copy();return this.params.isCategory&&(t=this.scale.domain(),e.domain([t[0],t[1]-1])),e},e.textFormatted=function(t){var e=this.tickFormat?this.tickFormat(t):t;return void 0!==e?e:""},e.updateRange=function(){var t=this;return t.range=t.scale.rangeExtent?t.scale.rangeExtent():t.scaleExtent(t.scale.range()),t.range},e.updateTickTextCharSize=function(t){var e=this;if(e.tickTextCharSize)return e.tickTextCharSize;var i={h:11.5,w:5.5};return t.select("text").text(function(t){return e.textFormatted(t)}).each(function(t){var n=this.getBoundingClientRect(),a=e.textFormatted(t),r=n.height,s=a?n.width/a.length:void 0;r&&s&&(i.h=r,i.w=s)}).text(""),e.tickTextCharSize=i,i},e.isVertical=function(){return"left"===this.orient||"right"===this.orient},e.tspanData=function(t,e,i){var n=this.params.tickMultiline?this.splitTickText(t,i):[].concat(this.textFormatted(t));return n.map(function(t){return{index:e,splitted:t,length:n.length}})},e.splitTickText=function(t,e){var i,n,a,r=this,s=r.textFormatted(t),o=r.params.tickWidth;if("[object Array]"===Object.prototype.toString.call(s))return s;return(!o||o<=0)&&(o=r.isVertical()?95:r.params.isCategory?Math.ceil(e(1)-e(0))-12:110),function t(e,s){n=void 0;for(var c=1;c<s.length;c++)if(" "===s.charAt(c)&&(n=c),i=s.substr(0,c+1),a=r.tickTextCharSize.w*i.length,o<a)return t(e.concat(s.substr(0,n||c)),s.slice(n?n+1:c));return e.concat(s)}([],s+"")},e.updateTickLength=function(){this.tickLength=Math.max(this.innerTickSize,0)+this.tickPadding},e.lineY2=function(t){var e=this,i=e.scale(t)+(e.tickCentered?0:e.tickOffset);return e.range[0]<i&&i<e.range[1]?e.innerTickSize:0},e.textY=function(){var t=this.tickTextRotate;return t?11.5-t/15*2.5*(t>0?1:-1):this.tickLength},e.textTransform=function(){var t=this.tickTextRotate;return t?"rotate("+t+")":""},e.textTextAnchor=function(){var t=this.tickTextRotate;return t?t>0?"start":"end":"middle"},e.tspanDx=function(){var t=this.tickTextRotate;return t?8*Math.sin(Math.PI*(t/180)):0},e.tspanDy=function(t,e){var i=this.tickTextCharSize.h;return 0===e&&(i=this.isVertical()?-((t.length-1)*(this.tickTextCharSize.h/2)-3):".71em"),i},e.generateAxis=function(){var t=this,e=t.d3,i=t.params;function n(a,r){var s;return a.each(function(){var a,o,c,d=n.g=e.select(this),u=this.__chart__||t.scale,l=this.__chart__=t.copyScale(),h=t.tickValues?t.tickValues:t.generateTicks(l),g=d.selectAll(".tick").data(h,l),f=g.enter().insert("g",".domain").attr("class","tick").style("opacity",1e-6),p=g.exit().remove(),_=g.merge(f);i.isCategory?(t.tickOffset=Math.ceil((l(1)-l(0))/2),o=t.tickCentered?0:t.tickOffset,c=t.tickCentered?t.tickOffset:0):t.tickOffset=o=0,t.updateRange(),t.updateTickLength(),t.updateTickTextCharSize(d.select(".tick"));var x=_.select("line").merge(f.append("line")),y=_.select("text").merge(f.append("text")),m=_.selectAll("text").selectAll("tspan").data(function(e,i){return t.tspanData(e,i,l)}),S=m.enter().append("tspan").merge(m).text(function(t){return t.splitted});m.exit().remove();var w=d.selectAll(".domain").data([0]),v=w.enter().append("path").merge(w).attr("class","domain");switch(t.orient){case"bottom":a=t.axisX,x.attr("x1",o).attr("x2",o).attr("y2",function(e,i){return t.lineY2(e,i)}),y.attr("x",0).attr("y",function(e,i){return t.textY(e,i)}).attr("transform",function(e,i){return t.textTransform(e,i)}).style("text-anchor",function(e,i){return t.textTextAnchor(e,i)}),S.attr("x",0).attr("dy",function(e,i){return t.tspanDy(e,i)}).attr("dx",function(e,i){return t.tspanDx(e,i)}),v.attr("d","M"+t.range[0]+","+t.outerTickSize+"V0H"+t.range[1]+"V"+t.outerTickSize);break;case"top":a=t.axisX,x.attr("x1",o).attr("x2",o).attr("y2",function(e,i){return-1*t.lineY2(e,i)}),y.attr("x",0).attr("y",function(e,n){return-1*t.textY(e,n)-(i.isCategory?2:t.tickLength-2)}).attr("transform",function(e,i){return t.textTransform(e,i)}).style("text-anchor",function(e,i){return t.textTextAnchor(e,i)}),S.attr("x",0).attr("dy",function(e,i){return t.tspanDy(e,i)}).attr("dx",function(e,i){return t.tspanDx(e,i)}),v.attr("d","M"+t.range[0]+","+-t.outerTickSize+"V0H"+t.range[1]+"V"+-t.outerTickSize);break;case"left":a=t.axisY,x.attr("x2",-t.innerTickSize).attr("y1",c).attr("y2",c),y.attr("x",-t.tickLength).attr("y",t.tickOffset).style("text-anchor","end"),S.attr("x",-t.tickLength).attr("dy",function(e,i){return t.tspanDy(e,i)}),v.attr("d","M"+-t.outerTickSize+","+t.range[0]+"H0V"+t.range[1]+"H"+-t.outerTickSize);break;case"right":a=t.axisY,x.attr("x2",t.innerTickSize).attr("y1",c).attr("y2",c),y.attr("x",t.tickLength).attr("y",t.tickOffset).style("text-anchor","start"),S.attr("x",t.tickLength).attr("dy",function(e,i){return t.tspanDy(e,i)}),v.attr("d","M"+t.outerTickSize+","+t.range[0]+"H0V"+t.range[1]+"H"+t.outerTickSize)}if(l.rangeBand){var b=l,T=b.rangeBand()/2;u=l=function(t){return b(t)+T}}else u.rangeBand?u=l:p.call(a,l,t.tickOffset);f.call(a,u,t.tickOffset),s=(r?_.transition(r):_).style("opacity",1).call(a,l,t.tickOffset)}),s}return n.scale=function(e){return arguments.length?(t.scale=e,n):t.scale},n.orient=function(e){return arguments.length?(t.orient=e in{top:1,right:1,bottom:1,left:1}?e+"":"bottom",n):t.orient},n.tickFormat=function(e){return arguments.length?(t.tickFormat=e,n):t.tickFormat},n.tickCentered=function(e){return arguments.length?(t.tickCentered=e,n):t.tickCentered},n.tickOffset=function(){return t.tickOffset},n.tickInterval=function(){var e;return(e=i.isCategory?2*t.tickOffset:(n.g.select("path.domain").node().getTotalLength()-2*t.outerTickSize)/n.g.selectAll("line").size())===1/0?0:e},n.ticks=function(){return arguments.length?(t.tickArguments=arguments,n):t.tickArguments},n.tickCulling=function(e){return arguments.length?(t.tickCulling=e,n):t.tickCulling},n.tickValues=function(e){if("function"==typeof e)t.tickValues=function(){return e(t.scale.domain())};else{if(!arguments.length)return t.tickValues;t.tickValues=e}return n},n};var v=function(i){function n(i){a(this,n);var s={fn:t,internal:{fn:e}},o=r(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,i,"axis",s));return o.d3=i.d3,o.internal=w,o}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(n,P),n}();(t=v.prototype).init=function(){var t=this.owner,e=t.config,n=t.main;t.axes.x=n.append("g").attr("class",i.axis+" "+i.axisX).attr("clip-path",e.axis_x_inner?"":t.clipPathForXAxis).attr("transform",t.getTranslate("x")).style("visibility",e.axis_x_show?"visible":"hidden"),t.axes.x.append("text").attr("class",i.axisXLabel).attr("transform",e.axis_rotated?"rotate(-90)":"").style("text-anchor",this.textAnchorForXAxisLabel.bind(this)),t.axes.y=n.append("g").attr("class",i.axis+" "+i.axisY).attr("clip-path",e.axis_y_inner?"":t.clipPathForYAxis).attr("transform",t.getTranslate("y")).style("visibility",e.axis_y_show?"visible":"hidden"),t.axes.y.append("text").attr("class",i.axisYLabel).attr("transform",e.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForYAxisLabel.bind(this)),t.axes.y2=n.append("g").attr("class",i.axis+" "+i.axisY2).attr("transform",t.getTranslate("y2")).style("visibility",e.axis_y2_show?"visible":"hidden"),t.axes.y2.append("text").attr("class",i.axisY2Label).attr("transform",e.axis_rotated?"":"rotate(-90)").style("text-anchor",this.textAnchorForY2AxisLabel.bind(this))},t.getXAxis=function(t,e,i,n,a,r,s){var o=this.owner,c=o.config,d={isCategory:o.isCategorized(),withOuterTick:a,tickMultiline:c.axis_x_tick_multiline,tickWidth:c.axis_x_tick_width,tickTextRotate:s?0:c.axis_x_tick_rotate,withoutTransition:r},u=new this.internal(this,d).axis.scale(t).orient(e);return o.isTimeSeries()&&n&&"function"!=typeof n&&(n=n.map(function(t){return o.parseDate(t)})),u.tickFormat(i).tickValues(n),o.isCategorized()&&(u.tickCentered(c.axis_x_tick_centered),p(c.axis_x_tick_culling)&&(c.axis_x_tick_culling=!1)),u},t.updateXAxisTickValues=function(t,e){var i,n=this.owner,a=n.config;return(a.axis_x_tick_fit||a.axis_x_tick_count)&&(i=this.generateTickValues(n.mapTargetsToUniqueXs(t),a.axis_x_tick_count,n.isTimeSeries())),e?e.tickValues(i):(n.xAxis.tickValues(i),n.subXAxis.tickValues(i)),i},t.getYAxis=function(t,e,i,n,a,r,s){var o=this.owner,c=o.config,d={withOuterTick:a,withoutTransition:r,tickTextRotate:s?0:c.axis_y_tick_rotate},u=new this.internal(this,d).axis.scale(t).orient(e).tickFormat(i);return o.isTimeSeriesY()?u.ticks(c.axis_y_tick_time_type,c.axis_y_tick_time_interval):u.tickValues(n),u},t.getId=function(t){var e=this.owner.config;return t in e.data_axes?e.data_axes[t]:"y"},t.getXAxisTickFormat=function(){var t=this.owner,e=t.config,i=t.isTimeSeries()?t.defaultAxisTimeFormat:t.isCategorized()?t.categoryName:function(t){return t<0?t.toFixed(0):t};return e.axis_x_tick_format&&(o(e.axis_x_tick_format)?i=e.axis_x_tick_format:t.isTimeSeries()&&(i=function(i){return i?t.axisTimeFormat(e.axis_x_tick_format)(i):""})),o(i)?function(e){return i.call(t,e)}:i},t.getTickValues=function(t,e){return t||(e?e.tickValues():void 0)},t.getXAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_x_tick_values,this.owner.xAxis)},t.getYAxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y_tick_values,this.owner.yAxis)},t.getY2AxisTickValues=function(){return this.getTickValues(this.owner.config.axis_y2_tick_values,this.owner.y2Axis)},t.getLabelOptionByAxisId=function(t){var e,i=this.owner.config;return"y"===t?e=i.axis_y_label:"y2"===t?e=i.axis_y2_label:"x"===t&&(e=i.axis_x_label),e},t.getLabelText=function(t){var e=this.getLabelOptionByAxisId(t);return d(e)?e:e?e.text:null},t.setLabelText=function(t,e){var i=this.owner.config,n=this.getLabelOptionByAxisId(t);d(n)?"y"===t?i.axis_y_label=e:"y2"===t?i.axis_y2_label=e:"x"===t&&(i.axis_x_label=e):n&&(n.text=e)},t.getLabelPosition=function(t,e){var i=this.getLabelOptionByAxisId(t),a=i&&"object"===(void 0===i?"undefined":n(i))&&i.position?i.position:e;return{isInner:a.indexOf("inner")>=0,isOuter:a.indexOf("outer")>=0,isLeft:a.indexOf("left")>=0,isCenter:a.indexOf("center")>=0,isRight:a.indexOf("right")>=0,isTop:a.indexOf("top")>=0,isMiddle:a.indexOf("middle")>=0,isBottom:a.indexOf("bottom")>=0}},t.getXAxisLabelPosition=function(){return this.getLabelPosition("x",this.owner.config.axis_rotated?"inner-top":"inner-right")},t.getYAxisLabelPosition=function(){return this.getLabelPosition("y",this.owner.config.axis_rotated?"inner-right":"inner-top")},t.getY2AxisLabelPosition=function(){return this.getLabelPosition("y2",this.owner.config.axis_rotated?"inner-right":"inner-top")},t.getLabelPositionById=function(t){return"y2"===t?this.getY2AxisLabelPosition():"y"===t?this.getYAxisLabelPosition():this.getXAxisLabelPosition()},t.textForXAxisLabel=function(){return this.getLabelText("x")},t.textForYAxisLabel=function(){return this.getLabelText("y")},t.textForY2AxisLabel=function(){return this.getLabelText("y2")},t.xForAxisLabel=function(t,e){var i=this.owner;return t?e.isLeft?0:e.isCenter?i.width/2:i.width:e.isBottom?-i.height:e.isMiddle?-i.height/2:0},t.dxForAxisLabel=function(t,e){return t?e.isLeft?"0.5em":e.isRight?"-0.5em":"0":e.isTop?"-0.5em":e.isBottom?"0.5em":"0"},t.textAnchorForAxisLabel=function(t,e){return t?e.isLeft?"start":e.isCenter?"middle":"end":e.isBottom?"start":e.isMiddle?"middle":"end"},t.xForXAxisLabel=function(){return this.xForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},t.xForYAxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},t.xForY2AxisLabel=function(){return this.xForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},t.dxForXAxisLabel=function(){return this.dxForAxisLabel(!this.owner.config.axis_rotated,this.getXAxisLabelPosition())},t.dxForYAxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getYAxisLabelPosition())},t.dxForY2AxisLabel=function(){return this.dxForAxisLabel(this.owner.config.axis_rotated,this.getY2AxisLabelPosition())},t.dyForXAxisLabel=function(){var t=this.owner,e=t.config,i=this.getXAxisLabelPosition();return e.axis_rotated?i.isInner?"1.2em":-25-(t.config.axis_x_inner?0:this.getMaxTickWidth("x")):i.isInner?"-0.5em":e.axis_x_height?e.axis_x_height-10:"3em"},t.dyForYAxisLabel=function(){var t=this.owner,e=this.getYAxisLabelPosition();return t.config.axis_rotated?e.isInner?"-0.5em":"3em":e.isInner?"1.2em":-10-(t.config.axis_y_inner?0:this.getMaxTickWidth("y")+10)},t.dyForY2AxisLabel=function(){var t=this.owner,e=this.getY2AxisLabelPosition();return t.config.axis_rotated?e.isInner?"1.2em":"-2.2em":e.isInner?"-0.5em":15+(t.config.axis_y2_inner?0:this.getMaxTickWidth("y2")+15)},t.textAnchorForXAxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(!t.config.axis_rotated,this.getXAxisLabelPosition())},t.textAnchorForYAxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(t.config.axis_rotated,this.getYAxisLabelPosition())},t.textAnchorForY2AxisLabel=function(){var t=this.owner;return this.textAnchorForAxisLabel(t.config.axis_rotated,this.getY2AxisLabelPosition())},t.getMaxTickWidth=function(t,e){var i,n,a,r,s=this.owner,o=s.config,c=0;return e&&s.currentMaxTickWidths[t]?s.currentMaxTickWidths[t]:(s.svg&&(i=s.filterTargetsToShow(s.data.targets),"y"===t?(n=s.y.copy().domain(s.getYDomain(i,"y")),a=this.getYAxis(n,s.yOrient,o.axis_y_tick_format,s.yAxisTickValues,!1,!0,!0)):"y2"===t?(n=s.y2.copy().domain(s.getYDomain(i,"y2")),a=this.getYAxis(n,s.y2Orient,o.axis_y2_tick_format,s.y2AxisTickValues,!1,!0,!0)):(n=s.x.copy().domain(s.getXDomain(i)),a=this.getXAxis(n,s.xOrient,s.xAxisTickFormat,s.xAxisTickValues,!1,!0,!0),this.updateXAxisTickValues(i,a)),(r=s.d3.select("body").append("div").classed("c3",!0)).append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0).append("g").call(a).each(function(){s.d3.select(this).selectAll("text").each(function(){var t=this.getBoundingClientRect();c<t.width&&(c=t.width)}),r.remove()})),s.currentMaxTickWidths[t]=c<=0?s.currentMaxTickWidths[t]:c,s.currentMaxTickWidths[t])},t.updateLabels=function(t){var e=this.owner,n=e.main.select("."+i.axisX+" ."+i.axisXLabel),a=e.main.select("."+i.axisY+" ."+i.axisYLabel),r=e.main.select("."+i.axisY2+" ."+i.axisY2Label);(t?n.transition():n).attr("x",this.xForXAxisLabel.bind(this)).attr("dx",this.dxForXAxisLabel.bind(this)).attr("dy",this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this)),(t?a.transition():a).attr("x",this.xForYAxisLabel.bind(this)).attr("dx",this.dxForYAxisLabel.bind(this)).attr("dy",this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this)),(t?r.transition():r).attr("x",this.xForY2AxisLabel.bind(this)).attr("dx",this.dxForY2AxisLabel.bind(this)).attr("dy",this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this))},t.getPadding=function(t,e,i,n){var a="number"==typeof t?t:t[e];return s(a)?"ratio"===t.unit?t[e]*n:this.convertPixelsToAxisPadding(a,n):i},t.convertPixelsToAxisPadding=function(t,e){var i=this.owner;return e*(t/(i.config.axis_rotated?i.width:i.height))},t.generateTickValues=function(t,e,i){var n,a,r,s,c,d,u,l=t;if(e)if(1===(n=o(e)?e():e))l=[t[0]];else if(2===n)l=[t[0],t[t.length-1]];else if(n>2){for(s=n-2,a=t[0],c=((r=t[t.length-1])-a)/(s+1),l=[a],d=0;d<s;d++)u=+a+c*(d+1),l.push(i?new Date(u):u);l.push(r)}return i||(l=l.sort(function(t,e){return t-e})),l},t.generateTransitions=function(t){var e=this.owner.axes;return{axisX:t?e.x.transition().duration(t):e.x,axisY:t?e.y.transition().duration(t):e.y,axisY2:t?e.y2.transition().duration(t):e.y2,axisSubX:t?e.subx.transition().duration(t):e.subx}},t.redraw=function(t,e){var i=this.owner,n=t?i.d3.transition().duration(t):null;i.axes.x.style("opacity",e?0:1).call(i.xAxis,n),i.axes.y.style("opacity",e?0:1).call(i.yAxis,n),i.axes.y2.style("opacity",e?0:1).call(i.y2Axis,n),i.axes.subx.style("opacity",e?0:1).call(i.subXAxis,n)};var b,T,A={version:"0.5.3"};function P(t,e,i){this.owner=t,A.chart.internal[e]=i}function C(t){var e=this.internal=new L(this);e.loadConfig(t),e.beforeInit(t),e.init(),e.afterInit(t),function t(e,i,n){Object.keys(e).forEach(function(a){i[a]=e[a].bind(n),Object.keys(e[a]).length>0&&t(e[a],i[a],n)})}(b,this,this)}function L(t){var e=this;e.d3=window.d3?window.d3:"undefined"!=typeof require?require("d3"):void 0,e.api=t,e.config=e.getDefaultConfig(),e.data={},e.cache={},e.axes={}}return A.generate=function(t){return new C(t)},A.chart={fn:C.prototype,internal:{fn:L.prototype}},b=A.chart.fn,(T=A.chart.internal.fn).beforeInit=function(){},T.afterInit=function(){},T.init=function(){var t=this,e=t.config;if(t.initParams(),e.data_url)t.convertUrlToData(e.data_url,e.data_mimeType,e.data_headers,e.data_keys,t.initWithData);else if(e.data_json)t.initWithData(t.convertJsonToData(e.data_json,e.data_keys));else if(e.data_rows)t.initWithData(t.convertRowsToData(e.data_rows));else{if(!e.data_columns)throw Error("url or json or rows or columns is required.");t.initWithData(t.convertColumnsToData(e.data_columns))}},T.initParams=function(){var t=this,e=t.d3,i=t.config;t.clipId="c3-"+ +new Date+"-clip",t.clipIdForXAxis=t.clipId+"-xaxis",t.clipIdForYAxis=t.clipId+"-yaxis",t.clipIdForGrid=t.clipId+"-grid",t.clipIdForSubchart=t.clipId+"-subchart",t.clipPath=t.getClipPath(t.clipId),t.clipPathForXAxis=t.getClipPath(t.clipIdForXAxis),t.clipPathForYAxis=t.getClipPath(t.clipIdForYAxis),t.clipPathForGrid=t.getClipPath(t.clipIdForGrid),t.clipPathForSubchart=t.getClipPath(t.clipIdForSubchart),t.dragStart=null,t.dragging=!1,t.flowing=!1,t.cancelClick=!1,t.mouseover=!1,t.transiting=!1,t.color=t.generateColor(),t.levelColor=t.generateLevelColor(),t.dataTimeParse=(i.data_xLocaltime?e.timeParse:e.utcParse)(t.config.data_xFormat),t.axisTimeFormat=i.axis_x_localtime?e.timeFormat:e.utcFormat,t.defaultAxisTimeFormat=function(t){return t.getMilliseconds()?e.timeFormat(".%L")(t):t.getSeconds()?e.timeFormat(":%S")(t):t.getMinutes()?e.timeFormat("%I:%M")(t):t.getHours()?e.timeFormat("%I %p")(t):t.getDay()&&1!==t.getDate()?e.timeFormat("%-m/%-d")(t):1!==t.getDate()?e.timeFormat("%-m/%-d")(t):t.getMonth()?e.timeFormat("%-m/%-d")(t):e.timeFormat("%Y/%-m/%-d")(t)},t.hiddenTargetIds=[],t.hiddenLegendIds=[],t.focusedTargetIds=[],t.defocusedTargetIds=[],t.xOrient=i.axis_rotated?i.axis_x_inner?"right":"left":i.axis_x_inner?"top":"bottom",t.yOrient=i.axis_rotated?i.axis_y_inner?"top":"bottom":i.axis_y_inner?"right":"left",t.y2Orient=i.axis_rotated?i.axis_y2_inner?"bottom":"top":i.axis_y2_inner?"left":"right",t.subXOrient=i.axis_rotated?"left":"bottom",t.isLegendRight="right"===i.legend_position,t.isLegendInset="inset"===i.legend_position,t.isLegendTop="top-left"===i.legend_inset_anchor||"top-right"===i.legend_inset_anchor,t.isLegendLeft="top-left"===i.legend_inset_anchor||"bottom-left"===i.legend_inset_anchor,t.legendStep=0,t.legendItemWidth=0,t.legendItemHeight=0,t.currentMaxTickWidths={x:0,y:0,y2:0},t.rotated_padding_left=30,t.rotated_padding_right=i.axis_rotated&&!i.axis_x_show?0:30,t.rotated_padding_top=5,t.withoutFadeIn={},t.intervalForObserveInserted=void 0,t.axes.subx=e.selectAll([])},T.initChartElements=function(){this.initBar&&this.initBar(),this.initLine&&this.initLine(),this.initArc&&this.initArc(),this.initGauge&&this.initGauge(),this.initText&&this.initText()},T.initWithData=function(t){var e,n,a=this,r=a.d3,s=a.config,o=!0;a.axis=new v(a),s.bindto?"function"==typeof s.bindto.node?a.selectChart=s.bindto:a.selectChart=r.select(s.bindto):a.selectChart=r.selectAll([]),a.selectChart.empty()&&(a.selectChart=r.select(document.createElement("div")).style("opacity",0),a.observeInserted(a.selectChart),o=!1),a.selectChart.html("").classed("c3",!0),a.data.xs={},a.data.targets=a.convertDataToTargets(t),s.data_filter&&(a.data.targets=a.data.targets.filter(s.data_filter)),s.data_hide&&a.addHiddenTargetIds(!0===s.data_hide?a.mapToIds(a.data.targets):s.data_hide),s.legend_hide&&a.addHiddenLegendIds(!0===s.legend_hide?a.mapToIds(a.data.targets):s.legend_hide),a.updateSizes(),a.updateScales(),a.x.domain(r.extent(a.getXDomain(a.data.targets))),a.y.domain(a.getYDomain(a.data.targets,"y")),a.y2.domain(a.getYDomain(a.data.targets,"y2")),a.subX.domain(a.x.domain()),a.subY.domain(a.y.domain()),a.subY2.domain(a.y2.domain()),a.orgXDomain=a.x.domain(),a.svg=a.selectChart.append("svg").style("overflow","hidden").on("mouseenter",function(){return s.onmouseover.call(a)}).on("mouseleave",function(){return s.onmouseout.call(a)}),a.config.svg_classname&&a.svg.attr("class",a.config.svg_classname),e=a.svg.append("defs"),a.clipChart=a.appendClip(e,a.clipId),a.clipXAxis=a.appendClip(e,a.clipIdForXAxis),a.clipYAxis=a.appendClip(e,a.clipIdForYAxis),a.clipGrid=a.appendClip(e,a.clipIdForGrid),a.clipSubchart=a.appendClip(e,a.clipIdForSubchart),a.updateSvgSize(),n=a.main=a.svg.append("g").attr("transform",a.getTranslate("main")),a.initPie&&a.initPie(),a.initSubchart&&a.initSubchart(),a.initTooltip&&a.initTooltip(),a.initLegend&&a.initLegend(),a.initTitle&&a.initTitle(),a.initZoom&&a.initZoom(),a.initSubchartBrush&&a.initSubchartBrush(),n.append("text").attr("class",i.text+" "+i.empty).attr("text-anchor","middle").attr("dominant-baseline","middle"),a.initRegion(),a.initGrid(),n.append("g").attr("clip-path",a.clipPath).attr("class",i.chart),s.grid_lines_front&&a.initGridLines(),a.initChartElements(),a.axis.init(),a.updateTargets(a.data.targets),a.initEventRect(),s.axis_x_selection&&a.brush.selectionAsValue(a.getDefaultSelection()),o&&(a.updateDimension(),a.config.oninit.call(a),a.redraw({withTransition:!1,withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransitionForAxis:!1})),a.bindResize(),a.api.element=a.selectChart.node()},T.smoothLines=function(t,e){var i=this;"grid"===e&&t.each(function(){var t=i.d3.select(this),e=t.attr("x1"),n=t.attr("x2"),a=t.attr("y1"),r=t.attr("y2");t.attr({x1:Math.ceil(e),x2:Math.ceil(n),y1:Math.ceil(a),y2:Math.ceil(r)})})},T.updateSizes=function(){var t=this,e=t.config,i=t.legend?t.getLegendHeight():0,n=t.legend?t.getLegendWidth():0,a=t.isLegendRight||t.isLegendInset?0:i,r=t.hasArcType(),s=e.axis_rotated||r?0:t.getHorizontalAxisHeight("x"),o=e.subchart_show&&!r?e.subchart_size_height+s:0;t.currentWidth=t.getCurrentWidth(),t.currentHeight=t.getCurrentHeight(),t.margin=e.axis_rotated?{top:t.getHorizontalAxisHeight("y2")+t.getCurrentPaddingTop(),right:r?0:t.getCurrentPaddingRight(),bottom:t.getHorizontalAxisHeight("y")+a+t.getCurrentPaddingBottom(),left:o+(r?0:t.getCurrentPaddingLeft())}:{top:4+t.getCurrentPaddingTop(),right:r?0:t.getCurrentPaddingRight(),bottom:s+o+a+t.getCurrentPaddingBottom(),left:r?0:t.getCurrentPaddingLeft()},t.margin2=e.axis_rotated?{top:t.margin.top,right:NaN,bottom:20+a,left:t.rotated_padding_left}:{top:t.currentHeight-o-a,right:NaN,bottom:s+a,left:t.margin.left},t.margin3={top:0,right:NaN,bottom:0,left:0},t.updateSizeForLegend&&t.updateSizeForLegend(i,n),t.width=t.currentWidth-t.margin.left-t.margin.right,t.height=t.currentHeight-t.margin.top-t.margin.bottom,t.width<0&&(t.width=0),t.height<0&&(t.height=0),t.width2=e.axis_rotated?t.margin.left-t.rotated_padding_left-t.rotated_padding_right:t.width,t.height2=e.axis_rotated?t.height:t.currentHeight-t.margin2.top-t.margin2.bottom,t.width2<0&&(t.width2=0),t.height2<0&&(t.height2=0),t.arcWidth=t.width-(t.isLegendRight?n+10:0),t.arcHeight=t.height-(t.isLegendRight?0:10),t.hasType("gauge")&&!e.gauge_fullCircle&&(t.arcHeight+=t.height-t.getGaugeLabelHeight()),t.updateRadius&&t.updateRadius(),t.isLegendRight&&r&&(t.margin3.left=t.arcWidth/2+1.1*t.radiusExpanded)},T.updateTargets=function(t){var e=this;e.updateTargetsForText(t),e.updateTargetsForBar(t),e.updateTargetsForLine(t),e.hasArcType()&&e.updateTargetsForArc&&e.updateTargetsForArc(t),e.updateTargetsForSubchart&&e.updateTargetsForSubchart(t),e.showTargets()},T.showTargets=function(){var t=this;t.svg.selectAll("."+i.target).filter(function(e){return t.isTargetToShow(e.id)}).transition().duration(t.config.transition_duration).style("opacity",1)},T.redraw=function(t,e){var n,a,r,s,o,c,d,u,l,h,g,f,p,_,y,m,S,w,v,b,T,A,P,C,L,V,G,E,O,I,R,k=this,D=k.main,F=k.d3,X=k.config,M=k.getShapeIndices(k.isAreaType),z=k.getShapeIndices(k.isBarType),H=k.getShapeIndices(k.isLineType),B=k.hasArcType(),Y=k.filterTargetsToShow(k.data.targets),N=k.xv.bind(k);if(n=x(t=t||{},"withY",!0),a=x(t,"withSubchart",!0),r=x(t,"withTransition",!0),c=x(t,"withTransform",!1),d=x(t,"withUpdateXDomain",!1),u=x(t,"withUpdateOrgXDomain",!1),l=x(t,"withTrimXDomain",!0),p=x(t,"withUpdateXAxis",d),h=x(t,"withLegend",!1),g=x(t,"withEventRect",!0),f=x(t,"withDimension",!0),s=x(t,"withTransitionForExit",r),o=x(t,"withTransitionForAxis",r),v=r?X.transition_duration:0,b=s?v:0,T=o?v:0,e=e||k.axis.generateTransitions(T),h&&X.legend_show?k.updateLegend(k.mapToIds(k.data.targets),t,e):f&&k.updateDimension(!0),k.isCategorized()&&0===Y.length&&k.x.domain([0,k.axes.x.selectAll(".tick").size()]),Y.length?(k.updateXDomain(Y,d,u,l),X.axis_x_tick_values||(V=k.axis.updateXAxisTickValues(Y))):(k.xAxis.tickValues([]),k.subXAxis.tickValues([])),X.zoom_rescale&&!t.flow&&(O=k.x.orgDomain()),k.y.domain(k.getYDomain(Y,"y",O)),k.y2.domain(k.getYDomain(Y,"y2",O)),!X.axis_y_tick_values&&X.axis_y_tick_count&&k.yAxis.tickValues(k.axis.generateTickValues(k.y.domain(),X.axis_y_tick_count)),!X.axis_y2_tick_values&&X.axis_y2_tick_count&&k.y2Axis.tickValues(k.axis.generateTickValues(k.y2.domain(),X.axis_y2_tick_count)),k.axis.redraw(T,B),k.axis.updateLabels(r),(d||p)&&Y.length)if(X.axis_x_tick_culling&&V){for(G=1;G<V.length;G++)if(V.length/G<X.axis_x_tick_culling_max){E=G;break}k.svg.selectAll("."+i.axisX+" .tick text").each(function(t){var e=V.indexOf(t);e>=0&&F.select(this).style("display",e%E?"none":"block")})}else k.svg.selectAll("."+i.axisX+" .tick text").style("display","block");_=k.generateDrawArea?k.generateDrawArea(M,!1):void 0,y=k.generateDrawBar?k.generateDrawBar(z):void 0,m=k.generateDrawLine?k.generateDrawLine(H,!1):void 0,S=k.generateXYForText(M,z,H,!0),w=k.generateXYForText(M,z,H,!1),k.updateCircleY(),I=(k.config.axis_rotated?k.circleY:k.circleX).bind(k),R=(k.config.axis_rotated?k.circleX:k.circleY).bind(k),n&&(k.subY.domain(k.getYDomain(Y,"y")),k.subY2.domain(k.getYDomain(Y,"y2"))),k.updateXgridFocus(),D.select("text."+i.text+"."+i.empty).attr("x",k.width/2).attr("y",k.height/2).text(X.data_empty_label_text).transition().style("opacity",Y.length?0:1),g&&k.redrawEventRect(),k.updateGrid(v),k.updateRegion(v),k.updateBar(b),k.updateLine(b),k.updateArea(b),k.updateCircle(I,R),k.hasDataLabel()&&k.updateText(S,w,b),k.redrawTitle&&k.redrawTitle(),k.redrawArc&&k.redrawArc(v,b,c),k.redrawSubchart&&k.redrawSubchart(a,e,v,b,M,z,H),D.selectAll("."+i.selectedCircles).filter(k.isBarType.bind(k)).selectAll("circle").remove(),t.flow&&(C=k.generateFlow({targets:Y,flow:t.flow,duration:t.flow.duration,drawBar:y,drawLine:m,drawArea:_,cx:I,cy:R,xv:N,xForText:S,yForText:w})),k.isTabVisible()&&(v?(L=F.transition().duration(v),A=[],[k.redrawBar(y,!0,L),k.redrawLine(m,!0,L),k.redrawArea(_,!0,L),k.redrawCircle(I,R,!0,L),k.redrawText(S,w,t.flow,!0,L),k.redrawRegion(!0,L),k.redrawGrid(!0,L)].forEach(function(t){t.forEach(function(t){A.push(t)})}),P=k.generateWait(),A.forEach(function(t){P.add(t)}),P(function(){C&&C(),X.onrendered&&X.onrendered.call(k)})):(k.redrawBar(y),k.redrawLine(m),k.redrawArea(_),k.redrawCircle(I,R),k.redrawText(S,w,t.flow),k.redrawRegion(),k.redrawGrid(),C&&C(),X.onrendered&&X.onrendered.call(k))),k.mapToIds(k.data.targets).forEach(function(t){k.withoutFadeIn[t]=!0})},T.updateAndRedraw=function(t){var e,i=this,n=i.config;(t=t||{}).withTransition=x(t,"withTransition",!0),t.withTransform=x(t,"withTransform",!1),t.withLegend=x(t,"withLegend",!1),t.withUpdateXDomain=x(t,"withUpdateXDomain",!0),t.withUpdateOrgXDomain=x(t,"withUpdateOrgXDomain",!0),t.withTransitionForExit=!1,t.withTransitionForTransform=x(t,"withTransitionForTransform",t.withTransition),i.updateSizes(),t.withLegend&&n.legend_show||(e=i.axis.generateTransitions(t.withTransitionForAxis?n.transition_duration:0),i.updateScales(),i.updateSvgSize(),i.transformAll(t.withTransitionForTransform,e)),i.redraw(t,e)},T.redrawWithoutRescale=function(){this.redraw({withY:!1,withSubchart:!1,withEventRect:!1,withTransitionForAxis:!1})},T.isTimeSeries=function(){return"timeseries"===this.config.axis_x_type},T.isCategorized=function(){return this.config.axis_x_type.indexOf("categor")>=0},T.isCustomX=function(){var t=this.config;return!this.isTimeSeries()&&(t.data_x||_(t.data_xs))},T.isTimeSeriesY=function(){return"timeseries"===this.config.axis_y_type},T.getTranslate=function(t){var e,i,n=this,a=n.config;return"main"===t?(e=g(n.margin.left),i=g(n.margin.top)):"context"===t?(e=g(n.margin2.left),i=g(n.margin2.top)):"legend"===t?(e=n.margin3.left,i=n.margin3.top):"x"===t?(e=0,i=a.axis_rotated?0:n.height):"y"===t?(e=0,i=a.axis_rotated?n.height:0):"y2"===t?(e=a.axis_rotated?0:n.width,i=a.axis_rotated?1:0):"subx"===t?(e=0,i=a.axis_rotated?0:n.height2):"arc"===t&&(e=n.arcWidth/2,i=n.arcHeight/2-(n.hasType("gauge")?6:0)),"translate("+e+","+i+")"},T.initialOpacity=function(t){return null!==t.value&&this.withoutFadeIn[t.id]?1:0},T.initialOpacityForCircle=function(t){return null!==t.value&&this.withoutFadeIn[t.id]?this.opacityForCircle(t):0},T.opacityForCircle=function(t){var e=(o(this.config.point_show)?this.config.point_show(t):this.config.point_show)?1:0;return s(t.value)?this.isScatterType(t)?.5:e:0},T.opacityForText=function(){return this.hasDataLabel()?1:0},T.xx=function(t){return t?this.x(t.x):null},T.xv=function(t){var e=this,i=t.value;return e.isTimeSeries()?i=e.parseDate(t.value):e.isCategorized()&&"string"==typeof t.value&&(i=e.config.axis_x_categories.indexOf(t.value)),Math.ceil(e.x(i))},T.yv=function(t){var e=t.axis&&"y2"===t.axis?this.y2:this.y;return Math.ceil(e(t.value))},T.subxx=function(t){return t?this.subX(t.x):null},T.transformMain=function(t,e){var n,a,r,s=this;e&&e.axisX?n=e.axisX:(n=s.main.select("."+i.axisX),t&&(n=n.transition())),e&&e.axisY?a=e.axisY:(a=s.main.select("."+i.axisY),t&&(a=a.transition())),e&&e.axisY2?r=e.axisY2:(r=s.main.select("."+i.axisY2),t&&(r=r.transition())),(t?s.main.transition():s.main).attr("transform",s.getTranslate("main")),n.attr("transform",s.getTranslate("x")),a.attr("transform",s.getTranslate("y")),r.attr("transform",s.getTranslate("y2")),s.main.select("."+i.chartArcs).attr("transform",s.getTranslate("arc"))},T.transformAll=function(t,e){var i=this;i.transformMain(t,e),i.config.subchart_show&&i.transformContext(t,e),i.legend&&i.transformLegend(t)},T.updateSvgSize=function(){var t=this,e=t.svg.select(".c3-brush .overlay");t.svg.attr("width",t.currentWidth).attr("height",t.currentHeight),t.svg.selectAll(["#"+t.clipId,"#"+t.clipIdForGrid]).select("rect").attr("width",t.width).attr("height",t.height),t.svg.select("#"+t.clipIdForXAxis).select("rect").attr("x",t.getXAxisClipX.bind(t)).attr("y",t.getXAxisClipY.bind(t)).attr("width",t.getXAxisClipWidth.bind(t)).attr("height",t.getXAxisClipHeight.bind(t)),t.svg.select("#"+t.clipIdForYAxis).select("rect").attr("x",t.getYAxisClipX.bind(t)).attr("y",t.getYAxisClipY.bind(t)).attr("width",t.getYAxisClipWidth.bind(t)).attr("height",t.getYAxisClipHeight.bind(t)),t.svg.select("#"+t.clipIdForSubchart).select("rect").attr("width",t.width).attr("height",e.size()?e.attr("height"):0),t.selectChart.style("max-height",t.currentHeight+"px")},T.updateDimension=function(t){var e=this;t||(e.config.axis_rotated?(e.axes.x.call(e.xAxis),e.axes.subx.call(e.subXAxis)):(e.axes.y.call(e.yAxis),e.axes.y2.call(e.y2Axis))),e.updateSizes(),e.updateScales(),e.updateSvgSize(),e.transformAll(!1)},T.observeInserted=function(t){var e,i=this;"undefined"!=typeof MutationObserver?(e=new MutationObserver(function(n){n.forEach(function(n){"childList"===n.type&&n.previousSibling&&(e.disconnect(),i.intervalForObserveInserted=window.setInterval(function(){t.node().parentNode&&(window.clearInterval(i.intervalForObserveInserted),i.updateDimension(),i.brush&&i.brush.update(),i.config.oninit.call(i),i.redraw({withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransition:!1,withTransitionForTransform:!1,withLegend:!0}),t.transition().style("opacity",1))},10))})})).observe(t.node(),{attributes:!0,childList:!0,characterData:!0}):window.console.error("MutationObserver not defined.")},T.bindResize=function(){var t=this,e=t.config;if(t.resizeFunction=t.generateResize(),t.resizeFunction.add(function(){e.onresize.call(t)}),e.resize_auto&&t.resizeFunction.add(function(){void 0!==t.resizeTimeout&&window.clearTimeout(t.resizeTimeout),t.resizeTimeout=window.setTimeout(function(){delete t.resizeTimeout,t.updateAndRedraw({withUpdateXDomain:!1,withUpdateOrgXDomain:!1,withTransition:!1,withTransitionForTransform:!1,withLegend:!0}),t.brush&&t.brush.update()},100)}),t.resizeFunction.add(function(){e.onresized.call(t)}),t.resizeIfElementDisplayed=function(){null!=t.api&&t.api.element.offsetParent&&t.resizeFunction()},window.attachEvent)window.attachEvent("onresize",t.resizeIfElementDisplayed);else if(window.addEventListener)window.addEventListener("resize",t.resizeIfElementDisplayed,!1);else{var i=window.onresize;i?i.add&&i.remove||(i=t.generateResize()).add(window.onresize):i=t.generateResize(),i.add(t.resizeFunction),window.onresize=function(){t.api.element.offsetParent&&i()}}},T.generateResize=function(){var t=[];function e(){t.forEach(function(t){t()})}return e.add=function(e){t.push(e)},e.remove=function(e){for(var i=0;i<t.length;i++)if(t[i]===e){t.splice(i,1);break}},e},T.endall=function(t,e){var i=0;t.each(function(){++i}).on("end",function(){--i||e.apply(this,arguments)})},T.generateWait=function(){var t=[],e=function(e){var i=setInterval(function(){var n=0;t.forEach(function(t){if(t.empty())n+=1;else try{t.transition()}catch(t){n+=1}}),n===t.length&&(clearInterval(i),e&&e())},50)};return e.add=function(e){t.push(e)},e},T.parseDate=function(t){var e;return t instanceof Date?e=t:"string"==typeof t?e=this.dataTimeParse(t):"object"===(void 0===t?"undefined":n(t))?e=new Date(+t):"number"!=typeof t||isNaN(t)||(e=new Date(+t)),e&&!isNaN(+e)||window.console.error("Failed to parse x '"+t+"' to Date object"),e},T.isTabVisible=function(){var t;return void 0!==document.hidden?t="hidden":void 0!==document.mozHidden?t="mozHidden":void 0!==document.msHidden?t="msHidden":void 0!==document.webkitHidden&&(t="webkitHidden"),!document[t]},T.isValue=s,T.isFunction=o,T.isString=d,T.isUndefined=u,T.isDefined=l,T.ceil10=h,T.asHalfPixel=g,T.diffDomain=f,T.isEmpty=p,T.notEmpty=_,T.notEmpty=_,T.getOption=x,T.hasValue=y,T.sanitise=m,T.getPathBox=S,T.CLASS=i,Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},a=function(){return i.apply(this instanceof n?this:t,e.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,a.prototype=new n,a}),"SVGPathSeg"in window||(window.SVGPathSeg=function(t,e,i){this.pathSegType=t,this.pathSegTypeAsLetter=e,this._owningPathSegList=i},window.SVGPathSeg.prototype.classname="SVGPathSeg",window.SVGPathSeg.PATHSEG_UNKNOWN=0,window.SVGPathSeg.PATHSEG_CLOSEPATH=1,window.SVGPathSeg.PATHSEG_MOVETO_ABS=2,window.SVGPathSeg.PATHSEG_MOVETO_REL=3,window.SVGPathSeg.PATHSEG_LINETO_ABS=4,window.SVGPathSeg.PATHSEG_LINETO_REL=5,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS=6,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL=7,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS=8,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL=9,window.SVGPathSeg.PATHSEG_ARC_ABS=10,window.SVGPathSeg.PATHSEG_ARC_REL=11,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS=12,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL=13,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS=14,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL=15,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS=16,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL=17,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS=18,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL=19,window.SVGPathSeg.prototype._segmentChanged=function(){this._owningPathSegList&&this._owningPathSegList.segmentChanged(this)},window.SVGPathSegClosePath=function(t){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CLOSEPATH,"z",t)},window.SVGPathSegClosePath.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegClosePath.prototype.toString=function(){return"[object SVGPathSegClosePath]"},window.SVGPathSegClosePath.prototype._asPathString=function(){return this.pathSegTypeAsLetter},window.SVGPathSegClosePath.prototype.clone=function(){return new window.SVGPathSegClosePath(void 0)},window.SVGPathSegMovetoAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_MOVETO_ABS,"M",t),this._x=e,this._y=i},window.SVGPathSegMovetoAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegMovetoAbs.prototype.toString=function(){return"[object SVGPathSegMovetoAbs]"},window.SVGPathSegMovetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegMovetoAbs.prototype.clone=function(){return new window.SVGPathSegMovetoAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegMovetoAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegMovetoAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegMovetoRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_MOVETO_REL,"m",t),this._x=e,this._y=i},window.SVGPathSegMovetoRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegMovetoRel.prototype.toString=function(){return"[object SVGPathSegMovetoRel]"},window.SVGPathSegMovetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegMovetoRel.prototype.clone=function(){return new window.SVGPathSegMovetoRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegMovetoRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegMovetoRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_ABS,"L",t),this._x=e,this._y=i},window.SVGPathSegLinetoAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoAbs.prototype.toString=function(){return"[object SVGPathSegLinetoAbs]"},window.SVGPathSegLinetoAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegLinetoAbs.prototype.clone=function(){return new window.SVGPathSegLinetoAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegLinetoAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegLinetoAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_REL,"l",t),this._x=e,this._y=i},window.SVGPathSegLinetoRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoRel.prototype.toString=function(){return"[object SVGPathSegLinetoRel]"},window.SVGPathSegLinetoRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegLinetoRel.prototype.clone=function(){return new window.SVGPathSegLinetoRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegLinetoRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegLinetoRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicAbs=function(t,e,i,n,a,r,s){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS,"C",t),this._x=e,this._y=i,this._x1=n,this._y1=a,this._x2=r,this._y2=s},window.SVGPathSegCurvetoCubicAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicAbs]"},window.SVGPathSegCurvetoCubicAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicAbs(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicRel=function(t,e,i,n,a,r,s){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL,"c",t),this._x=e,this._y=i,this._x1=n,this._y1=a,this._x2=r,this._y2=s},window.SVGPathSegCurvetoCubicRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicRel]"},window.SVGPathSegCurvetoCubicRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicRel.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicRel(void 0,this._x,this._y,this._x1,this._y1,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticAbs=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS,"Q",t),this._x=e,this._y=i,this._x1=n,this._y1=a},window.SVGPathSegCurvetoQuadraticAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticAbs]"},window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticAbs(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticRel=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL,"q",t),this._x=e,this._y=i,this._x1=n,this._y1=a},window.SVGPathSegCurvetoQuadraticRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticRel]"},window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x1+" "+this._y1+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticRel.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticRel(void 0,this._x,this._y,this._x1,this._y1)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"x1",{get:function(){return this._x1},set:function(t){this._x1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype,"y1",{get:function(){return this._y1},set:function(t){this._y1=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegArcAbs=function(t,e,i,n,a,r,s,o){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_ARC_ABS,"A",t),this._x=e,this._y=i,this._r1=n,this._r2=a,this._angle=r,this._largeArcFlag=s,this._sweepFlag=o},window.SVGPathSegArcAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegArcAbs.prototype.toString=function(){return"[object SVGPathSegArcAbs]"},window.SVGPathSegArcAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},window.SVGPathSegArcAbs.prototype.clone=function(){return new window.SVGPathSegArcAbs(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(window.SVGPathSegArcAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"r1",{get:function(){return this._r1},set:function(t){this._r1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"r2",{get:function(){return this._r2},set:function(t){this._r2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"angle",{get:function(){return this._angle},set:function(t){this._angle=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(t){this._largeArcFlag=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcAbs.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(t){this._sweepFlag=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegArcRel=function(t,e,i,n,a,r,s,o){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_ARC_REL,"a",t),this._x=e,this._y=i,this._r1=n,this._r2=a,this._angle=r,this._largeArcFlag=s,this._sweepFlag=o},window.SVGPathSegArcRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegArcRel.prototype.toString=function(){return"[object SVGPathSegArcRel]"},window.SVGPathSegArcRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._r1+" "+this._r2+" "+this._angle+" "+(this._largeArcFlag?"1":"0")+" "+(this._sweepFlag?"1":"0")+" "+this._x+" "+this._y},window.SVGPathSegArcRel.prototype.clone=function(){return new window.SVGPathSegArcRel(void 0,this._x,this._y,this._r1,this._r2,this._angle,this._largeArcFlag,this._sweepFlag)},Object.defineProperty(window.SVGPathSegArcRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"r1",{get:function(){return this._r1},set:function(t){this._r1=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"r2",{get:function(){return this._r2},set:function(t){this._r2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"angle",{get:function(){return this._angle},set:function(t){this._angle=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"largeArcFlag",{get:function(){return this._largeArcFlag},set:function(t){this._largeArcFlag=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegArcRel.prototype,"sweepFlag",{get:function(){return this._sweepFlag},set:function(t){this._sweepFlag=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoHorizontalAbs=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS,"H",t),this._x=e},window.SVGPathSegLinetoHorizontalAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoHorizontalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalAbs]"},window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},window.SVGPathSegLinetoHorizontalAbs.prototype.clone=function(){return new window.SVGPathSegLinetoHorizontalAbs(void 0,this._x)},Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoHorizontalRel=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL,"h",t),this._x=e},window.SVGPathSegLinetoHorizontalRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoHorizontalRel.prototype.toString=function(){return"[object SVGPathSegLinetoHorizontalRel]"},window.SVGPathSegLinetoHorizontalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x},window.SVGPathSegLinetoHorizontalRel.prototype.clone=function(){return new window.SVGPathSegLinetoHorizontalRel(void 0,this._x)},Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoVerticalAbs=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS,"V",t),this._y=e},window.SVGPathSegLinetoVerticalAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoVerticalAbs.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalAbs]"},window.SVGPathSegLinetoVerticalAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},window.SVGPathSegLinetoVerticalAbs.prototype.clone=function(){return new window.SVGPathSegLinetoVerticalAbs(void 0,this._y)},Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegLinetoVerticalRel=function(t,e){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL,"v",t),this._y=e},window.SVGPathSegLinetoVerticalRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegLinetoVerticalRel.prototype.toString=function(){return"[object SVGPathSegLinetoVerticalRel]"},window.SVGPathSegLinetoVerticalRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._y},window.SVGPathSegLinetoVerticalRel.prototype.clone=function(){return new window.SVGPathSegLinetoVerticalRel(void 0,this._y)},Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicSmoothAbs=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS,"S",t),this._x=e,this._y=i,this._x2=n,this._y2=a},window.SVGPathSegCurvetoCubicSmoothAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothAbs]"},window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicSmoothAbs(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoCubicSmoothRel=function(t,e,i,n,a){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL,"s",t),this._x=e,this._y=i,this._x2=n,this._y2=a},window.SVGPathSegCurvetoCubicSmoothRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoCubicSmoothRel]"},window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x2+" "+this._y2+" "+this._x+" "+this._y},window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone=function(){return new window.SVGPathSegCurvetoCubicSmoothRel(void 0,this._x,this._y,this._x2,this._y2)},Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"x2",{get:function(){return this._x2},set:function(t){this._x2=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype,"y2",{get:function(){return this._y2},set:function(t){this._y2=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticSmoothAbs=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS,"T",t),this._x=e,this._y=i},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothAbs]"},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticSmoothAbs(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathSegCurvetoQuadraticSmoothRel=function(t,e,i){window.SVGPathSeg.call(this,window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,"t",t),this._x=e,this._y=i},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype=Object.create(window.SVGPathSeg.prototype),window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString=function(){return"[object SVGPathSegCurvetoQuadraticSmoothRel]"},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString=function(){return this.pathSegTypeAsLetter+" "+this._x+" "+this._y},window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone=function(){return new window.SVGPathSegCurvetoQuadraticSmoothRel(void 0,this._x,this._y)},Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype,"x",{get:function(){return this._x},set:function(t){this._x=t,this._segmentChanged()},enumerable:!0}),Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype,"y",{get:function(){return this._y},set:function(t){this._y=t,this._segmentChanged()},enumerable:!0}),window.SVGPathElement.prototype.createSVGPathSegClosePath=function(){return new window.SVGPathSegClosePath(void 0)},window.SVGPathElement.prototype.createSVGPathSegMovetoAbs=function(t,e){return new window.SVGPathSegMovetoAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegMovetoRel=function(t,e){return new window.SVGPathSegMovetoRel(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegLinetoAbs=function(t,e){return new window.SVGPathSegLinetoAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegLinetoRel=function(t,e){return new window.SVGPathSegLinetoRel(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs=function(t,e,i,n,a,r){return new window.SVGPathSegCurvetoCubicAbs(void 0,t,e,i,n,a,r)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel=function(t,e,i,n,a,r){return new window.SVGPathSegCurvetoCubicRel(void 0,t,e,i,n,a,r)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs=function(t,e,i,n){return new window.SVGPathSegCurvetoQuadraticAbs(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel=function(t,e,i,n){return new window.SVGPathSegCurvetoQuadraticRel(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegArcAbs=function(t,e,i,n,a,r,s){return new window.SVGPathSegArcAbs(void 0,t,e,i,n,a,r,s)},window.SVGPathElement.prototype.createSVGPathSegArcRel=function(t,e,i,n,a,r,s){return new window.SVGPathSegArcRel(void 0,t,e,i,n,a,r,s)},window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs=function(t){return new window.SVGPathSegLinetoHorizontalAbs(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel=function(t){return new window.SVGPathSegLinetoHorizontalRel(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs=function(t){return new window.SVGPathSegLinetoVerticalAbs(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel=function(t){return new window.SVGPathSegLinetoVerticalRel(void 0,t)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs=function(t,e,i,n){return new window.SVGPathSegCurvetoCubicSmoothAbs(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel=function(t,e,i,n){return new window.SVGPathSegCurvetoCubicSmoothRel(void 0,t,e,i,n)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs=function(t,e){return new window.SVGPathSegCurvetoQuadraticSmoothAbs(void 0,t,e)},window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel=function(t,e){return new window.SVGPathSegCurvetoQuadraticSmoothRel(void 0,t,e)},"getPathSegAtLength"in window.SVGPathElement.prototype||(window.SVGPathElement.prototype.getPathSegAtLength=function(t){if(void 0===t||!isFinite(t))throw"Invalid arguments.";var e=document.createElementNS("http://www.w3.org/2000/svg","path");e.setAttribute("d",this.getAttribute("d"));var i=e.pathSegList.numberOfItems-1;if(i<=0)return 0;do{if(e.pathSegList.removeItem(i),t>e.getTotalLength())break;i--}while(i>0);return i})),"SVGPathSegList"in window||(window.SVGPathSegList=function(t){this._pathElement=t,this._list=this._parsePath(this._pathElement.getAttribute("d")),this._mutationObserverConfig={attributes:!0,attributeFilter:["d"]},this._pathElementMutationObserver=new MutationObserver(this._updateListFromPathMutations.bind(this)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},window.SVGPathSegList.prototype.classname="SVGPathSegList",Object.defineProperty(window.SVGPathSegList.prototype,"numberOfItems",{get:function(){return this._checkPathSynchronizedToList(),this._list.length},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"pathSegList",{get:function(){return this._pathSegList||(this._pathSegList=new window.SVGPathSegList(this)),this._pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"normalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"animatedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),Object.defineProperty(window.SVGPathElement.prototype,"animatedNormalizedPathSegList",{get:function(){return this.pathSegList},enumerable:!0}),window.SVGPathSegList.prototype._checkPathSynchronizedToList=function(){this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords())},window.SVGPathSegList.prototype._updateListFromPathMutations=function(t){if(this._pathElement){var e=!1;t.forEach(function(t){"d"==t.attributeName&&(e=!0)}),e&&(this._list=this._parsePath(this._pathElement.getAttribute("d")))}},window.SVGPathSegList.prototype._writeListToPath=function(){this._pathElementMutationObserver.disconnect(),this._pathElement.setAttribute("d",window.SVGPathSegList._pathSegArrayAsString(this._list)),this._pathElementMutationObserver.observe(this._pathElement,this._mutationObserverConfig)},window.SVGPathSegList.prototype.segmentChanged=function(t){this._writeListToPath()},window.SVGPathSegList.prototype.clear=function(){this._checkPathSynchronizedToList(),this._list.forEach(function(t){t._owningPathSegList=null}),this._list=[],this._writeListToPath()},window.SVGPathSegList.prototype.initialize=function(t){return this._checkPathSynchronizedToList(),this._list=[t],t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype._checkValidIndex=function(t){if(isNaN(t)||t<0||t>=this.numberOfItems)throw"INDEX_SIZE_ERR"},window.SVGPathSegList.prototype.getItem=function(t){return this._checkPathSynchronizedToList(),this._checkValidIndex(t),this._list[t]},window.SVGPathSegList.prototype.insertItemBefore=function(t,e){return this._checkPathSynchronizedToList(),e>this.numberOfItems&&(e=this.numberOfItems),t._owningPathSegList&&(t=t.clone()),this._list.splice(e,0,t),t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype.replaceItem=function(t,e){return this._checkPathSynchronizedToList(),t._owningPathSegList&&(t=t.clone()),this._checkValidIndex(e),this._list[e]=t,t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList.prototype.removeItem=function(t){this._checkPathSynchronizedToList(),this._checkValidIndex(t);var e=this._list[t];return this._list.splice(t,1),this._writeListToPath(),e},window.SVGPathSegList.prototype.appendItem=function(t){return this._checkPathSynchronizedToList(),t._owningPathSegList&&(t=t.clone()),this._list.push(t),t._owningPathSegList=this,this._writeListToPath(),t},window.SVGPathSegList._pathSegArrayAsString=function(t){var e="",i=!0;return t.forEach(function(t){i?(i=!1,e+=t._asPathString()):e+=" "+t._asPathString()}),e},window.SVGPathSegList.prototype._parsePath=function(t){if(!t||0==t.length)return[];var e=this,i=function(){this.pathSegList=[]};i.prototype.appendSegment=function(t){this.pathSegList.push(t)};var n=function(t){this._string=t,this._currentIndex=0,this._endIndex=this._string.length,this._previousCommand=window.SVGPathSeg.PATHSEG_UNKNOWN,this._skipOptionalSpaces()};n.prototype._isCurrentSpace=function(){var t=this._string[this._currentIndex];return t<=" "&&(" "==t||"\n"==t||"\t"==t||"\r"==t||"\f"==t)},n.prototype._skipOptionalSpaces=function(){for(;this._currentIndex<this._endIndex&&this._isCurrentSpace();)this._currentIndex++;return this._currentIndex<this._endIndex},n.prototype._skipOptionalSpacesOrDelimiter=function(){return!(this._currentIndex<this._endIndex&&!this._isCurrentSpace()&&","!=this._string.charAt(this._currentIndex))&&(this._skipOptionalSpaces()&&this._currentIndex<this._endIndex&&","==this._string.charAt(this._currentIndex)&&(this._currentIndex++,this._skipOptionalSpaces()),this._currentIndex<this._endIndex)},n.prototype.hasMoreData=function(){return this._currentIndex<this._endIndex},n.prototype.peekSegmentType=function(){var t=this._string[this._currentIndex];return this._pathSegTypeFromChar(t)},n.prototype._pathSegTypeFromChar=function(t){switch(t){case"Z":case"z":return window.SVGPathSeg.PATHSEG_CLOSEPATH;case"M":return window.SVGPathSeg.PATHSEG_MOVETO_ABS;case"m":return window.SVGPathSeg.PATHSEG_MOVETO_REL;case"L":return window.SVGPathSeg.PATHSEG_LINETO_ABS;case"l":return window.SVGPathSeg.PATHSEG_LINETO_REL;case"C":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;case"c":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;case"Q":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;case"q":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;case"A":return window.SVGPathSeg.PATHSEG_ARC_ABS;case"a":return window.SVGPathSeg.PATHSEG_ARC_REL;case"H":return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;case"h":return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;case"V":return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;case"v":return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;case"S":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;case"s":return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;case"T":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;case"t":return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;default:return window.SVGPathSeg.PATHSEG_UNKNOWN}},n.prototype._nextCommandHelper=function(t,e){return("+"==t||"-"==t||"."==t||t>="0"&&t<="9")&&e!=window.SVGPathSeg.PATHSEG_CLOSEPATH?e==window.SVGPathSeg.PATHSEG_MOVETO_ABS?window.SVGPathSeg.PATHSEG_LINETO_ABS:e==window.SVGPathSeg.PATHSEG_MOVETO_REL?window.SVGPathSeg.PATHSEG_LINETO_REL:e:window.SVGPathSeg.PATHSEG_UNKNOWN},n.prototype.initialCommandIsMoveTo=function(){if(!this.hasMoreData())return!0;var t=this.peekSegmentType();return t==window.SVGPathSeg.PATHSEG_MOVETO_ABS||t==window.SVGPathSeg.PATHSEG_MOVETO_REL},n.prototype._parseNumber=function(){var t=0,e=0,i=1,n=0,a=1,r=1,s=this._currentIndex;if(this._skipOptionalSpaces(),this._currentIndex<this._endIndex&&"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:this._currentIndex<this._endIndex&&"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,a=-1),!(this._currentIndex==this._endIndex||(this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")&&"."!=this._string.charAt(this._currentIndex))){for(var o=this._currentIndex;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)this._currentIndex++;if(this._currentIndex!=o)for(var c=this._currentIndex-1,d=1;c>=o;)e+=d*(this._string.charAt(c--)-"0"),d*=10;if(this._currentIndex<this._endIndex&&"."==this._string.charAt(this._currentIndex)){if(this._currentIndex++,this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)i*=10,n+=(this._string.charAt(this._currentIndex)-"0")/i,this._currentIndex+=1}if(this._currentIndex!=s&&this._currentIndex+1<this._endIndex&&("e"==this._string.charAt(this._currentIndex)||"E"==this._string.charAt(this._currentIndex))&&"x"!=this._string.charAt(this._currentIndex+1)&&"m"!=this._string.charAt(this._currentIndex+1)){if(this._currentIndex++,"+"==this._string.charAt(this._currentIndex)?this._currentIndex++:"-"==this._string.charAt(this._currentIndex)&&(this._currentIndex++,r=-1),this._currentIndex>=this._endIndex||this._string.charAt(this._currentIndex)<"0"||this._string.charAt(this._currentIndex)>"9")return;for(;this._currentIndex<this._endIndex&&this._string.charAt(this._currentIndex)>="0"&&this._string.charAt(this._currentIndex)<="9";)t*=10,t+=this._string.charAt(this._currentIndex)-"0",this._currentIndex++}var u=e+n;if(u*=a,t&&(u*=Math.pow(10,r*t)),s!=this._currentIndex)return this._skipOptionalSpacesOrDelimiter(),u}},n.prototype._parseArcFlag=function(){if(!(this._currentIndex>=this._endIndex)){var t=!1,e=this._string.charAt(this._currentIndex++);if("0"==e)t=!1;else{if("1"!=e)return;t=!0}return this._skipOptionalSpacesOrDelimiter(),t}},n.prototype.parseSegment=function(){var t=this._string[this._currentIndex],i=this._pathSegTypeFromChar(t);if(i==window.SVGPathSeg.PATHSEG_UNKNOWN){if(this._previousCommand==window.SVGPathSeg.PATHSEG_UNKNOWN)return null;if((i=this._nextCommandHelper(t,this._previousCommand))==window.SVGPathSeg.PATHSEG_UNKNOWN)return null}else this._currentIndex++;switch(this._previousCommand=i,i){case window.SVGPathSeg.PATHSEG_MOVETO_REL:return new window.SVGPathSegMovetoRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_MOVETO_ABS:return new window.SVGPathSegMovetoAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_REL:return new window.SVGPathSegLinetoRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_ABS:return new window.SVGPathSegLinetoAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:return new window.SVGPathSegLinetoHorizontalRel(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:return new window.SVGPathSegLinetoHorizontalAbs(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:return new window.SVGPathSegLinetoVerticalRel(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:return new window.SVGPathSegLinetoVerticalAbs(e,this._parseNumber());case window.SVGPathSeg.PATHSEG_CLOSEPATH:return this._skipOptionalSpaces(),new window.SVGPathSegClosePath(e);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:var n={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()};return new window.SVGPathSegCurvetoCubicRel(e,n.x,n.y,n.x1,n.y1,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:return n={x1:this._parseNumber(),y1:this._parseNumber(),x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicAbs(e,n.x,n.y,n.x1,n.y1,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:return n={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicSmoothRel(e,n.x,n.y,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:return n={x2:this._parseNumber(),y2:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoCubicSmoothAbs(e,n.x,n.y,n.x2,n.y2);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:return n={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoQuadraticRel(e,n.x,n.y,n.x1,n.y1);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:return n={x1:this._parseNumber(),y1:this._parseNumber(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegCurvetoQuadraticAbs(e,n.x,n.y,n.x1,n.y1);case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:return new window.SVGPathSegCurvetoQuadraticSmoothRel(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:return new window.SVGPathSegCurvetoQuadraticSmoothAbs(e,this._parseNumber(),this._parseNumber());case window.SVGPathSeg.PATHSEG_ARC_REL:return n={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegArcRel(e,n.x,n.y,n.x1,n.y1,n.arcAngle,n.arcLarge,n.arcSweep);case window.SVGPathSeg.PATHSEG_ARC_ABS:return n={x1:this._parseNumber(),y1:this._parseNumber(),arcAngle:this._parseNumber(),arcLarge:this._parseArcFlag(),arcSweep:this._parseArcFlag(),x:this._parseNumber(),y:this._parseNumber()},new window.SVGPathSegArcAbs(e,n.x,n.y,n.x1,n.y1,n.arcAngle,n.arcLarge,n.arcSweep);default:throw"Unknown path seg type."}};var a=new i,r=new n(t);if(!r.initialCommandIsMoveTo())return[];for(;r.hasMoreData();){var s=r.parseSegment();if(!s)return[];a.appendSegment(s)}return a.pathSegList}),b.axis=function(){},b.axis.labels=function(t){var e=this.internal;arguments.length&&(Object.keys(t).forEach(function(i){e.axis.setLabelText(i,t[i])}),e.axis.updateLabels())},b.axis.max=function(t){var e=this.internal,i=e.config;if(!arguments.length)return{x:i.axis_x_max,y:i.axis_y_max,y2:i.axis_y2_max};"object"===(void 0===t?"undefined":n(t))?(s(t.x)&&(i.axis_x_max=t.x),s(t.y)&&(i.axis_y_max=t.y),s(t.y2)&&(i.axis_y2_max=t.y2)):i.axis_y_max=i.axis_y2_max=t,e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})},b.axis.min=function(t){var e=this.internal,i=e.config;if(!arguments.length)return{x:i.axis_x_min,y:i.axis_y_min,y2:i.axis_y2_min};"object"===(void 0===t?"undefined":n(t))?(s(t.x)&&(i.axis_x_min=t.x),s(t.y)&&(i.axis_y_min=t.y),s(t.y2)&&(i.axis_y2_min=t.y2)):i.axis_y_min=i.axis_y2_min=t,e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})},b.axis.range=function(t){if(!arguments.length)return{max:this.axis.max(),min:this.axis.min()};l(t.max)&&this.axis.max(t.max),l(t.min)&&this.axis.min(t.min)},b.category=function(t,e){var i=this.internal,n=i.config;return arguments.length>1&&(n.axis_x_categories[t]=e,i.redraw()),n.axis_x_categories[t]},b.categories=function(t){var e=this.internal,i=e.config;return arguments.length?(i.axis_x_categories=t,e.redraw(),i.axis_x_categories):i.axis_x_categories},b.resize=function(t){var e=this.internal.config;e.size_width=t?t.width:null,e.size_height=t?t.height:null,this.flush()},b.flush=function(){this.internal.updateAndRedraw({withLegend:!0,withTransition:!1,withTransitionForTransform:!1})},b.destroy=function(){var t=this.internal;if(window.clearInterval(t.intervalForObserveInserted),void 0!==t.resizeTimeout&&window.clearTimeout(t.resizeTimeout),window.detachEvent)window.detachEvent("onresize",t.resizeIfElementDisplayed);else if(window.removeEventListener)window.removeEventListener("resize",t.resizeIfElementDisplayed);else{var e=window.onresize;e&&e.add&&e.remove&&e.remove(t.resizeFunction)}return t.resizeFunction.remove(),t.selectChart.classed("c3",!1).html(""),Object.keys(t).forEach(function(e){t[e]=null}),null},b.color=function(t){return this.internal.color(t)},b.data=function(t){var e=this.internal.data.targets;return void 0===t?e:e.filter(function(e){return[].concat(t).indexOf(e.id)>=0})},b.data.shown=function(t){return this.internal.filterTargetsToShow(this.data(t))},b.data.values=function(t){var e,i=null;return t&&(i=(e=this.data(t))[0]?e[0].values.map(function(t){return t.value}):null),i},b.data.names=function(t){return this.internal.clearLegendItemTextBoxCache(),this.internal.updateDataAttributes("names",t)},b.data.colors=function(t){return this.internal.updateDataAttributes("colors",t)},b.data.axes=function(t){return this.internal.updateDataAttributes("axes",t)},b.flow=function(t){var e,i,n,a,r,o,c,d=this.internal,u=[],h=d.getMaxDataCount(),g=0,f=0;if(t.json)i=d.convertJsonToData(t.json,t.keys);else if(t.rows)i=d.convertRowsToData(t.rows);else{if(!t.columns)return;i=d.convertColumnsToData(t.columns)}e=d.convertDataToTargets(i,!0),d.data.targets.forEach(function(t){var i,n,a=!1;for(i=0;i<e.length;i++)if(t.id===e[i].id){for(a=!0,t.values[t.values.length-1]&&(f=t.values[t.values.length-1].index+1),g=e[i].values.length,n=0;n<g;n++)e[i].values[n].index=f+n,d.isTimeSeries()||(e[i].values[n].x=f+n);t.values=t.values.concat(e[i].values),e.splice(i,1);break}a||u.push(t.id)}),d.data.targets.forEach(function(t){var e,i;for(e=0;e<u.length;e++)if(t.id===u[e])for(f=t.values[t.values.length-1].index+1,i=0;i<g;i++)t.values.push({id:t.id,index:f+i,x:d.isTimeSeries()?d.getOtherTargetX(f+i):f+i,value:null})}),d.data.targets.length&&e.forEach(function(t){var e,i=[];for(e=d.data.targets[0].values[0].index;e<f;e++)i.push({id:t.id,index:e,x:d.isTimeSeries()?d.getOtherTargetX(e):e,value:null});t.values.forEach(function(t){t.index+=f,d.isTimeSeries()||(t.x+=f)}),t.values=i.concat(t.values)}),d.data.targets=d.data.targets.concat(e),d.getMaxDataCount(),r=(a=d.data.targets[0]).values[0],l(t.to)?(g=0,c=d.isTimeSeries()?d.parseDate(t.to):t.to,a.values.forEach(function(t){t.x<c&&g++})):l(t.length)&&(g=t.length),h?1===h&&d.isTimeSeries()&&(o=(a.values[a.values.length-1].x-r.x)/2,n=[new Date(+r.x-o),new Date(+r.x+o)],d.updateXDomain(null,!0,!0,!1,n)):(o=d.isTimeSeries()?a.values.length>1?a.values[a.values.length-1].x-r.x:r.x-d.getXDomain(d.data.targets)[0]:1,n=[r.x-o,r.x],d.updateXDomain(null,!0,!0,!1,n)),d.updateTargets(d.data.targets),d.redraw({flow:{index:r.index,length:g,duration:s(t.duration)?t.duration:d.config.transition_duration,done:t.done,orgDataCount:h},withLegend:!0,withTransition:h>1,withTrimXDomain:!1,withUpdateXAxis:!0})},T.generateFlow=function(t){var e=this,n=e.config,a=e.d3;return function(){var r,s,o,c,d,u,l,h,g,p,_,x=t.targets,y=t.flow,m=t.drawBar,S=t.drawLine,w=t.drawArea,v=t.cx,b=t.cy,T=t.xv,A=t.xForText,P=t.yForText,C=t.duration,L=y.index,V=y.length,G=e.getValueOnIndex(e.data.targets[0].values,L),E=e.getValueOnIndex(e.data.targets[0].values,L+V),O=e.x.domain(),I=y.duration||C,R=y.done||function(){},k=e.generateWait();e.flowing=!0,e.data.targets.forEach(function(t){t.values.splice(0,V)}),o=e.updateXDomain(x,!0,!0),e.updateXGrid&&e.updateXGrid(!0),c=e.xgrid||a.selectAll([]),d=e.xgridLines||a.selectAll([]),u=e.mainRegion||a.selectAll([]),l=e.mainText||a.selectAll([]),h=e.mainBar||a.selectAll([]),g=e.mainLine||a.selectAll([]),p=e.mainArea||a.selectAll([]),_=e.mainCircle||a.selectAll([]),y.orgDataCount?r=1===y.orgDataCount||(G&&G.x)===(E&&E.x)?e.x(O[0])-e.x(o[0]):e.isTimeSeries()?e.x(O[0])-e.x(o[0]):e.x(G.x)-e.x(E.x):1!==e.data.targets[0].values.length?r=e.x(O[0])-e.x(o[0]):e.isTimeSeries()?(G=e.getValueOnIndex(e.data.targets[0].values,0),E=e.getValueOnIndex(e.data.targets[0].values,e.data.targets[0].values.length-1),r=e.x(G.x)-e.x(E.x)):r=f(o)/2,s="translate("+r+",0) scale("+f(O)/f(o)+",1)",e.hideXGridFocus();var D=a.transition().ease(a.easeLinear).duration(I);k.add(e.xAxis(e.axes.x,D)),k.add(h.transition(D).attr("transform",s)),k.add(g.transition(D).attr("transform",s)),k.add(p.transition(D).attr("transform",s)),k.add(_.transition(D).attr("transform",s)),k.add(l.transition(D).attr("transform",s)),k.add(u.filter(e.isRegionOnX).transition(D).attr("transform",s)),k.add(c.transition(D).attr("transform",s)),k.add(d.transition(D).attr("transform",s)),k(function(){var t,a=[],r=[];if(V){for(t=0;t<V;t++)a.push("."+i.shape+"-"+(L+t)),r.push("."+i.text+"-"+(L+t));e.svg.selectAll("."+i.shapes).selectAll(a).remove(),e.svg.selectAll("."+i.texts).selectAll(r).remove(),e.svg.select("."+i.xgrid).remove()}c.attr("transform",null).attr("x1",e.xgridAttr.x1).attr("x2",e.xgridAttr.x2).attr("y1",e.xgridAttr.y1).attr("y2",e.xgridAttr.y2).style("opacity",e.xgridAttr.opacity),d.attr("transform",null),d.select("line").attr("x1",n.axis_rotated?0:T).attr("x2",n.axis_rotated?e.width:T),d.select("text").attr("x",n.axis_rotated?e.width:0).attr("y",T),h.attr("transform",null).attr("d",m),g.attr("transform",null).attr("d",S),p.attr("transform",null).attr("d",w),_.attr("transform",null).attr("cx",v).attr("cy",b),l.attr("transform",null).attr("x",A).attr("y",P).style("fill-opacity",e.opacityForText.bind(e)),u.attr("transform",null),u.filter(e.isRegionOnX).attr("x",e.regionX.bind(e)).attr("width",e.regionWidth.bind(e)),R(),e.flowing=!1})}},b.focus=function(t){var e,n=this.internal;t=n.mapToTargetIds(t),e=n.svg.selectAll(n.selectorTargets(t.filter(n.isTargetToShow,n))),this.revert(),this.defocus(),e.classed(i.focused,!0).classed(i.defocused,!1),n.hasArcType()&&n.expandArc(t),n.toggleFocusLegend(t,!0),n.focusedTargetIds=t,n.defocusedTargetIds=n.defocusedTargetIds.filter(function(e){return t.indexOf(e)<0})},b.defocus=function(t){var e=this.internal;t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t.filter(e.isTargetToShow,e))).classed(i.focused,!1).classed(i.defocused,!0),e.hasArcType()&&e.unexpandArc(t),e.toggleFocusLegend(t,!1),e.focusedTargetIds=e.focusedTargetIds.filter(function(e){return t.indexOf(e)<0}),e.defocusedTargetIds=t},b.revert=function(t){var e=this.internal;t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t)).classed(i.focused,!1).classed(i.defocused,!1),e.hasArcType()&&e.unexpandArc(t),e.config.legend_show&&(e.showLegend(t.filter(e.isLegendToShow.bind(e))),e.legend.selectAll(e.selectorLegends(t)).filter(function(){return e.d3.select(this).classed(i.legendItemFocused)}).classed(i.legendItemFocused,!1)),e.focusedTargetIds=[],e.defocusedTargetIds=[]},b.xgrids=function(t){var e=this.internal,i=e.config;return t?(i.grid_x_lines=t,e.redrawWithoutRescale(),i.grid_x_lines):i.grid_x_lines},b.xgrids.add=function(t){var e=this.internal;return this.xgrids(e.config.grid_x_lines.concat(t||[]))},b.xgrids.remove=function(t){this.internal.removeGridLines(t,!0)},b.ygrids=function(t){var e=this.internal,i=e.config;return t?(i.grid_y_lines=t,e.redrawWithoutRescale(),i.grid_y_lines):i.grid_y_lines},b.ygrids.add=function(t){var e=this.internal;return this.ygrids(e.config.grid_y_lines.concat(t||[]))},b.ygrids.remove=function(t){this.internal.removeGridLines(t,!1)},b.groups=function(t){var e=this.internal,i=e.config;return u(t)?i.data_groups:(i.data_groups=t,e.redraw(),i.data_groups)},b.legend=function(){},b.legend.show=function(t){var e=this.internal;e.showLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},b.legend.hide=function(t){var e=this.internal;e.hideLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},b.load=function(t){var e=this.internal,i=e.config;t.xs&&e.addXs(t.xs),"names"in t&&b.data.names.bind(this)(t.names),"classes"in t&&Object.keys(t.classes).forEach(function(e){i.data_classes[e]=t.classes[e]}),"categories"in t&&e.isCategorized()&&(i.axis_x_categories=t.categories),"axes"in t&&Object.keys(t.axes).forEach(function(e){i.data_axes[e]=t.axes[e]}),"colors"in t&&Object.keys(t.colors).forEach(function(e){i.data_colors[e]=t.colors[e]}),"cacheIds"in t&&e.hasCaches(t.cacheIds)?e.load(e.getCaches(t.cacheIds),t.done):"unload"in t?e.unload(e.mapToTargetIds("boolean"==typeof t.unload&&t.unload?null:t.unload),function(){e.loadFromArgs(t)}):e.loadFromArgs(t)},b.unload=function(t){var e=this.internal;(t=t||{})instanceof Array?t={ids:t}:"string"==typeof t&&(t={ids:[t]}),e.unload(e.mapToTargetIds(t.ids),function(){e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),t.done&&t.done()})},b.regions=function(t){var e=this.internal,i=e.config;return t?(i.regions=t,e.redrawWithoutRescale(),i.regions):i.regions},b.regions.add=function(t){var e=this.internal,i=e.config;return t?(i.regions=i.regions.concat(t),e.redrawWithoutRescale(),i.regions):i.regions},b.regions.remove=function(t){var e,n,a,r=this.internal,s=r.config;return t=t||{},e=r.getOption(t,"duration",s.transition_duration),n=r.getOption(t,"classes",[i.region]),a=r.main.select("."+i.regions).selectAll(n.map(function(t){return"."+t})),(e?a.transition().duration(e):a).style("opacity",0).remove(),s.regions=s.regions.filter(function(t){var e=!1;return!t.class||(t.class.split(" ").forEach(function(t){n.indexOf(t)>=0&&(e=!0)}),!e)}),s.regions},b.selected=function(t){var e=this.internal,n=e.d3;return n.merge(e.main.selectAll("."+i.shapes+e.getTargetSelectorSuffix(t)).selectAll("."+i.shape).filter(function(){return n.select(this).classed(i.SELECTED)}).map(function(t){return t.map(function(t){var e=t.__data__;return e.data?e.data:e})}))},b.select=function(t,e,n){var a=this.internal,r=a.d3,s=a.config;s.data_selection_enabled&&a.main.selectAll("."+i.shapes).selectAll("."+i.shape).each(function(o,c){var d=r.select(this),u=o.data?o.data.id:o.id,h=a.getToggle(this,o).bind(a),g=s.data_selection_grouped||!t||t.indexOf(u)>=0,f=!e||e.indexOf(c)>=0,p=d.classed(i.SELECTED);d.classed(i.line)||d.classed(i.area)||(g&&f?s.data_selection_isselectable(o)&&!p&&h(!0,d.classed(i.SELECTED,!0),o,c):l(n)&&n&&p&&h(!1,d.classed(i.SELECTED,!1),o,c))})},b.unselect=function(t,e){var n=this.internal,a=n.d3,r=n.config;r.data_selection_enabled&&n.main.selectAll("."+i.shapes).selectAll("."+i.shape).each(function(s,o){var c=a.select(this),d=s.data?s.data.id:s.id,u=n.getToggle(this,s).bind(n),l=r.data_selection_grouped||!t||t.indexOf(d)>=0,h=!e||e.indexOf(o)>=0,g=c.classed(i.SELECTED);c.classed(i.line)||c.classed(i.area)||l&&h&&r.data_selection_isselectable(s)&&g&&u(!1,c.classed(i.SELECTED,!1),s,o)})},b.show=function(t,e){var i,n=this.internal;t=n.mapToTargetIds(t),e=e||{},n.removeHiddenTargetIds(t),(i=n.svg.selectAll(n.selectorTargets(t))).transition().style("opacity",1,"important").call(n.endall,function(){i.style("opacity",null).style("opacity",1)}),e.withLegend&&n.showLegend(t),n.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},b.hide=function(t,e){var i,n=this.internal;t=n.mapToTargetIds(t),e=e||{},n.addHiddenTargetIds(t),(i=n.svg.selectAll(n.selectorTargets(t))).transition().style("opacity",0,"important").call(n.endall,function(){i.style("opacity",null).style("opacity",0)}),e.withLegend&&n.hideLegend(t),n.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0})},b.toggle=function(t,e){var i=this,n=this.internal;n.mapToTargetIds(t).forEach(function(t){n.isTargetToShow(t)?i.hide(t,e):i.show(t,e)})},b.tooltip=function(){},b.tooltip.show=function(t){var e,i,n=this.internal,a={};t.mouse?a=t.mouse:(t.data?i=t.data:void 0!==t.x&&(e=t.id?n.data.targets.filter(function(e){return e.id===t.id}):n.data.targets,i=n.filterByX(e,t.x).slice(0,1)[0]),a=i?n.getMousePosition(i):null),n.dispatchEvent("mousemove",a),n.config.tooltip_onshow.call(n,i)},b.tooltip.hide=function(){this.internal.dispatchEvent("mouseout",0),this.internal.config.tooltip_onhide.call(this)},b.transform=function(t,e){var i=this.internal,n=["pie","donut"].indexOf(t)>=0?{withTransform:!0}:null;i.transformTo(e,t,n)},T.transformTo=function(t,e,i){var n=this,a=!n.hasArcType(),r=i||{withTransitionForAxis:a};r.withTransitionForTransform=!1,n.transiting=!1,n.setTargetType(t,e),n.updateTargets(n.data.targets),n.updateAndRedraw(r)},b.x=function(t){var e=this.internal;return arguments.length&&(e.updateTargetX(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs},b.xs=function(t){var e=this.internal;return arguments.length&&(e.updateTargetXs(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs},b.zoom=function(t){var e=this.internal;return t&&(e.isTimeSeries()&&(t=t.map(function(t){return e.parseDate(t)})),e.config.subchart_show?e.brush.selectionAsValue(t,!0):(e.updateXDomain(null,!0,!1,!1,t),e.redraw({withY:e.config.zoom_rescale,withSubchart:!1})),e.config.zoom_onzoom.call(this,e.x.orgDomain())),t},b.zoom.enable=function(t){var e=this.internal;e.config.zoom_enabled=t,e.updateAndRedraw()},b.unzoom=function(){var t=this.internal;t.config.subchart_show?t.brush.clear():(t.updateXDomain(null,!0,!1,!1,t.subX.domain()),t.redraw({withY:t.config.zoom_rescale,withSubchart:!1}))},b.zoom.max=function(t){var e=this.internal,i=e.config,n=e.d3;if(0!==t&&!t)return i.zoom_x_max;i.zoom_x_max=n.max([e.orgXDomain[1],t])},b.zoom.min=function(t){var e=this.internal,i=e.config,n=e.d3;if(0!==t&&!t)return i.zoom_x_min;i.zoom_x_min=n.min([e.orgXDomain[0],t])},b.zoom.range=function(t){if(!arguments.length)return{max:this.domain.max(),min:this.domain.min()};l(t.max)&&this.domain.max(t.max),l(t.min)&&this.domain.min(t.min)},T.initPie=function(){var t=this,e=t.d3;t.pie=e.pie().value(function(t){return t.values.reduce(function(t,e){return t+e.value},0)});var i=t.getOrderFunction();if(i&&(t.isOrderAsc()||t.isOrderDesc())){var n=i;i=function(t,e){return-1*n(t,e)}}t.pie.sort(i||null)},T.updateRadius=function(){var t=this,e=t.config,i=e.gauge_width||e.donut_width,n=t.filterTargetsToShow(t.data.targets).length*t.config.gauge_arcs_minWidth;t.radiusExpanded=Math.min(t.arcWidth,t.arcHeight)/2*(t.hasType("gauge")?.85:1),t.radius=.95*t.radiusExpanded,t.innerRadiusRatio=i?(t.radius-i)/t.radius:.6,t.innerRadius=t.hasType("donut")||t.hasType("gauge")?t.radius*t.innerRadiusRatio:0,t.gaugeArcWidth=i||(n<=t.radius-t.innerRadius?t.radius-t.innerRadius:n<=t.radius?n:t.radius)},T.updateArc=function(){var t=this;t.svgArc=t.getSvgArc(),t.svgArcExpanded=t.getSvgArcExpanded(),t.svgArcExpandedSub=t.getSvgArcExpanded(.98)},T.updateAngle=function(t){var e,i,n,a,r=this,s=r.config,o=!1,c=0;return s?(r.pie(r.filterTargetsToShow(r.data.targets)).forEach(function(e){o||e.data.id!==t.data.id||(o=!0,(t=e).index=c),c++}),isNaN(t.startAngle)&&(t.startAngle=0),isNaN(t.endAngle)&&(t.endAngle=t.startAngle),r.isGaugeType(t.data)&&(e=s.gauge_min,i=s.gauge_max,n=Math.PI*(s.gauge_fullCircle?2:1)/(i-e),a=t.value<e?0:t.value<i?t.value-e:i-e,t.startAngle=s.gauge_startingAngle,t.endAngle=t.startAngle+n*a),o?t:null):null},T.getSvgArc=function(){var t=this,e=t.hasType("gauge"),i=t.gaugeArcWidth/t.filterTargetsToShow(t.data.targets).length,n=t.d3.arc().outerRadius(function(n){return e?t.radius-i*n.index:t.radius}).innerRadius(function(n){return e?t.radius-i*(n.index+1):t.innerRadius}),a=function(e,i){var a;return i?n(e):(a=t.updateAngle(e))?n(a):"M 0 0"};return a.centroid=n.centroid,a},T.getSvgArcExpanded=function(t){t=t||1;var e=this,i=e.hasType("gauge"),n=e.gaugeArcWidth/e.filterTargetsToShow(e.data.targets).length,a=Math.min(e.radiusExpanded*t-e.radius,.8*n-100*(1-t)),r=e.d3.arc().outerRadius(function(r){return i?e.radius-n*r.index+a:e.radiusExpanded*t}).innerRadius(function(t){return i?e.radius-n*(t.index+1):e.innerRadius});return function(t){var i=e.updateAngle(t);return i?r(i):"M 0 0"}},T.getArc=function(t,e,i){return i||this.isArcType(t.data)?this.svgArc(t,e):"M 0 0"},T.transformForArcLabel=function(t){var e,i,n,a,r,s=this,c=s.config,d=s.updateAngle(t),u="",l=s.hasType("gauge");if(d&&!l)e=this.svgArc.centroid(d),i=isNaN(e[0])?0:e[0],n=isNaN(e[1])?0:e[1],a=Math.sqrt(i*i+n*n),u="translate("+i*(r=s.hasType("donut")&&c.donut_label_ratio?o(c.donut_label_ratio)?c.donut_label_ratio(t,s.radius,a):c.donut_label_ratio:s.hasType("pie")&&c.pie_label_ratio?o(c.pie_label_ratio)?c.pie_label_ratio(t,s.radius,a):c.pie_label_ratio:s.radius&&a?(36/s.radius>.375?1.175-36/s.radius:.8)*s.radius/a:0)+","+n*r+")";else if(d&&l&&s.filterTargetsToShow(s.data.targets).length>1){var h=Math.sin(d.endAngle-Math.PI/2);u="translate("+(i=Math.cos(d.endAngle-Math.PI/2)*(s.radiusExpanded+25))+","+(n=h*(s.radiusExpanded+15-Math.abs(10*h))+3)+")"}return u},T.getArcRatio=function(t){var e=this.config,i=Math.PI*(this.hasType("gauge")&&!e.gauge_fullCircle?1:2);return t?(t.endAngle-t.startAngle)/i:null},T.convertToArcData=function(t){return this.addName({id:t.data.id,value:t.value,ratio:this.getArcRatio(t),index:t.index})},T.textForArcLabel=function(t){var e,i,n,a,r,s=this;return s.shouldShowArcLabel()?(i=(e=s.updateAngle(t))?e.value:null,n=s.getArcRatio(e),a=t.data.id,s.hasType("gauge")||s.meetsArcLabelThreshold(n)?(r=s.getArcLabelFormat())?r(i,n,a):s.defaultArcValueFormat(i,n):""):""},T.textForGaugeMinMax=function(t,e){var i=this.getGaugeLabelExtents();return i?i(t,e):t},T.expandArc=function(t){var e,n=this;n.transiting?e=window.setInterval(function(){n.transiting||(window.clearInterval(e),n.legend.selectAll(".c3-legend-item-focused").size()>0&&n.expandArc(t))},10):(t=n.mapToTargetIds(t),n.svg.selectAll(n.selectorTargets(t,"."+i.chartArc)).each(function(t){n.shouldExpand(t.data.id)&&n.d3.select(this).selectAll("path").transition().duration(n.expandDuration(t.data.id)).attr("d",n.svgArcExpanded).transition().duration(2*n.expandDuration(t.data.id)).attr("d",n.svgArcExpandedSub).each(function(t){n.isDonutType(t.data)})}))},T.unexpandArc=function(t){var e=this;e.transiting||(t=e.mapToTargetIds(t),e.svg.selectAll(e.selectorTargets(t,"."+i.chartArc)).selectAll("path").transition().duration(function(t){return e.expandDuration(t.data.id)}).attr("d",e.svgArc),e.svg.selectAll("."+i.arc))},T.expandDuration=function(t){var e=this.config;return this.isDonutType(t)?e.donut_expand_duration:this.isGaugeType(t)?e.gauge_expand_duration:this.isPieType(t)?e.pie_expand_duration:50},T.shouldExpand=function(t){var e=this.config;return this.isDonutType(t)&&e.donut_expand||this.isGaugeType(t)&&e.gauge_expand||this.isPieType(t)&&e.pie_expand},T.shouldShowArcLabel=function(){var t=this.config,e=!0;return this.hasType("donut")?e=t.donut_label_show:this.hasType("pie")&&(e=t.pie_label_show),e},T.meetsArcLabelThreshold=function(t){var e=this.config;return t>=(this.hasType("donut")?e.donut_label_threshold:e.pie_label_threshold)},T.getArcLabelFormat=function(){var t=this.config,e=t.pie_label_format;return this.hasType("gauge")?e=t.gauge_label_format:this.hasType("donut")&&(e=t.donut_label_format),e},T.getGaugeLabelExtents=function(){return this.config.gauge_label_extents},T.getArcTitle=function(){return this.hasType("donut")?this.config.donut_title:""},T.updateTargetsForArc=function(t){var e,n=this,a=n.main,r=n.classChartArc.bind(n),s=n.classArcs.bind(n),o=n.classFocus.bind(n);(e=a.select("."+i.chartArcs).selectAll("."+i.chartArc).data(n.pie(t)).attr("class",function(t){return r(t)+o(t.data)}).enter().append("g").attr("class",r)).append("g").attr("class",s),e.append("text").attr("dy",n.hasType("gauge")?"-.1em":".35em").style("opacity",0).style("text-anchor","middle").style("pointer-events","none")},T.initArc=function(){var t=this;t.arcs=t.main.select("."+i.chart).append("g").attr("class",i.chartArcs).attr("transform",t.getTranslate("arc")),t.arcs.append("text").attr("class",i.chartArcsTitle).style("text-anchor","middle").text(t.getArcTitle())},T.redrawArc=function(t,e,n){var a,r,s,o,c,d=this,u=d.d3,l=d.config,h=d.main,g=d.hasType("gauge");if(r=(a=h.selectAll("."+i.arcs).selectAll("."+i.arc).data(d.arcData.bind(d))).enter().append("path").attr("class",d.classArc.bind(d)).style("fill",function(t){return d.color(t.data)}).style("cursor",function(t){return l.interaction_enabled&&l.data_selection_isselectable(t)?"pointer":null}).each(function(t){d.isGaugeType(t.data)&&(t.startAngle=t.endAngle=l.gauge_startingAngle),this._current=t}).merge(a),g&&(c=(o=h.selectAll("."+i.arcs).selectAll("."+i.arcLabelLine).data(d.arcData.bind(d))).enter().append("rect").attr("class",function(t){return i.arcLabelLine+" "+i.target+" "+i.target+"-"+t.data.id}).merge(o),1===d.filterTargetsToShow(d.data.targets).length?c.style("display","none"):c.style("fill",function(t){return l.color_pattern.length>0?d.levelColor(t.data.values[0].value):d.color(t.data)}).style("display",l.gauge_labelLine_show?"":"none").each(function(t){var e=0,i=0,n=0,a="";if(d.hiddenTargetIds.indexOf(t.data.id)<0){var r=d.updateAngle(t),s=d.gaugeArcWidth/d.filterTargetsToShow(d.data.targets).length*(r.index+1),o=r.endAngle-Math.PI/2,c=d.radius-s,l=o-(0===c?0:1/c);e=d.radiusExpanded-d.radius+s,i=Math.cos(l)*c,n=Math.sin(l)*c,a="rotate("+180*o/Math.PI+", "+i+", "+n+")"}u.select(this).attr("x",i).attr("y",n).attr("width",e).attr("height",2).attr("transform",a).style("stroke-dasharray","0, "+(e+2)+", 0")})),r.attr("transform",function(t){return!d.isGaugeType(t.data)&&n?"scale(0)":""}).on("mouseover",l.interaction_enabled?function(t){var e,i;d.transiting||(e=d.updateAngle(t))&&(i=d.convertToArcData(e),d.expandArc(e.data.id),d.api.focus(e.data.id),d.toggleFocusLegend(e.data.id,!0),d.config.data_onmouseover(i,this))}:null).on("mousemove",l.interaction_enabled?function(t){var e,i=d.updateAngle(t);i&&(e=[d.convertToArcData(i)],d.showTooltip(e,this))}:null).on("mouseout",l.interaction_enabled?function(t){var e,i;d.transiting||(e=d.updateAngle(t))&&(i=d.convertToArcData(e),d.unexpandArc(e.data.id),d.api.revert(),d.revertLegend(),d.hideTooltip(),d.config.data_onmouseout(i,this))}:null).on("click",l.interaction_enabled?function(t,e){var i,n=d.updateAngle(t);n&&(i=d.convertToArcData(n),d.toggleShape&&d.toggleShape(this,i,e),d.config.data_onclick.call(d.api,i,this))}:null).each(function(){d.transiting=!0}).transition().duration(t).attrTween("d",function(t){var e,i=d.updateAngle(t);return i?(isNaN(this._current.startAngle)&&(this._current.startAngle=0),isNaN(this._current.endAngle)&&(this._current.endAngle=this._current.startAngle),e=u.interpolate(this._current,i),this._current=e(0),function(i){var n=e(i);return n.data=t.data,d.getArc(n,!0)}):function(){return"M 0 0"}}).attr("transform",n?"scale(1)":"").style("fill",function(t){return d.levelColor?d.levelColor(t.data.values[0].value):d.color(t.data.id)}).call(d.endall,function(){d.transiting=!1}),a.exit().transition().duration(e).style("opacity",0).remove(),h.selectAll("."+i.chartArc).select("text").style("opacity",0).attr("class",function(t){return d.isGaugeType(t.data)?i.gaugeValue:""}).text(d.textForArcLabel.bind(d)).attr("transform",d.transformForArcLabel.bind(d)).style("font-size",function(t){return d.isGaugeType(t.data)&&1===d.filterTargetsToShow(d.data.targets).length?Math.round(d.radius/5)+"px":""}).transition().duration(t).style("opacity",function(t){return d.isTargetToShow(t.data.id)&&d.isArcType(t.data)?1:0}),h.select("."+i.chartArcsTitle).style("opacity",d.hasType("donut")||g?1:0),g){var f=0;(s=d.arcs.select("g."+i.chartArcsBackground).selectAll("path."+i.chartArcsBackground).data(d.data.targets)).enter().append("path"),s.attr("class",function(t,e){return i.chartArcsBackground+" "+i.chartArcsBackground+"-"+e}).attr("d",function(t){if(d.hiddenTargetIds.indexOf(t.id)>=0)return"M 0 0";var e={data:[{value:l.gauge_max}],startAngle:l.gauge_startingAngle,endAngle:-1*l.gauge_startingAngle*(l.gauge_fullCircle?Math.PI:1),index:f++};return d.getArc(e,!0,!0)}),s.exit().remove(),d.arcs.select("."+i.chartArcsGaugeUnit).attr("dy",".75em").text(l.gauge_label_show?l.gauge_units:""),d.arcs.select("."+i.chartArcsGaugeMin).attr("dx",-1*(d.innerRadius+(d.radius-d.innerRadius)/(l.gauge_fullCircle?1:2))+"px").attr("dy","1.2em").text(l.gauge_label_show?d.textForGaugeMinMax(l.gauge_min,!1):""),d.arcs.select("."+i.chartArcsGaugeMax).attr("dx",d.innerRadius+(d.radius-d.innerRadius)/(l.gauge_fullCircle?1:2)+"px").attr("dy","1.2em").text(l.gauge_label_show?d.textForGaugeMinMax(l.gauge_max,!0):"")}},T.initGauge=function(){var t=this.arcs;this.hasType("gauge")&&(t.append("g").attr("class",i.chartArcsBackground),t.append("text").attr("class",i.chartArcsGaugeUnit).style("text-anchor","middle").style("pointer-events","none"),t.append("text").attr("class",i.chartArcsGaugeMin).style("text-anchor","middle").style("pointer-events","none"),t.append("text").attr("class",i.chartArcsGaugeMax).style("text-anchor","middle").style("pointer-events","none"))},T.getGaugeLabelHeight=function(){return this.config.gauge_label_show?20:0},T.hasCaches=function(t){for(var e=0;e<t.length;e++)if(!(t[e]in this.cache))return!1;return!0},T.addCache=function(t,e){this.cache[t]=this.cloneTarget(e)},T.getCaches=function(t){var e,i=[];for(e=0;e<t.length;e++)t[e]in this.cache&&i.push(this.cloneTarget(this.cache[t[e]]));return i},T.categoryName=function(t){var e=this.config;return t<e.axis_x_categories.length?e.axis_x_categories[t]:t},T.generateTargetClass=function(t){return t||0===t?("-"+t).replace(/\s/g,"-"):""},T.generateClass=function(t,e){return" "+t+" "+t+this.generateTargetClass(e)},T.classText=function(t){return this.generateClass(i.text,t.index)},T.classTexts=function(t){return this.generateClass(i.texts,t.id)},T.classShape=function(t){return this.generateClass(i.shape,t.index)},T.classShapes=function(t){return this.generateClass(i.shapes,t.id)},T.classLine=function(t){return this.classShape(t)+this.generateClass(i.line,t.id)},T.classLines=function(t){return this.classShapes(t)+this.generateClass(i.lines,t.id)},T.classCircle=function(t){return this.classShape(t)+this.generateClass(i.circle,t.index)},T.classCircles=function(t){return this.classShapes(t)+this.generateClass(i.circles,t.id)},T.classBar=function(t){return this.classShape(t)+this.generateClass(i.bar,t.index)},T.classBars=function(t){return this.classShapes(t)+this.generateClass(i.bars,t.id)},T.classArc=function(t){return this.classShape(t.data)+this.generateClass(i.arc,t.data.id)},T.classArcs=function(t){return this.classShapes(t.data)+this.generateClass(i.arcs,t.data.id)},T.classArea=function(t){return this.classShape(t)+this.generateClass(i.area,t.id)},T.classAreas=function(t){return this.classShapes(t)+this.generateClass(i.areas,t.id)},T.classRegion=function(t,e){return this.generateClass(i.region,e)+" "+("class"in t?t.class:"")},T.classEvent=function(t){return this.generateClass(i.eventRect,t.index)},T.classTarget=function(t){var e=this.config.data_classes[t],n="";return e&&(n=" "+i.target+"-"+e),this.generateClass(i.target,t)+n},T.classFocus=function(t){return this.classFocused(t)+this.classDefocused(t)},T.classFocused=function(t){return" "+(this.focusedTargetIds.indexOf(t.id)>=0?i.focused:"")},T.classDefocused=function(t){return" "+(this.defocusedTargetIds.indexOf(t.id)>=0?i.defocused:"")},T.classChartText=function(t){return i.chartText+this.classTarget(t.id)},T.classChartLine=function(t){return i.chartLine+this.classTarget(t.id)},T.classChartBar=function(t){return i.chartBar+this.classTarget(t.id)},T.classChartArc=function(t){return i.chartArc+this.classTarget(t.data.id)},T.getTargetSelectorSuffix=function(t){return this.generateTargetClass(t).replace(/([?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\])/g,"\\$1")},T.selectorTarget=function(t,e){return(e||"")+"."+i.target+this.getTargetSelectorSuffix(t)},T.selectorTargets=function(t,e){var i=this;return(t=t||[]).length?t.map(function(t){return i.selectorTarget(t,e)}):null},T.selectorLegend=function(t){return"."+i.legendItem+this.getTargetSelectorSuffix(t)},T.selectorLegends=function(t){var e=this;return t&&t.length?t.map(function(t){return e.selectorLegend(t)}):null},T.getClipPath=function(t){return"url("+(window.navigator.appVersion.toLowerCase().indexOf("msie 9.")>=0?"":document.URL.split("#")[0])+"#"+t+")"},T.appendClip=function(t,e){return t.append("clipPath").attr("id",e).append("rect")},T.getAxisClipX=function(t){var e=Math.max(30,this.margin.left);return t?-(1+e):-(e-1)},T.getAxisClipY=function(t){return t?-20:-this.margin.top},T.getXAxisClipX=function(){return this.getAxisClipX(!this.config.axis_rotated)},T.getXAxisClipY=function(){return this.getAxisClipY(!this.config.axis_rotated)},T.getYAxisClipX=function(){return this.config.axis_y_inner?-1:this.getAxisClipX(this.config.axis_rotated)},T.getYAxisClipY=function(){return this.getAxisClipY(this.config.axis_rotated)},T.getAxisClipWidth=function(t){var e=Math.max(30,this.margin.left),i=Math.max(30,this.margin.right);return t?this.width+2+e+i:this.margin.left+20},T.getAxisClipHeight=function(t){return(t?this.margin.bottom:this.margin.top+this.height)+20},T.getXAxisClipWidth=function(){return this.getAxisClipWidth(!this.config.axis_rotated)},T.getXAxisClipHeight=function(){return this.getAxisClipHeight(!this.config.axis_rotated)},T.getYAxisClipWidth=function(){return this.getAxisClipWidth(this.config.axis_rotated)+(this.config.axis_y_inner?20:0)},T.getYAxisClipHeight=function(){return this.getAxisClipHeight(this.config.axis_rotated)},T.generateColor=function(){var t=this.config,e=this.d3,i=t.data_colors,n=_(t.color_pattern)?t.color_pattern:e.schemeCategory10,a=t.data_color,r=[];return function(t){var e,s=t.id||t.data&&t.data.id||t;return i[s]instanceof Function?e=i[s](t):i[s]?e=i[s]:(r.indexOf(s)<0&&r.push(s),e=n[r.indexOf(s)%n.length],i[s]=e),a instanceof Function?a(e,t):e}},T.generateLevelColor=function(){var t=this.config,e=t.color_pattern,i=t.color_threshold,n="value"===i.unit,a=i.values&&i.values.length?i.values:[],r=i.max||100;return _(t.color_threshold)?function(t){var i,s=e[e.length-1];for(i=0;i<a.length;i++)if((n?t:100*t/r)<a[i]){s=e[i];break}return s}:null},T.getDefaultConfig=function(){var t={bindto:"#chart",svg_classname:void 0,size_width:void 0,size_height:void 0,padding_left:void 0,padding_right:void 0,padding_top:void 0,padding_bottom:void 0,resize_auto:!0,zoom_enabled:!1,zoom_initialRange:void 0,zoom_privileged:!1,zoom_rescale:!1,zoom_onzoom:function(){},zoom_onzoomstart:function(){},zoom_onzoomend:function(){},zoom_x_min:void 0,zoom_x_max:void 0,interaction_brighten:!0,interaction_enabled:!0,onmouseover:function(){},onmouseout:function(){},onresize:function(){},onresized:function(){},oninit:function(){},onrendered:function(){},transition_duration:350,data_x:void 0,data_xs:{},data_xFormat:"%Y-%m-%d",data_xLocaltime:!0,data_xSort:!0,data_idConverter:function(t){return t},data_names:{},data_classes:{},data_groups:[],data_axes:{},data_type:void 0,data_types:{},data_labels:{},data_order:"desc",data_regions:{},data_color:void 0,data_colors:{},data_hide:!1,data_filter:void 0,data_selection_enabled:!1,data_selection_grouped:!1,data_selection_isselectable:function(){return!0},data_selection_multiple:!0,data_selection_draggable:!1,data_onclick:function(){},data_onmouseover:function(){},data_onmouseout:function(){},data_onselected:function(){},data_onunselected:function(){},data_url:void 0,data_headers:void 0,data_json:void 0,data_rows:void 0,data_columns:void 0,data_mimeType:void 0,data_keys:void 0,data_empty_label_text:"",subchart_show:!1,subchart_size_height:60,subchart_axis_x_show:!0,subchart_onbrush:function(){},color_pattern:[],color_threshold:{},legend_show:!0,legend_hide:!1,legend_position:"bottom",legend_inset_anchor:"top-left",legend_inset_x:10,legend_inset_y:0,legend_inset_step:void 0,legend_item_onclick:void 0,legend_item_onmouseover:void 0,legend_item_onmouseout:void 0,legend_equally:!1,legend_padding:0,legend_item_tile_width:10,legend_item_tile_height:10,axis_rotated:!1,axis_x_show:!0,axis_x_type:"indexed",axis_x_localtime:!0,axis_x_categories:[],axis_x_tick_centered:!1,axis_x_tick_format:void 0,axis_x_tick_culling:{},axis_x_tick_culling_max:10,axis_x_tick_count:void 0,axis_x_tick_fit:!0,axis_x_tick_values:null,axis_x_tick_rotate:0,axis_x_tick_outer:!0,axis_x_tick_multiline:!0,axis_x_tick_width:null,axis_x_max:void 0,axis_x_min:void 0,axis_x_padding:{},axis_x_height:void 0,axis_x_selection:void 0,axis_x_label:{},axis_x_inner:void 0,axis_y_show:!0,axis_y_type:void 0,axis_y_max:void 0,axis_y_min:void 0,axis_y_inverted:!1,axis_y_center:void 0,axis_y_inner:void 0,axis_y_label:{},axis_y_tick_format:void 0,axis_y_tick_outer:!0,axis_y_tick_values:null,axis_y_tick_rotate:0,axis_y_tick_count:void 0,axis_y_tick_time_type:void 0,axis_y_tick_time_interval:void 0,axis_y_padding:{},axis_y_default:void 0,axis_y2_show:!1,axis_y2_max:void 0,axis_y2_min:void 0,axis_y2_inverted:!1,axis_y2_center:void 0,axis_y2_inner:void 0,axis_y2_label:{},axis_y2_tick_format:void 0,axis_y2_tick_outer:!0,axis_y2_tick_values:null,axis_y2_tick_count:void 0,axis_y2_padding:{},axis_y2_default:void 0,grid_x_show:!1,grid_x_type:"tick",grid_x_lines:[],grid_y_show:!1,grid_y_lines:[],grid_y_ticks:10,grid_focus_show:!0,grid_lines_front:!0,point_show:!0,point_r:2.5,point_sensitivity:10,point_focus_expand_enabled:!0,point_focus_expand_r:void 0,point_select_r:void 0,line_connectNull:!1,line_step_type:"step",bar_width:void 0,bar_width_ratio:.6,bar_width_max:void 0,bar_zerobased:!0,bar_space:0,area_zerobased:!0,area_above:!1,pie_label_show:!0,pie_label_format:void 0,pie_label_threshold:.05,pie_label_ratio:void 0,pie_expand:{},pie_expand_duration:50,gauge_fullCircle:!1,gauge_label_show:!0,gauge_labelLine_show:!0,gauge_label_format:void 0,gauge_min:0,gauge_max:100,gauge_startingAngle:-1*Math.PI/2,gauge_label_extents:void 0,gauge_units:void 0,gauge_width:void 0,gauge_arcs_minWidth:5,gauge_expand:{},gauge_expand_duration:50,donut_label_show:!0,donut_label_format:void 0,donut_label_threshold:.05,donut_label_ratio:void 0,donut_width:void 0,donut_title:"",donut_expand:{},donut_expand_duration:50,spline_interpolation_type:"cardinal",regions:[],tooltip_show:!0,tooltip_grouped:!0,tooltip_order:void 0,tooltip_format_title:void 0,tooltip_format_name:void 0,tooltip_format_value:void 0,tooltip_position:void 0,tooltip_contents:function(t,e,i,n){return this.getTooltipContent?this.getTooltipContent(t,e,i,n):""},tooltip_init_show:!1,tooltip_init_x:0,tooltip_init_position:{top:"0px",left:"50px"},tooltip_onshow:function(){},tooltip_onhide:function(){},title_text:void 0,title_padding:{top:0,right:0,bottom:0,left:0},title_position:"top-center"};return Object.keys(this.additionalConfig).forEach(function(e){t[e]=this.additionalConfig[e]},this),t},T.additionalConfig={},T.loadConfig=function(t){var e,i,a,r=this.config;Object.keys(r).forEach(function(s){e=t,i=s.split("_"),a=function t(){var a=i.shift();return a&&e&&"object"===(void 0===e?"undefined":n(e))&&a in e?(e=e[a],t()):a?void 0:e}(),l(a)&&(r[s]=a)})},T.convertUrlToData=function(t,e,i,n,a){var r=this,s=e||"csv",o=r.d3.request(t);i&&Object.keys(i).forEach(function(t){o.header(t,i[t])}),o.get(function(t,e){var i,o=e.response||e.responseText;if(!e)throw new Error(t.responseURL+" "+t.status+" ("+t.statusText+")");i="json"===s?r.convertJsonToData(JSON.parse(o),n):"tsv"===s?r.convertTsvToData(o):r.convertCsvToData(o),a.call(r,i)})},T.convertXsvToData=function(t,e){var i,n=e.parseRows(t),a=(i=n,Array.isArray(i)?i:Array.from(i)),r=a[0];return 0===a.slice(1).length?{keys:r,rows:[r.reduce(function(t,e){return Object.assign(t,(a=null,(n=e)in(i={})?Object.defineProperty(i,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):i[n]=a,i));var i,n,a},{})]}:{keys:r,rows:[].concat(e.parse(t))}},T.convertCsvToData=function(t){return this.convertXsvToData(t,{parse:this.d3.csvParse,parseRows:this.d3.csvParseRows})},T.convertTsvToData=function(t){return this.convertXsvToData(t,{parse:this.d3.tsvParse,parseRows:this.d3.tsvParseRows})},T.convertJsonToData=function(t,e){var i,n,a=this,r=[];return e?(e.x?(i=e.value.concat(e.x),a.config.data_x=e.x):i=e.value,r.push(i),t.forEach(function(t){var e=[];i.forEach(function(i){var n=a.findValueInJson(t,i);u(n)&&(n=null),e.push(n)}),r.push(e)}),n=a.convertRowsToData(r)):(Object.keys(t).forEach(function(e){r.push([e].concat(t[e]))}),n=a.convertColumnsToData(r)),n},T.findValueInJson=function(t,e){for(var i=(e=(e=e.replace(/\[(\w+)\]/g,".$1")).replace(/^\./,"")).split("."),n=0;n<i.length;++n){var a=i[n];if(!(a in t))return;t=t[a]}return t},T.convertRowsToData=function(t){for(var e=[],i=t[0],n=1;n<t.length;n++){for(var a={},r=0;r<t[n].length;r++){if(u(t[n][r]))throw new Error("Source data is missing a component at ("+n+","+r+")!");a[i[r]]=t[n][r]}e.push(a)}return{keys:i,rows:e}},T.convertColumnsToData=function(t){for(var e=[],i=[],n=0;n<t.length;n++){for(var a=t[n][0],r=1;r<t[n].length;r++){if(u(e[r-1])&&(e[r-1]={}),u(t[n][r]))throw new Error("Source data is missing a component at ("+n+","+r+")!");e[r-1][a]=t[n][r]}i.push(a)}return{keys:i,rows:e}},T.convertDataToTargets=function(t,e){var i,n,a,r,o=this,d=o.config;return c(t)?r=Object.keys(t[0]):(r=t.keys,t=t.rows),n=r.filter(o.isNotX,o),a=r.filter(o.isX,o),n.forEach(function(i){var n=o.getXKey(i);o.isCustomX()||o.isTimeSeries()?a.indexOf(n)>=0?o.data.xs[i]=(e&&o.data.xs[i]?o.data.xs[i]:[]).concat(t.map(function(t){return t[n]}).filter(s).map(function(t,e){return o.generateTargetX(t,i,e)})):d.data_x?o.data.xs[i]=o.getOtherTargetXs():_(d.data_xs)&&(o.data.xs[i]=o.getXValuesOfXKey(n,o.data.targets)):o.data.xs[i]=t.map(function(t,e){return e})}),n.forEach(function(t){if(!o.data.xs[t])throw new Error('x is not defined for id = "'+t+'".')}),(i=n.map(function(e,i){var n=d.data_idConverter(e);return{id:n,id_org:e,values:t.map(function(t,a){var r,s=t[o.getXKey(e)],c=null===t[e]||isNaN(t[e])?null:+t[e];return o.isCustomX()&&o.isCategorized()&&!u(s)?(0===i&&0===a&&(d.axis_x_categories=[]),-1===(r=d.axis_x_categories.indexOf(s))&&(r=d.axis_x_categories.length,d.axis_x_categories.push(s))):r=o.generateTargetX(s,e,a),(u(t[e])||o.data.xs[e].length<=a)&&(r=void 0),{x:r,value:c,id:n}}).filter(function(t){return l(t.x)})}})).forEach(function(t){var e;d.data_xSort&&(t.values=t.values.sort(function(t,e){return(t.x||0===t.x?t.x:1/0)-(e.x||0===e.x?e.x:1/0)})),e=0,t.values.forEach(function(t){t.index=e++}),o.data.xs[t.id].sort(function(t,e){return t-e})}),o.hasNegativeValue=o.hasNegativeValueInTargets(i),o.hasPositiveValue=o.hasPositiveValueInTargets(i),d.data_type&&o.setTargetType(o.mapToIds(i).filter(function(t){return!(t in d.data_types)}),d.data_type),i.forEach(function(t){o.addCache(t.id_org,t)}),i},T.isX=function(t){var e=this.config;return e.data_x&&t===e.data_x||_(e.data_xs)&&y(e.data_xs,t)},T.isNotX=function(t){return!this.isX(t)},T.getXKey=function(t){var e=this.config;return e.data_x?e.data_x:_(e.data_xs)?e.data_xs[t]:null},T.getXValuesOfXKey=function(t,e){var i,n=this;return(e&&_(e)?n.mapToIds(e):[]).forEach(function(e){n.getXKey(e)===t&&(i=n.data.xs[e])}),i},T.getXValue=function(t,e){return t in this.data.xs&&this.data.xs[t]&&s(this.data.xs[t][e])?this.data.xs[t][e]:e},T.getOtherTargetXs=function(){var t=Object.keys(this.data.xs);return t.length?this.data.xs[t[0]]:null},T.getOtherTargetX=function(t){var e=this.getOtherTargetXs();return e&&t<e.length?e[t]:null},T.addXs=function(t){var e=this;Object.keys(t).forEach(function(i){e.config.data_xs[i]=t[i]})},T.addName=function(t){var e;return t&&(e=this.config.data_names[t.id],t.name=void 0!==e?e:t.id),t},T.getValueOnIndex=function(t,e){var i=t.filter(function(t){return t.index===e});return i.length?i[0]:null},T.updateTargetX=function(t,e){var i=this;t.forEach(function(t){t.values.forEach(function(n,a){n.x=i.generateTargetX(e[a],t.id,a)}),i.data.xs[t.id]=e})},T.updateTargetXs=function(t,e){var i=this;t.forEach(function(t){e[t.id]&&i.updateTargetX([t],e[t.id])})},T.generateTargetX=function(t,e,i){var n=this;return n.isTimeSeries()?t?n.parseDate(t):n.parseDate(n.getXValue(e,i)):n.isCustomX()&&!n.isCategorized()?s(t)?+t:n.getXValue(e,i):i},T.cloneTarget=function(t){return{id:t.id,id_org:t.id_org,values:t.values.map(function(t){return{x:t.x,value:t.value,id:t.id}})}},T.getMaxDataCount=function(){return this.d3.max(this.data.targets,function(t){return t.values.length})},T.mapToIds=function(t){return t.map(function(t){return t.id})},T.mapToTargetIds=function(t){return t?[].concat(t):this.mapToIds(this.data.targets)},T.hasTarget=function(t,e){var i,n=this.mapToIds(t);for(i=0;i<n.length;i++)if(n[i]===e)return!0;return!1},T.isTargetToShow=function(t){return this.hiddenTargetIds.indexOf(t)<0},T.isLegendToShow=function(t){return this.hiddenLegendIds.indexOf(t)<0},T.filterTargetsToShow=function(t){var e=this;return t.filter(function(t){return e.isTargetToShow(t.id)})},T.mapTargetsToUniqueXs=function(t){var e=this.d3.set(this.d3.merge(t.map(function(t){return t.values.map(function(t){return+t.x})}))).values();return(e=this.isTimeSeries()?e.map(function(t){return new Date(+t)}):e.map(function(t){return+t})).sort(function(t,e){return t<e?-1:t>e?1:t>=e?0:NaN})},T.addHiddenTargetIds=function(t){t=t instanceof Array?t:new Array(t);for(var e=0;e<t.length;e++)this.hiddenTargetIds.indexOf(t[e])<0&&(this.hiddenTargetIds=this.hiddenTargetIds.concat(t[e]))},T.removeHiddenTargetIds=function(t){this.hiddenTargetIds=this.hiddenTargetIds.filter(function(e){return t.indexOf(e)<0})},T.addHiddenLegendIds=function(t){t=t instanceof Array?t:new Array(t);for(var e=0;e<t.length;e++)this.hiddenLegendIds.indexOf(t[e])<0&&(this.hiddenLegendIds=this.hiddenLegendIds.concat(t[e]))},T.removeHiddenLegendIds=function(t){this.hiddenLegendIds=this.hiddenLegendIds.filter(function(e){return t.indexOf(e)<0})},T.getValuesAsIdKeyed=function(t){var e={};return t.forEach(function(t){e[t.id]=[],t.values.forEach(function(i){e[t.id].push(i.value)})}),e},T.checkValueInTargets=function(t,e){var i,n,a,r=Object.keys(t);for(i=0;i<r.length;i++)for(a=t[r[i]].values,n=0;n<a.length;n++)if(e(a[n].value))return!0;return!1},T.hasNegativeValueInTargets=function(t){return this.checkValueInTargets(t,function(t){return t<0})},T.hasPositiveValueInTargets=function(t){return this.checkValueInTargets(t,function(t){return t>0})},T.isOrderDesc=function(){var t=this.config;return"string"==typeof t.data_order&&"desc"===t.data_order.toLowerCase()},T.isOrderAsc=function(){var t=this.config;return"string"==typeof t.data_order&&"asc"===t.data_order.toLowerCase()},T.getOrderFunction=function(){var t=this.config,e=this.isOrderAsc(),i=this.isOrderDesc();if(e||i){var n=function(t,e){return t+Math.abs(e.value)};return function(t,i){var a=t.values.reduce(n,0),r=i.values.reduce(n,0);return e?r-a:a-r}}if(o(t.data_order))return t.data_order;if(c(t.data_order)){var a=t.data_order;return function(t,e){return a.indexOf(t.id)-a.indexOf(e.id)}}},T.orderTargets=function(t){var e=this.getOrderFunction();return e&&t.sort(e),t},T.filterByX=function(t,e){return this.d3.merge(t.map(function(t){return t.values})).filter(function(t){return t.x-e==0})},T.filterRemoveNull=function(t){return t.filter(function(t){return s(t.value)})},T.filterByXDomain=function(t,e){return t.map(function(t){return{id:t.id,id_org:t.id_org,values:t.values.filter(function(t){return e[0]<=t.x&&t.x<=e[1]})}})},T.hasDataLabel=function(){var t=this.config;return!("boolean"!=typeof t.data_labels||!t.data_labels)||!("object"!==n(t.data_labels)||!_(t.data_labels))},T.getDataLabelLength=function(t,e,i){var n=this,a=[0,0];return n.selectChart.select("svg").selectAll(".dummy").data([t,e]).enter().append("text").text(function(t){return n.dataLabelFormat(t.id)(t)}).each(function(t,e){a[e]=1.3*this.getBoundingClientRect()[i]}).remove(),a},T.isNoneArc=function(t){return this.hasTarget(this.data.targets,t.id)},T.isArc=function(t){return"data"in t&&this.hasTarget(this.data.targets,t.data.id)},T.findClosestFromTargets=function(t,e){var i,n=this;return i=t.map(function(t){return n.findClosest(t.values,e)}),n.findClosest(i,e)},T.findClosest=function(t,e){var n,a=this,r=a.config.point_sensitivity;return t.filter(function(t){return t&&a.isBarType(t.id)}).forEach(function(t){var e=a.main.select("."+i.bars+a.getTargetSelectorSuffix(t.id)+" ."+i.bar+"-"+t.index).node();!n&&a.isWithinBar(a.d3.mouse(e),e)&&(n=t)}),t.filter(function(t){return t&&!a.isBarType(t.id)}).forEach(function(t){var i=a.dist(t,e);i<r&&(r=i,n=t)}),n},T.dist=function(t,e){var i=this.config,n=i.axis_rotated?1:0,a=i.axis_rotated?0:1,r=this.circleY(t,t.index),s=this.x(t.x);return Math.sqrt(Math.pow(s-e[n],2)+Math.pow(r-e[a],2))},T.convertValuesToStep=function(t){var e,i=[].concat(t);if(!this.isCategorized())return t;for(e=t.length+1;0<e;e--)i[e]=i[e-1];return i[0]={x:i[0].x-1,value:i[0].value,id:i[0].id},i[t.length+1]={x:i[t.length].x+1,value:i[t.length].value,id:i[t.length].id},i},T.updateDataAttributes=function(t,e){var i=this.config["data_"+t];return void 0===e?i:(Object.keys(e).forEach(function(t){i[t]=e[t]}),this.redraw({withLegend:!0}),i)},T.load=function(t,e){var i=this;t&&(e.filter&&(t=t.filter(e.filter)),(e.type||e.types)&&t.forEach(function(t){var n=e.types&&e.types[t.id]?e.types[t.id]:e.type;i.setTargetType(t.id,n)}),i.data.targets.forEach(function(e){for(var i=0;i<t.length;i++)if(e.id===t[i].id){e.values=t[i].values,t.splice(i,1);break}}),i.data.targets=i.data.targets.concat(t)),i.updateTargets(i.data.targets),i.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),e.done&&e.done()},T.loadFromArgs=function(t){var e=this;t.data?e.load(e.convertDataToTargets(t.data),t):t.url?e.convertUrlToData(t.url,t.mimeType,t.headers,t.keys,function(i){e.load(e.convertDataToTargets(i),t)}):t.json?e.load(e.convertDataToTargets(e.convertJsonToData(t.json,t.keys)),t):t.rows?e.load(e.convertDataToTargets(e.convertRowsToData(t.rows)),t):t.columns?e.load(e.convertDataToTargets(e.convertColumnsToData(t.columns)),t):e.load(null,t)},T.unload=function(t,e){var n=this;e||(e=function(){}),(t=t.filter(function(t){return n.hasTarget(n.data.targets,t)}))&&0!==t.length?(n.svg.selectAll(t.map(function(t){return n.selectorTarget(t)})).transition().style("opacity",0).remove().call(n.endall,e),t.forEach(function(t){n.withoutFadeIn[t]=!1,n.legend&&n.legend.selectAll("."+i.legendItem+n.getTargetSelectorSuffix(t)).remove(),n.data.targets=n.data.targets.filter(function(e){return e.id!==t})})):e()},T.getYDomainMin=function(t){var e,i,n,a,r,s,o=this,c=o.config,d=o.mapToIds(t),u=o.getValuesAsIdKeyed(t);if(c.data_groups.length>0)for(s=o.hasNegativeValueInTargets(t),e=0;e<c.data_groups.length;e++)if(0!==(a=c.data_groups[e].filter(function(t){return d.indexOf(t)>=0})).length)for(n=a[0],s&&u[n]&&u[n].forEach(function(t,e){u[n][e]=t<0?t:0}),i=1;i<a.length;i++)r=a[i],u[r]&&u[r].forEach(function(t,e){o.axis.getId(r)!==o.axis.getId(n)||!u[n]||s&&+t>0||(u[n][e]+=+t)});return o.d3.min(Object.keys(u).map(function(t){return o.d3.min(u[t])}))},T.getYDomainMax=function(t){var e,i,n,a,r,s,o=this,c=o.config,d=o.mapToIds(t),u=o.getValuesAsIdKeyed(t);if(c.data_groups.length>0)for(s=o.hasPositiveValueInTargets(t),e=0;e<c.data_groups.length;e++)if(0!==(a=c.data_groups[e].filter(function(t){return d.indexOf(t)>=0})).length)for(n=a[0],s&&u[n]&&u[n].forEach(function(t,e){u[n][e]=t>0?t:0}),i=1;i<a.length;i++)r=a[i],u[r]&&u[r].forEach(function(t,e){o.axis.getId(r)!==o.axis.getId(n)||!u[n]||s&&+t<0||(u[n][e]+=+t)});return o.d3.max(Object.keys(u).map(function(t){return o.d3.max(u[t])}))},T.getYDomain=function(t,e,i){var n,a,r,o,c,d,u,l,h,g,p=this,x=p.config,y=t.filter(function(t){return p.axis.getId(t.id)===e}),m=i?p.filterByXDomain(y,i):y,S="y2"===e?x.axis_y2_min:x.axis_y_min,w="y2"===e?x.axis_y2_max:x.axis_y_max,v=p.getYDomainMin(m),b=p.getYDomainMax(m),T="y2"===e?x.axis_y2_center:x.axis_y_center,A=p.hasType("bar",m)&&x.bar_zerobased||p.hasType("area",m)&&x.area_zerobased,P="y2"===e?x.axis_y2_inverted:x.axis_y_inverted,C=p.hasDataLabel()&&x.axis_rotated,L=p.hasDataLabel()&&!x.axis_rotated;return v=s(S)?S:s(w)?v<w?v:w-10:v,b=s(w)?w:s(S)?S<b?b:S+10:b,0===m.length?"y2"===e?p.y2.domain():p.y.domain():(isNaN(v)&&(v=0),isNaN(b)&&(b=v),v===b&&(v<0?b=0:v=0),h=v>=0&&b>=0,g=v<=0&&b<=0,(s(S)&&h||s(w)&&g)&&(A=!1),A&&(h&&(v=0),g&&(b=0)),r=o=.1*(a=Math.abs(b-v)),void 0!==T&&(b=T+(c=Math.max(Math.abs(v),Math.abs(b))),v=T-c),C?(d=p.getDataLabelLength(v,b,"width"),u=f(p.y.range()),r+=a*((l=[d[0]/u,d[1]/u])[1]/(1-l[0]-l[1])),o+=a*(l[0]/(1-l[0]-l[1]))):L&&(d=p.getDataLabelLength(v,b,"height"),r+=p.axis.convertPixelsToAxisPadding(d[1],a),o+=p.axis.convertPixelsToAxisPadding(d[0],a)),"y"===e&&_(x.axis_y_padding)&&(r=p.axis.getPadding(x.axis_y_padding,"top",r,a),o=p.axis.getPadding(x.axis_y_padding,"bottom",o,a)),"y2"===e&&_(x.axis_y2_padding)&&(r=p.axis.getPadding(x.axis_y2_padding,"top",r,a),o=p.axis.getPadding(x.axis_y2_padding,"bottom",o,a)),A&&(h&&(o=v),g&&(r=-b)),n=[v-o,b+r],P?n.reverse():n)},T.getXDomainMin=function(t){var e=this,i=e.config;return l(i.axis_x_min)?e.isTimeSeries()?this.parseDate(i.axis_x_min):i.axis_x_min:e.d3.min(t,function(t){return e.d3.min(t.values,function(t){return t.x})})},T.getXDomainMax=function(t){var e=this,i=e.config;return l(i.axis_x_max)?e.isTimeSeries()?this.parseDate(i.axis_x_max):i.axis_x_max:e.d3.max(t,function(t){return e.d3.max(t.values,function(t){return t.x})})},T.getXDomainPadding=function(t){var e,i,a,r,o=this.config,c=t[1]-t[0];return i=this.isCategorized()?0:this.hasType("bar")?(e=this.getMaxDataCount())>1?c/(e-1)/2:.5:.01*c,"object"===n(o.axis_x_padding)&&_(o.axis_x_padding)?(a=s(o.axis_x_padding.left)?o.axis_x_padding.left:i,r=s(o.axis_x_padding.right)?o.axis_x_padding.right:i):a=r="number"==typeof o.axis_x_padding?o.axis_x_padding:i,{left:a,right:r}},T.getXDomain=function(t){var e=this,i=[e.getXDomainMin(t),e.getXDomainMax(t)],n=i[0],a=i[1],r=e.getXDomainPadding(i),s=0,o=0;return n-a!=0||e.isCategorized()||(e.isTimeSeries()?(n=new Date(.5*n.getTime()),a=new Date(1.5*a.getTime())):(n=0===n?1:.5*n,a=0===a?-1:1.5*a)),(n||0===n)&&(s=e.isTimeSeries()?new Date(n.getTime()-r.left):n-r.left),(a||0===a)&&(o=e.isTimeSeries()?new Date(a.getTime()+r.right):a+r.right),[s,o]},T.updateXDomain=function(t,e,i,n,a){var r=this,s=r.config;return i&&(r.x.domain(a||r.d3.extent(r.getXDomain(t))),r.orgXDomain=r.x.domain(),s.zoom_enabled&&r.zoom.update(),r.subX.domain(r.x.domain()),r.brush&&r.brush.updateScale(r.subX)),e&&r.x.domain(a||(!r.brush||r.brush.empty()?r.orgXDomain:r.brush.selectionAsValue())),n&&r.x.domain(r.trimXDomain(r.x.orgDomain())),r.x.domain()},T.trimXDomain=function(t){var e=this.getZoomDomain(),i=e[0],n=e[1];return t[0]<=i&&(t[1]=+t[1]+(i-t[0]),t[0]=i),n<=t[1]&&(t[0]=+t[0]-(t[1]-n),t[1]=n),t},T.drag=function(t){var e,n,a,r,s,o,c,d,u=this,l=u.config,h=u.main,g=u.d3;u.hasArcType()||l.data_selection_enabled&&l.data_selection_multiple&&(e=u.dragStart[0],n=u.dragStart[1],a=t[0],r=t[1],s=Math.min(e,a),o=Math.max(e,a),c=l.data_selection_grouped?u.margin.top:Math.min(n,r),d=l.data_selection_grouped?u.height:Math.max(n,r),h.select("."+i.dragarea).attr("x",s).attr("y",c).attr("width",o-s).attr("height",d-c),h.selectAll("."+i.shapes).selectAll("."+i.shape).filter(function(t){return l.data_selection_isselectable(t)}).each(function(t,e){var n,a,r,l,h,f,p=g.select(this),_=p.classed(i.SELECTED),x=p.classed(i.INCLUDED),y=!1;if(p.classed(i.circle))n=1*p.attr("cx"),a=1*p.attr("cy"),h=u.togglePoint,y=s<n&&n<o&&c<a&&a<d;else{if(!p.classed(i.bar))return;n=(f=S(this)).x,a=f.y,r=f.width,l=f.height,h=u.togglePath,y=!(o<n||n+r<s||d<a||a+l<c)}y^x&&(p.classed(i.INCLUDED,!x),p.classed(i.SELECTED,!_),h.call(u,!_,p,t,e))}))},T.dragstart=function(t){var e=this,n=e.config;e.hasArcType()||n.data_selection_enabled&&(e.dragStart=t,e.main.select("."+i.chart).append("rect").attr("class",i.dragarea).style("opacity",.1),e.dragging=!0)},T.dragend=function(){var t=this,e=t.config;t.hasArcType()||e.data_selection_enabled&&(t.main.select("."+i.dragarea).transition().duration(100).style("opacity",0).remove(),t.main.selectAll("."+i.shape).classed(i.INCLUDED,!1),t.dragging=!1)},T.getYFormat=function(t){var e=this,i=t&&!e.hasType("gauge")?e.defaultArcValueFormat:e.yFormat,n=t&&!e.hasType("gauge")?e.defaultArcValueFormat:e.y2Format;return function(t,a,r){return("y2"===e.axis.getId(r)?n:i).call(e,t,a)}},T.yFormat=function(t){var e=this.config;return(e.axis_y_tick_format?e.axis_y_tick_format:this.defaultValueFormat)(t)},T.y2Format=function(t){var e=this.config;return(e.axis_y2_tick_format?e.axis_y2_tick_format:this.defaultValueFormat)(t)},T.defaultValueFormat=function(t){return s(t)?+t:""},T.defaultArcValueFormat=function(t,e){return(100*e).toFixed(1)+"%"},T.dataLabelFormat=function(t){var e=this.config.data_labels,i=function(t){return s(t)?+t:""};return"function"==typeof e.format?e.format:"object"===n(e.format)?e.format[t]?!0===e.format[t]?i:e.format[t]:function(){return""}:i},T.initGrid=function(){var t=this,e=t.config,n=t.d3;t.grid=t.main.append("g").attr("clip-path",t.clipPathForGrid).attr("class",i.grid),e.grid_x_show&&t.grid.append("g").attr("class",i.xgrids),e.grid_y_show&&t.grid.append("g").attr("class",i.ygrids),e.grid_focus_show&&t.grid.append("g").attr("class",i.xgridFocus).append("line").attr("class",i.xgridFocus),t.xgrid=n.selectAll([]),e.grid_lines_front||t.initGridLines()},T.initGridLines=function(){var t=this,e=t.d3;t.gridLines=t.main.append("g").attr("clip-path",t.clipPathForGrid).attr("class",i.grid+" "+i.gridLines),t.gridLines.append("g").attr("class",i.xgridLines),t.gridLines.append("g").attr("class",i.ygridLines),t.xgridLines=e.selectAll([])},T.updateXGrid=function(t){var e=this,n=e.config,a=e.d3,r=e.generateGridData(n.grid_x_type,e.x),s=e.isCategorized()?e.xAxis.tickOffset():0;e.xgridAttr=n.axis_rotated?{x1:0,x2:e.width,y1:function(t){return e.x(t)-s},y2:function(t){return e.x(t)-s}}:{x1:function(t){return e.x(t)+s},x2:function(t){return e.x(t)+s},y1:0,y2:e.height},e.xgridAttr.opacity=function(){return+a.select(this).attr(n.axis_rotated?"y1":"x1")===(n.axis_rotated?e.height:0)?0:1};var o=e.main.select("."+i.xgrids).selectAll("."+i.xgrid).data(r),c=o.enter().append("line").attr("class",i.xgrid).attr("x1",e.xgridAttr.x1).attr("x2",e.xgridAttr.x2).attr("y1",e.xgridAttr.y1).attr("y2",e.xgridAttr.y2).style("opacity",0);e.xgrid=c.merge(o),t||e.xgrid.attr("x1",e.xgridAttr.x1).attr("x2",e.xgridAttr.x2).attr("y1",e.xgridAttr.y1).attr("y2",e.xgridAttr.y2).style("opacity",e.xgridAttr.opacity),o.exit().remove()},T.updateYGrid=function(){var t=this,e=t.config,n=t.yAxis.tickValues()||t.y.ticks(e.grid_y_ticks),a=t.main.select("."+i.ygrids).selectAll("."+i.ygrid).data(n),r=a.enter().append("line").attr("class",i.ygrid);t.ygrid=r.merge(a),t.ygrid.attr("x1",e.axis_rotated?t.y:0).attr("x2",e.axis_rotated?t.y:t.width).attr("y1",e.axis_rotated?0:t.y).attr("y2",e.axis_rotated?t.height:t.y),a.exit().remove(),t.smoothLines(t.ygrid,"grid")},T.gridTextAnchor=function(t){return t.position?t.position:"end"},T.gridTextDx=function(t){return"start"===t.position?4:"middle"===t.position?0:-4},T.xGridTextX=function(t){return"start"===t.position?-this.height:"middle"===t.position?-this.height/2:0},T.yGridTextX=function(t){return"start"===t.position?0:"middle"===t.position?this.width/2:this.width},T.updateGrid=function(t){var e,n,a,r,s=this,o=s.main,c=s.config,d=s.xv.bind(s),u=s.yv.bind(s),l=s.xGridTextX.bind(s),h=s.yGridTextX.bind(s);s.grid.style("visibility",s.hasArcType()?"hidden":"visible"),o.select("line."+i.xgridFocus).style("visibility","hidden"),c.grid_x_show&&s.updateXGrid(),(n=(e=o.select("."+i.xgridLines).selectAll("."+i.xgridLine).data(c.grid_x_lines)).enter().append("g").attr("class",function(t){return i.xgridLine+(t.class?" "+t.class:"")})).append("line").attr("x1",c.axis_rotated?0:d).attr("x2",c.axis_rotated?s.width:d).attr("y1",c.axis_rotated?d:0).attr("y2",c.axis_rotated?d:s.height).style("opacity",0),n.append("text").attr("text-anchor",s.gridTextAnchor).attr("transform",c.axis_rotated?"":"rotate(-90)").attr("x",c.axis_rotated?h:l).attr("y",d).attr("dx",s.gridTextDx).attr("dy",-5).style("opacity",0),s.xgridLines=n.merge(e),e.exit().transition().duration(t).style("opacity",0).remove(),c.grid_y_show&&s.updateYGrid(),(r=(a=o.select("."+i.ygridLines).selectAll("."+i.ygridLine).data(c.grid_y_lines)).enter().append("g").attr("class",function(t){return i.ygridLine+(t.class?" "+t.class:"")})).append("line").attr("x1",c.axis_rotated?u:0).attr("x2",c.axis_rotated?u:s.width).attr("y1",c.axis_rotated?0:u).attr("y2",c.axis_rotated?s.height:u).style("opacity",0),r.append("text").attr("text-anchor",s.gridTextAnchor).attr("transform",c.axis_rotated?"rotate(-90)":"").attr("x",c.axis_rotated?l:h).attr("y",u).attr("dx",s.gridTextDx).attr("dy",-5).style("opacity",0),s.ygridLines=r.merge(a),s.ygridLines.select("line").transition().duration(t).attr("x1",c.axis_rotated?u:0).attr("x2",c.axis_rotated?u:s.width).attr("y1",c.axis_rotated?0:u).attr("y2",c.axis_rotated?s.height:u).style("opacity",1),s.ygridLines.select("text").transition().duration(t).attr("x",c.axis_rotated?s.xGridTextX.bind(s):s.yGridTextX.bind(s)).attr("y",u).text(function(t){return t.text}).style("opacity",1),a.exit().transition().duration(t).style("opacity",0).remove()},T.redrawGrid=function(t,e){var i=this,n=i.config,a=i.xv.bind(i),r=i.xgridLines.select("line"),s=i.xgridLines.select("text");return[(t?r.transition(e):r).attr("x1",n.axis_rotated?0:a).attr("x2",n.axis_rotated?i.width:a).attr("y1",n.axis_rotated?a:0).attr("y2",n.axis_rotated?a:i.height).style("opacity",1),(t?s.transition(e):s).attr("x",n.axis_rotated?i.yGridTextX.bind(i):i.xGridTextX.bind(i)).attr("y",a).text(function(t){return t.text}).style("opacity",1)]},T.showXGridFocus=function(t){var e=this,n=e.config,a=t.filter(function(t){return t&&s(t.value)}),r=e.main.selectAll("line."+i.xgridFocus),o=e.xx.bind(e);n.tooltip_show&&(e.hasType("scatter")||e.hasArcType()||(r.style("visibility","visible").data([a[0]]).attr(n.axis_rotated?"y1":"x1",o).attr(n.axis_rotated?"y2":"x2",o),e.smoothLines(r,"grid")))},T.hideXGridFocus=function(){this.main.select("line."+i.xgridFocus).style("visibility","hidden")},T.updateXgridFocus=function(){var t=this.config;this.main.select("line."+i.xgridFocus).attr("x1",t.axis_rotated?0:-10).attr("x2",t.axis_rotated?this.width:-10).attr("y1",t.axis_rotated?-10:0).attr("y2",t.axis_rotated?-10:this.height)},T.generateGridData=function(t,e){var n,a,r,s,o=[],c=this.main.select("."+i.axisX).selectAll(".tick").size();if("year"===t)for(a=(n=this.getXDomain())[0].getFullYear(),r=n[1].getFullYear(),s=a;s<=r;s++)o.push(new Date(s+"-01-01 00:00:00"));else(o=e.ticks(10)).length>c&&(o=o.filter(function(t){return(""+t).indexOf(".")<0}));return o},T.getGridFilterToRemove=function(t){return t?function(e){var i=!1;return[].concat(t).forEach(function(t){("value"in t&&e.value===t.value||"class"in t&&e.class===t.class)&&(i=!0)}),i}:function(){return!0}},T.removeGridLines=function(t,e){var n=this.config,a=this.getGridFilterToRemove(t),r=function(t){return!a(t)},s=e?i.xgridLines:i.ygridLines,o=e?i.xgridLine:i.ygridLine;this.main.select("."+s).selectAll("."+o).filter(a).transition().duration(n.transition_duration).style("opacity",0).remove(),e?n.grid_x_lines=n.grid_x_lines.filter(r):n.grid_y_lines=n.grid_y_lines.filter(r)},T.initEventRect=function(){var t=this,e=t.config;t.main.select("."+i.chart).append("g").attr("class",i.eventRects).style("fill-opacity",0),t.eventRect=t.main.select("."+i.eventRects).append("rect").attr("class",i.eventRect),e.zoom_enabled&&t.zoom&&(t.eventRect.call(t.zoom).on("dblclick.zoom",null),e.zoom_initialRange&&t.eventRect.transition().duration(0).call(t.zoom.transform,t.zoomTransform(e.zoom_initialRange)))},T.redrawEventRect=function(){var t,e,n=this,a=n.d3,r=n.config;function s(){n.svg.select("."+i.eventRect).style("cursor",null),n.hideXGridFocus(),n.hideTooltip(),n.unexpandCircles(),n.unexpandBars()}t=n.width,e=n.height,n.main.select("."+i.eventRects).style("cursor",r.zoom_enabled?r.axis_rotated?"ns-resize":"ew-resize":null),n.eventRect.attr("x",0).attr("y",0).attr("width",t).attr("height",e).on("mouseout",r.interaction_enabled?function(){r&&(n.hasArcType()||s())}:null).on("mousemove",r.interaction_enabled?function(){var t,e,o,c;n.dragging||n.hasArcType(t)||(t=n.filterTargetsToShow(n.data.targets),e=a.mouse(this),o=n.findClosestFromTargets(t,e),!n.mouseover||o&&o.id===n.mouseover.id||(r.data_onmouseout.call(n.api,n.mouseover),n.mouseover=void 0),o?(c=(n.isScatterType(o)||!r.tooltip_grouped?[o]:n.filterByX(t,o.x)).map(function(t){return n.addName(t)}),n.showTooltip(c,this),r.point_focus_expand_enabled&&(n.unexpandCircles(),c.forEach(function(t){n.expandCircles(t.index,t.id,!1)})),n.expandBars(o.index,o.id,!0),n.showXGridFocus(c),(n.isBarType(o.id)||n.dist(o,e)<r.point_sensitivity)&&(n.svg.select("."+i.eventRect).style("cursor","pointer"),n.mouseover||(r.data_onmouseover.call(n.api,o),n.mouseover=o))):s())}:null).on("click",r.interaction_enabled?function(){var t,e,s;n.hasArcType(t)||(t=n.filterTargetsToShow(n.data.targets),e=a.mouse(this),(s=n.findClosestFromTargets(t,e))&&(n.isBarType(s.id)||n.dist(s,e)<r.point_sensitivity)&&(n.isScatterType(s)||!r.data_selection_grouped?[s]:n.filterByX(t,s.x)).forEach(function(t){n.main.selectAll("."+i.shapes+n.getTargetSelectorSuffix(t.id)).selectAll("."+i.shape+"-"+t.index).each(function(){(r.data_selection_grouped||n.isWithinShape(this,t))&&(n.toggleShape(this,t,t.index),r.data_onclick.call(n.api,t,this))})}))}:null).call(r.interaction_enabled&&r.data_selection_draggable&&n.drag?a.drag().on("drag",function(){n.drag(a.mouse(this))}).on("start",function(){n.dragstart(a.mouse(this))}).on("end",function(){n.dragend()}):function(){})},T.getMousePosition=function(t){return[this.x(t.x),this.getYScale(t.id)(t.value)]},T.dispatchEvent=function(t,e){var n="."+i.eventRect,a=this.main.select(n).node(),r=a.getBoundingClientRect(),s=r.left+(e?e[0]:0),o=r.top+(e?e[1]:0),c=document.createEvent("MouseEvents");c.initMouseEvent(t,!0,!0,window,0,s,o,s,o,!1,!1,!1,!1,0,null),a.dispatchEvent(c)},T.initLegend=function(){var t=this;if(t.legendItemTextBox={},t.legendHasRendered=!1,t.legend=t.svg.append("g").attr("transform",t.getTranslate("legend")),!t.config.legend_show)return t.legend.style("visibility","hidden"),void(t.hiddenLegendIds=t.mapToIds(t.data.targets));t.updateLegendWithDefaults()},T.updateLegendWithDefaults=function(){this.updateLegend(this.mapToIds(this.data.targets),{withTransform:!1,withTransitionForTransform:!1,withTransition:!1})},T.updateSizeForLegend=function(t,e){var i=this,n=i.config,a={top:i.isLegendTop?i.getCurrentPaddingTop()+n.legend_inset_y+5.5:i.currentHeight-t-i.getCurrentPaddingBottom()-n.legend_inset_y,left:i.isLegendLeft?i.getCurrentPaddingLeft()+n.legend_inset_x+.5:i.currentWidth-e-i.getCurrentPaddingRight()-n.legend_inset_x+.5};i.margin3={top:i.isLegendRight?0:i.isLegendInset?a.top:i.currentHeight-t,right:NaN,bottom:0,left:i.isLegendRight?i.currentWidth-e:i.isLegendInset?a.left:0}},T.transformLegend=function(t){(t?this.legend.transition():this.legend).attr("transform",this.getTranslate("legend"))},T.updateLegendStep=function(t){this.legendStep=t},T.updateLegendItemWidth=function(t){this.legendItemWidth=t},T.updateLegendItemHeight=function(t){this.legendItemHeight=t},T.getLegendWidth=function(){var t=this;return t.config.legend_show?t.isLegendRight||t.isLegendInset?t.legendItemWidth*(t.legendStep+1):t.currentWidth:0},T.getLegendHeight=function(){var t=this,e=0;return t.config.legend_show&&(e=t.isLegendRight?t.currentHeight:Math.max(20,t.legendItemHeight)*(t.legendStep+1)),e},T.opacityForLegend=function(t){return t.classed(i.legendItemHidden)?null:1},T.opacityForUnfocusedLegend=function(t){return t.classed(i.legendItemHidden)?null:.3},T.toggleFocusLegend=function(t,e){var n=this;t=n.mapToTargetIds(t),n.legend.selectAll("."+i.legendItem).filter(function(e){return t.indexOf(e)>=0}).classed(i.legendItemFocused,e).transition().duration(100).style("opacity",function(){return(e?n.opacityForLegend:n.opacityForUnfocusedLegend).call(n,n.d3.select(this))})},T.revertLegend=function(){var t=this,e=t.d3;t.legend.selectAll("."+i.legendItem).classed(i.legendItemFocused,!1).transition().duration(100).style("opacity",function(){return t.opacityForLegend(e.select(this))})},T.showLegend=function(t){var e=this,i=e.config;i.legend_show||(i.legend_show=!0,e.legend.style("visibility","visible"),e.legendHasRendered||e.updateLegendWithDefaults()),e.removeHiddenLegendIds(t),e.legend.selectAll(e.selectorLegends(t)).style("visibility","visible").transition().style("opacity",function(){return e.opacityForLegend(e.d3.select(this))})},T.hideLegend=function(t){var e=this,i=e.config;i.legend_show&&p(t)&&(i.legend_show=!1,e.legend.style("visibility","hidden")),e.addHiddenLegendIds(t),e.legend.selectAll(e.selectorLegends(t)).style("opacity",0).style("visibility","hidden")},T.clearLegendItemTextBoxCache=function(){this.legendItemTextBox={}},T.updateLegend=function(t,e,n){var a,r,s,o,c,d,u,h,g,f,p,_,y,m,S,w,v=this,b=v.config,T=4,A=10,P=0,C=0,L=10,V=b.legend_item_tile_width+5,G=0,E={},O={},I={},R=[0],k={},D=0;function F(e,n,a){var r,s,o,c,d=0===a,u=a===t.length-1,l=(o=e,c=n,v.legendItemTextBox[c]||(v.legendItemTextBox[c]=v.getTextRect(o.textContent,i.legendItem,o)),v.legendItemTextBox[c]),h=l.width+V+(!u||v.isLegendRight||v.isLegendInset?A:0)+b.legend_padding,g=l.height+T,f=v.isLegendRight||v.isLegendInset?g:h,p=v.isLegendRight||v.isLegendInset?v.getLegendHeight():v.getLegendWidth();function _(t,e){e||(r=(p-G-f)/2)<L&&(r=(p-f)/2,G=0,D++),k[t]=D,R[D]=v.isLegendInset?10:r,E[t]=G,G+=f}d&&(G=0,D=0,P=0,C=0),!b.legend_show||v.isLegendToShow(n)?(O[n]=h,I[n]=g,(!P||h>=P)&&(P=h),(!C||g>=C)&&(C=g),s=v.isLegendRight||v.isLegendInset?C:P,b.legend_equally?(Object.keys(O).forEach(function(t){O[t]=P}),Object.keys(I).forEach(function(t){I[t]=C}),(r=(p-s*t.length)/2)<L?(G=0,D=0,t.forEach(function(t){_(t)})):_(n,!0)):_(n)):O[n]=I[n]=k[n]=E[n]=0}t=t.filter(function(t){return!l(b.data_names[t])||null!==b.data_names[t]}),p=x(e=e||{},"withTransition",!0),_=x(e,"withTransitionForTransform",!0),v.isLegendInset&&(D=b.legend_inset_step?b.legend_inset_step:t.length,v.updateLegendStep(D)),v.isLegendRight?(a=function(t){return P*k[t]},o=function(t){return R[k[t]]+E[t]}):v.isLegendInset?(a=function(t){return P*k[t]+10},o=function(t){return R[k[t]]+E[t]}):(a=function(t){return R[k[t]]+E[t]},o=function(t){return C*k[t]}),r=function(t,e){return a(t,e)+4+b.legend_item_tile_width},c=function(t,e){return o(t,e)+9},s=function(t,e){return a(t,e)},d=function(t,e){return o(t,e)-5},u=function(t,e){return a(t,e)-2},h=function(t,e){return a(t,e)-2+b.legend_item_tile_width},g=function(t,e){return o(t,e)+4},(f=v.legend.selectAll("."+i.legendItem).data(t).enter().append("g").attr("class",function(t){return v.generateClass(i.legendItem,t)}).style("visibility",function(t){return v.isLegendToShow(t)?"visible":"hidden"}).style("cursor","pointer").on("click",function(t){b.legend_item_onclick?b.legend_item_onclick.call(v,t):v.d3.event.altKey?(v.api.hide(),v.api.show(t)):(v.api.toggle(t),v.isTargetToShow(t)?v.api.focus(t):v.api.revert())}).on("mouseover",function(t){b.legend_item_onmouseover?b.legend_item_onmouseover.call(v,t):(v.d3.select(this).classed(i.legendItemFocused,!0),!v.transiting&&v.isTargetToShow(t)&&v.api.focus(t))}).on("mouseout",function(t){b.legend_item_onmouseout?b.legend_item_onmouseout.call(v,t):(v.d3.select(this).classed(i.legendItemFocused,!1),v.api.revert())})).append("text").text(function(t){return l(b.data_names[t])?b.data_names[t]:t}).each(function(t,e){F(this,t,e)}).style("pointer-events","none").attr("x",v.isLegendRight||v.isLegendInset?r:-200).attr("y",v.isLegendRight||v.isLegendInset?-200:c),f.append("rect").attr("class",i.legendItemEvent).style("fill-opacity",0).attr("x",v.isLegendRight||v.isLegendInset?s:-200).attr("y",v.isLegendRight||v.isLegendInset?-200:d),f.append("line").attr("class",i.legendItemTile).style("stroke",v.color).style("pointer-events","none").attr("x1",v.isLegendRight||v.isLegendInset?u:-200).attr("y1",v.isLegendRight||v.isLegendInset?-200:g).attr("x2",v.isLegendRight||v.isLegendInset?h:-200).attr("y2",v.isLegendRight||v.isLegendInset?-200:g).attr("stroke-width",b.legend_item_tile_height),w=v.legend.select("."+i.legendBackground+" rect"),v.isLegendInset&&P>0&&0===w.size()&&(w=v.legend.insert("g","."+i.legendItem).attr("class",i.legendBackground).append("rect")),y=v.legend.selectAll("text").data(t).text(function(t){return l(b.data_names[t])?b.data_names[t]:t}).each(function(t,e){F(this,t,e)}),(p?y.transition():y).attr("x",r).attr("y",c),m=v.legend.selectAll("rect."+i.legendItemEvent).data(t),(p?m.transition():m).attr("width",function(t){return O[t]}).attr("height",function(t){return I[t]}).attr("x",s).attr("y",d),S=v.legend.selectAll("line."+i.legendItemTile).data(t),(p?S.transition():S).style("stroke",v.levelColor?function(t){return v.levelColor(v.cache[t].values[0].value)}:v.color).attr("x1",u).attr("y1",g).attr("x2",h).attr("y2",g),w&&(p?w.transition():w).attr("height",v.getLegendHeight()-12).attr("width",P*(D+1)+10),v.legend.selectAll("."+i.legendItem).classed(i.legendItemHidden,function(t){return!v.isTargetToShow(t)}),v.updateLegendItemWidth(P),v.updateLegendItemHeight(C),v.updateLegendStep(D),v.updateSizes(),v.updateScales(),v.updateSvgSize(),v.transformAll(_,n),v.legendHasRendered=!0},T.initRegion=function(){this.region=this.main.append("g").attr("clip-path",this.clipPath).attr("class",i.regions)},T.updateRegion=function(t){var e=this,n=e.config;e.region.style("visibility",e.hasArcType()?"hidden":"visible");var a=e.main.select("."+i.regions).selectAll("."+i.region).data(n.regions),r=a.enter().append("rect").attr("x",e.regionX.bind(e)).attr("y",e.regionY.bind(e)).attr("width",e.regionWidth.bind(e)).attr("height",e.regionHeight.bind(e)).style("fill-opacity",0);e.mainRegion=r.merge(a).attr("class",e.classRegion.bind(e)),a.exit().transition().duration(t).style("opacity",0).remove()},T.redrawRegion=function(t,e){var i=this,n=i.mainRegion;return[(t?n.transition(e):n).attr("x",i.regionX.bind(i)).attr("y",i.regionY.bind(i)).attr("width",i.regionWidth.bind(i)).attr("height",i.regionHeight.bind(i)).style("fill-opacity",function(t){return s(t.opacity)?t.opacity:.1})]},T.regionX=function(t){var e=this,i=e.config,n="y"===t.axis?e.y:e.y2;return"y"===t.axis||"y2"===t.axis?i.axis_rotated&&"start"in t?n(t.start):0:i.axis_rotated?0:"start"in t?e.x(e.isTimeSeries()?e.parseDate(t.start):t.start):0},T.regionY=function(t){var e=this,i=e.config,n="y"===t.axis?e.y:e.y2;return"y"===t.axis||"y2"===t.axis?i.axis_rotated?0:"end"in t?n(t.end):0:i.axis_rotated&&"start"in t?e.x(e.isTimeSeries()?e.parseDate(t.start):t.start):0},T.regionWidth=function(t){var e,i=this,n=i.config,a=i.regionX(t),r="y"===t.axis?i.y:i.y2;return(e="y"===t.axis||"y2"===t.axis?n.axis_rotated&&"end"in t?r(t.end):i.width:n.axis_rotated?i.width:"end"in t?i.x(i.isTimeSeries()?i.parseDate(t.end):t.end):i.width)<a?0:e-a},T.regionHeight=function(t){var e,i=this,n=i.config,a=this.regionY(t),r="y"===t.axis?i.y:i.y2;return(e="y"===t.axis||"y2"===t.axis?n.axis_rotated?i.height:"start"in t?r(t.start):i.height:n.axis_rotated&&"end"in t?i.x(i.isTimeSeries()?i.parseDate(t.end):t.end):i.height)<a?0:e-a},T.isRegionOnX=function(t){return!t.axis||"x"===t.axis},T.getScale=function(t,e,i){return(i?this.d3.scaleTime():this.d3.scaleLinear()).range([t,e])},T.getX=function(t,e,i,n){var a,r=this.getScale(t,e,this.isTimeSeries()),s=i?r.domain(i):r;for(a in this.isCategorized()?(n=n||function(){return 0},r=function(t,e){var i=s(t)+n(t);return e?i:Math.ceil(i)}):r=function(t,e){var i=s(t);return e?i:Math.ceil(i)},s)r[a]=s[a];return r.orgDomain=function(){return s.domain()},this.isCategorized()&&(r.domain=function(t){return arguments.length?(s.domain(t),r):[(t=this.orgDomain())[0],t[1]+1]}),r},T.getY=function(t,e,i){var n=this.getScale(t,e,this.isTimeSeriesY());return i&&n.domain(i),n},T.getYScale=function(t){return"y2"===this.axis.getId(t)?this.y2:this.y},T.getSubYScale=function(t){return"y2"===this.axis.getId(t)?this.subY2:this.subY},T.updateScales=function(){var t=this,e=t.config,i=!t.x;t.xMin=e.axis_rotated?1:0,t.xMax=e.axis_rotated?t.height:t.width,t.yMin=e.axis_rotated?0:t.height,t.yMax=e.axis_rotated?t.width:1,t.subXMin=t.xMin,t.subXMax=t.xMax,t.subYMin=e.axis_rotated?0:t.height2,t.subYMax=e.axis_rotated?t.width2:1,t.x=t.getX(t.xMin,t.xMax,i?void 0:t.x.orgDomain(),function(){return t.xAxis.tickOffset()}),t.y=t.getY(t.yMin,t.yMax,i?e.axis_y_default:t.y.domain()),t.y2=t.getY(t.yMin,t.yMax,i?e.axis_y2_default:t.y2.domain()),t.subX=t.getX(t.xMin,t.xMax,t.orgXDomain,function(e){return e%1?0:t.subXAxis.tickOffset()}),t.subY=t.getY(t.subYMin,t.subYMax,i?e.axis_y_default:t.subY.domain()),t.subY2=t.getY(t.subYMin,t.subYMax,i?e.axis_y2_default:t.subY2.domain()),t.xAxisTickFormat=t.axis.getXAxisTickFormat(),t.xAxisTickValues=t.axis.getXAxisTickValues(),t.yAxisTickValues=t.axis.getYAxisTickValues(),t.y2AxisTickValues=t.axis.getY2AxisTickValues(),t.xAxis=t.axis.getXAxis(t.x,t.xOrient,t.xAxisTickFormat,t.xAxisTickValues,e.axis_x_tick_outer),t.subXAxis=t.axis.getXAxis(t.subX,t.subXOrient,t.xAxisTickFormat,t.xAxisTickValues,e.axis_x_tick_outer),t.yAxis=t.axis.getYAxis(t.y,t.yOrient,e.axis_y_tick_format,t.yAxisTickValues,e.axis_y_tick_outer),t.y2Axis=t.axis.getYAxis(t.y2,t.y2Orient,e.axis_y2_tick_format,t.y2AxisTickValues,e.axis_y2_tick_outer),i||t.brush&&t.brush.updateScale(t.subX),t.updateArc&&t.updateArc()},T.selectPoint=function(t,e,n){var a=this,r=a.config,s=(r.axis_rotated?a.circleY:a.circleX).bind(a),o=(r.axis_rotated?a.circleX:a.circleY).bind(a),c=a.pointSelectR.bind(a);r.data_onselected.call(a.api,e,t.node()),a.main.select("."+i.selectedCircles+a.getTargetSelectorSuffix(e.id)).selectAll("."+i.selectedCircle+"-"+n).data([e]).enter().append("circle").attr("class",function(){return a.generateClass(i.selectedCircle,n)}).attr("cx",s).attr("cy",o).attr("stroke",function(){return a.color(e)}).attr("r",function(t){return 1.4*a.pointSelectR(t)}).transition().duration(100).attr("r",c)},T.unselectPoint=function(t,e,n){this.config.data_onunselected.call(this.api,e,t.node()),this.main.select("."+i.selectedCircles+this.getTargetSelectorSuffix(e.id)).selectAll("."+i.selectedCircle+"-"+n).transition().duration(100).attr("r",0).remove()},T.togglePoint=function(t,e,i,n){t?this.selectPoint(e,i,n):this.unselectPoint(e,i,n)},T.selectPath=function(t,e){var i=this;i.config.data_onselected.call(i,e,t.node()),i.config.interaction_brighten&&t.transition().duration(100).style("fill",function(){return i.d3.rgb(i.color(e)).brighter(.75)})},T.unselectPath=function(t,e){var i=this;i.config.data_onunselected.call(i,e,t.node()),i.config.interaction_brighten&&t.transition().duration(100).style("fill",function(){return i.color(e)})},T.togglePath=function(t,e,i,n){t?this.selectPath(e,i,n):this.unselectPath(e,i,n)},T.getToggle=function(t,e){var i;return"circle"===t.nodeName?i=this.isStepType(e)?function(){}:this.togglePoint:"path"===t.nodeName&&(i=this.togglePath),i},T.toggleShape=function(t,e,n){var a=this,r=a.d3,s=a.config,o=r.select(t),c=o.classed(i.SELECTED),d=a.getToggle(t,e).bind(a);s.data_selection_enabled&&s.data_selection_isselectable(e)&&(s.data_selection_multiple||a.main.selectAll("."+i.shapes+(s.data_selection_grouped?a.getTargetSelectorSuffix(e.id):"")).selectAll("."+i.shape).each(function(t,e){var n=r.select(this);n.classed(i.SELECTED)&&d(!1,n.classed(i.SELECTED,!1),t,e)}),o.classed(i.SELECTED,!c),d(!c,o,e,n))},T.initBar=function(){this.main.select("."+i.chart).append("g").attr("class",i.chartBars)},T.updateTargetsForBar=function(t){var e=this,n=e.config,a=e.classChartBar.bind(e),r=e.classBars.bind(e),s=e.classFocus.bind(e);e.main.select("."+i.chartBars).selectAll("."+i.chartBar).data(t).attr("class",function(t){return a(t)+s(t)}).enter().append("g").attr("class",a).style("pointer-events","none").append("g").attr("class",r).style("cursor",function(t){return n.data_selection_isselectable(t)?"pointer":null})},T.updateBar=function(t){var e=this,n=e.barData.bind(e),a=e.classBar.bind(e),r=e.initialOpacity.bind(e),s=function(t){return e.color(t.id)},o=e.main.selectAll("."+i.bars).selectAll("."+i.bar).data(n),c=o.enter().append("path").attr("class",a).style("stroke",s).style("fill",s);e.mainBar=c.merge(o).style("opacity",r),o.exit().transition().duration(t).style("opacity",0)},T.redrawBar=function(t,e,i){return[(e?this.mainBar.transition(i):this.mainBar).attr("d",t).style("stroke",this.color).style("fill",this.color).style("opacity",1)]},T.getBarW=function(t,e){var i=this.config,n="number"==typeof i.bar_width?i.bar_width:e?t.tickInterval()*i.bar_width_ratio/e:0;return i.bar_width_max&&n>i.bar_width_max?i.bar_width_max:n},T.getBars=function(t,e){return(e?this.main.selectAll("."+i.bars+this.getTargetSelectorSuffix(e)):this.main).selectAll("."+i.bar+(s(t)?"-"+t:""))},T.expandBars=function(t,e,n){n&&this.unexpandBars(),this.getBars(t,e).classed(i.EXPANDED,!0)},T.unexpandBars=function(t){this.getBars(t).classed(i.EXPANDED,!1)},T.generateDrawBar=function(t,e){var i=this.config,n=this.generateGetBarPoints(t,e);return function(t,e){var a=n(t,e),r=i.axis_rotated?1:0,s=i.axis_rotated?0:1;return"M "+a[0][r]+","+a[0][s]+" L"+a[1][r]+","+a[1][s]+" L"+a[2][r]+","+a[2][s]+" L"+a[3][r]+","+a[3][s]+" z"}},T.generateGetBarPoints=function(t,e){var i=this,n=e?i.subXAxis:i.xAxis,a=t.__max__+1,r=i.getBarW(n,a),s=i.getShapeX(r,a,t,!!e),o=i.getShapeY(!!e),c=i.getShapeOffset(i.isBarType,t,!!e),d=r*(i.config.bar_space/2),u=e?i.getSubYScale:i.getYScale;return function(t,e){var n=u.call(i,t.id)(0),a=c(t,e)||n,l=s(t),h=o(t);return i.config.axis_rotated&&(0<t.value&&h<n||t.value<0&&n<h)&&(h=n),[[l+d,a],[l+d,h-(n-a)],[l+r-d,h-(n-a)],[l+r-d,a]]}},T.isWithinBar=function(t,e){var i=e.getBoundingClientRect(),n=e.pathSegList.getItem(0),a=e.pathSegList.getItem(1),r=Math.min(n.x,a.x),s=Math.min(n.y,a.y),o=r+i.width+2,c=s+i.height+2,d=s-2;return r-2<t[0]&&t[0]<o&&d<t[1]&&t[1]<c},T.getShapeIndices=function(t){var e,i,n=this.config,a={},r=0;return this.filterTargetsToShow(this.data.targets.filter(t,this)).forEach(function(t){for(e=0;e<n.data_groups.length;e++)if(!(n.data_groups[e].indexOf(t.id)<0))for(i=0;i<n.data_groups[e].length;i++)if(n.data_groups[e][i]in a){a[t.id]=a[n.data_groups[e][i]];break}u(a[t.id])&&(a[t.id]=r++)}),a.__max__=r-1,a},T.getShapeX=function(t,e,i,n){var a=n?this.subX:this.x;return function(n){var r=n.id in i?i[n.id]:0;return n.x||0===n.x?a(n.x)-t*(e/2-r):0}},T.getShapeY=function(t){var e=this;return function(i){return(t?e.getSubYScale(i.id):e.getYScale(i.id))(i.value)}},T.getShapeOffset=function(t,e,i){var n=this,a=n.orderTargets(n.filterTargetsToShow(n.data.targets.filter(t,n))),r=a.map(function(t){return t.id});return function(t,s){var o=i?n.getSubYScale(t.id):n.getYScale(t.id),c=o(0),d=c;return a.forEach(function(i){var a=n.isStepType(t)?n.convertValuesToStep(i.values):i.values;i.id!==t.id&&e[i.id]===e[t.id]&&r.indexOf(i.id)<r.indexOf(t.id)&&(void 0!==a[s]&&+a[s].x==+t.x||(s=-1,a.forEach(function(e,i){e.x===t.x&&(s=i)})),s in a&&a[s].value*t.value>=0&&(d+=o(a[s].value)-c))}),d}},T.isWithinShape=function(t,e){var n,a=this,r=a.d3.select(t);return a.isTargetToShow(e.id)?"circle"===t.nodeName?n=a.isStepType(e)?a.isWithinStep(t,a.getYScale(e.id)(e.value)):a.isWithinCircle(t,1.5*a.pointSelectR(e)):"path"===t.nodeName&&(n=!r.classed(i.bar)||a.isWithinBar(a.d3.mouse(t),t)):n=!1,n},T.getInterpolate=function(t){var e=this,i=e.d3,n={linear:i.curveLinear,"linear-closed":i.curveLinearClosed,basis:i.curveBasis,"basis-open":i.curveBasisOpen,"basis-closed":i.curveBasisClosed,bundle:i.curveBundle,cardinal:i.curveCardinal,"cardinal-open":i.curveCardinalOpen,"cardinal-closed":i.curveCardinalClosed,monotone:i.curveMonotoneX,step:i.curveStep};return e.isSplineType(t)?n[e.config.spline_interpolation_type]||n.cardinal:e.isStepType(t)?n[e.config.line_step_type]:n.linear},T.initLine=function(){this.main.select("."+i.chart).append("g").attr("class",i.chartLines)},T.updateTargetsForLine=function(t){var e,n=this,a=n.config,r=n.classChartLine.bind(n),s=n.classLines.bind(n),o=n.classAreas.bind(n),c=n.classCircles.bind(n),d=n.classFocus.bind(n);(e=n.main.select("."+i.chartLines).selectAll("."+i.chartLine).data(t).attr("class",function(t){return r(t)+d(t)}).enter().append("g").attr("class",r).style("opacity",0).style("pointer-events","none")).append("g").attr("class",s),e.append("g").attr("class",o),e.append("g").attr("class",function(t){return n.generateClass(i.selectedCircles,t.id)}),e.append("g").attr("class",c).style("cursor",function(t){return a.data_selection_isselectable(t)?"pointer":null}),t.forEach(function(t){n.main.selectAll("."+i.selectedCircles+n.getTargetSelectorSuffix(t.id)).selectAll("."+i.selectedCircle).each(function(e){e.value=t.values[e.index].value})})},T.updateLine=function(t){var e=this,n=e.main.selectAll("."+i.lines).selectAll("."+i.line).data(e.lineData.bind(e)),a=n.enter().append("path").attr("class",e.classLine.bind(e)).style("stroke",e.color);e.mainLine=a.merge(n).style("opacity",e.initialOpacity.bind(e)).style("shape-rendering",function(t){return e.isStepType(t)?"crispEdges":""}).attr("transform",null),n.exit().transition().duration(t).style("opacity",0)},T.redrawLine=function(t,e,i){return[(e?this.mainLine.transition(i):this.mainLine).attr("d",t).style("stroke",this.color).style("opacity",1)]},T.generateDrawLine=function(t,e){var i=this,n=i.config,a=i.d3.line(),r=i.generateGetLinePoints(t,e),s=e?i.getSubYScale:i.getYScale,o=function(t){return(e?i.subxx:i.xx).call(i,t)},c=function(t,e){return n.data_groups.length>0?r(t,e)[0][1]:s.call(i,t.id)(t.value)};return a=n.axis_rotated?a.x(c).y(o):a.x(o).y(c),n.line_connectNull||(a=a.defined(function(t){return null!=t.value})),function(t){var r,o=n.line_connectNull?i.filterRemoveNull(t.values):t.values,c=e?i.subX:i.x,d=s.call(i,t.id),u=0,l=0;return i.isLineType(t)?n.data_regions[t.id]?r=i.lineWithRegions(o,c,d,n.data_regions[t.id]):(i.isStepType(t)&&(o=i.convertValuesToStep(o)),r=a.curve(i.getInterpolate(t))(o)):(o[0]&&(u=c(o[0].x),l=d(o[0].value)),r=n.axis_rotated?"M "+l+" "+u:"M "+u+" "+l),r||"M 0 0"}},T.generateGetLinePoints=function(t,e){var i=this,n=i.config,a=t.__max__+1,r=i.getShapeX(0,a,t,!!e),s=i.getShapeY(!!e),o=i.getShapeOffset(i.isLineType,t,!!e),c=e?i.getSubYScale:i.getYScale;return function(t,e){var a=c.call(i,t.id)(0),d=o(t,e)||a,u=r(t),l=s(t);return n.axis_rotated&&(0<t.value&&l<a||t.value<0&&a<l)&&(l=a),[[u,l-(a-d)],[u,l-(a-d)],[u,l-(a-d)],[u,l-(a-d)]]}},T.lineWithRegions=function(t,e,i,n){var a,r,s,o,c,d,h,g,f,p,_,x=this,y=x.config,m="M",S=x.isCategorized()?.5:0,w=[];function v(t,e){var i;for(i=0;i<e.length;i++)if(e[i].start<t&&t<=e[i].end)return!0;return!1}if(l(n))for(a=0;a<n.length;a++)w[a]={},u(n[a].start)?w[a].start=t[0].x:w[a].start=x.isTimeSeries()?x.parseDate(n[a].start):n[a].start,u(n[a].end)?w[a].end=t[t.length-1].x:w[a].end=x.isTimeSeries()?x.parseDate(n[a].end):n[a].end;function b(t){return"M"+t[0][0]+" "+t[0][1]+" "+t[1][0]+" "+t[1][1]}for(p=y.axis_rotated?function(t){return i(t.value)}:function(t){return e(t.x)},_=y.axis_rotated?function(t){return e(t.x)}:function(t){return i(t.value)},s=x.isTimeSeries()?function(t,n,a,r){var s=t.x.getTime(),o=n.x-t.x,d=new Date(s+o*a),u=new Date(s+o*(a+r));return b(y.axis_rotated?[[i(c(a)),e(d)],[i(c(a+r)),e(u)]]:[[e(d),i(c(a))],[e(u),i(c(a+r))]])}:function(t,n,a,r){return b(y.axis_rotated?[[i(c(a),!0),e(o(a))],[i(c(a+r),!0),e(o(a+r))]]:[[e(o(a),!0),i(c(a))],[e(o(a+r),!0),i(c(a+r))]])},a=0;a<t.length;a++){if(u(w)||!v(t[a].x,w))m+=" "+p(t[a])+" "+_(t[a]);else for(o=x.getScale(t[a-1].x+S,t[a].x+S,x.isTimeSeries()),c=x.getScale(t[a-1].value,t[a].value),d=e(t[a].x)-e(t[a-1].x),h=i(t[a].value)-i(t[a-1].value),f=2*(g=2/Math.sqrt(Math.pow(d,2)+Math.pow(h,2))),r=g;r<=1;r+=f)m+=s(t[a-1],t[a],r,g);t[a].x}return m},T.updateArea=function(t){var e=this,n=e.d3,a=e.main.selectAll("."+i.areas).selectAll("."+i.area).data(e.lineData.bind(e)),r=a.enter().append("path").attr("class",e.classArea.bind(e)).style("fill",e.color).style("opacity",function(){return e.orgAreaOpacity=+n.select(this).style("opacity"),0});e.mainArea=r.merge(a).style("opacity",e.orgAreaOpacity),a.exit().transition().duration(t).style("opacity",0)},T.redrawArea=function(t,e,i){return[(e?this.mainArea.transition(i):this.mainArea).attr("d",t).style("fill",this.color).style("opacity",this.orgAreaOpacity)]},T.generateDrawArea=function(t,e){var i=this,n=i.config,a=i.d3.area(),r=i.generateGetAreaPoints(t,e),s=e?i.getSubYScale:i.getYScale,o=function(t){return(e?i.subxx:i.xx).call(i,t)},c=function(t,e){return n.data_groups.length>0?r(t,e)[0][1]:s.call(i,t.id)(i.getAreaBaseValue(t.id))},d=function(t,e){return n.data_groups.length>0?r(t,e)[1][1]:s.call(i,t.id)(t.value)};return a=n.axis_rotated?a.x0(c).x1(d).y(o):a.x(o).y0(n.area_above?0:c).y1(d),n.line_connectNull||(a=a.defined(function(t){return null!==t.value})),function(t){var e,r=n.line_connectNull?i.filterRemoveNull(t.values):t.values,s=0,o=0;return i.isAreaType(t)?(i.isStepType(t)&&(r=i.convertValuesToStep(r)),e=a.curve(i.getInterpolate(t))(r)):(r[0]&&(s=i.x(r[0].x),o=i.getYScale(t.id)(r[0].value)),e=n.axis_rotated?"M "+o+" "+s:"M "+s+" "+o),e||"M 0 0"}},T.getAreaBaseValue=function(){return 0},T.generateGetAreaPoints=function(t,e){var i=this,n=i.config,a=t.__max__+1,r=i.getShapeX(0,a,t,!!e),s=i.getShapeY(!!e),o=i.getShapeOffset(i.isAreaType,t,!!e),c=e?i.getSubYScale:i.getYScale;return function(t,e){var a=c.call(i,t.id)(0),d=o(t,e)||a,u=r(t),l=s(t);return n.axis_rotated&&(0<t.value&&l<a||t.value<0&&a<l)&&(l=a),[[u,d],[u,l-(a-d)],[u,l-(a-d)],[u,d]]}},T.updateCircle=function(t,e){var n=this,a=n.main.selectAll("."+i.circles).selectAll("."+i.circle).data(n.lineOrScatterData.bind(n)),r=a.enter().append("circle").attr("class",n.classCircle.bind(n)).attr("cx",t).attr("cy",e).attr("r",n.pointR.bind(n)).style("fill",n.color);n.mainCircle=r.merge(a).style("opacity",n.initialOpacityForCircle.bind(n)),a.exit().style("opacity",0)},T.redrawCircle=function(t,e,n,a){var r=this,s=r.main.selectAll("."+i.selectedCircle);return[(n?r.mainCircle.transition(a):r.mainCircle).style("opacity",this.opacityForCircle.bind(r)).style("fill",r.color).attr("cx",t).attr("cy",e),(n?s.transition(a):s).attr("cx",t).attr("cy",e)]},T.circleX=function(t){return t.x||0===t.x?this.x(t.x):null},T.updateCircleY=function(){var t,e,i=this;i.config.data_groups.length>0?(t=i.getShapeIndices(i.isLineType),e=i.generateGetLinePoints(t),i.circleY=function(t,i){return e(t,i)[0][1]}):i.circleY=function(t){return i.getYScale(t.id)(t.value)}},T.getCircles=function(t,e){return(e?this.main.selectAll("."+i.circles+this.getTargetSelectorSuffix(e)):this.main).selectAll("."+i.circle+(s(t)?"-"+t:""))},T.expandCircles=function(t,e,n){var a=this.pointExpandedR.bind(this);n&&this.unexpandCircles(),this.getCircles(t,e).classed(i.EXPANDED,!0).attr("r",a)},T.unexpandCircles=function(t){var e=this,n=e.pointR.bind(e);e.getCircles(t).filter(function(){return e.d3.select(this).classed(i.EXPANDED)}).classed(i.EXPANDED,!1).attr("r",n)},T.pointR=function(t){var e=this.config;return this.isStepType(t)?0:o(e.point_r)?e.point_r(t):e.point_r},T.pointExpandedR=function(t){var e=this.config;return e.point_focus_expand_enabled?o(e.point_focus_expand_r)?e.point_focus_expand_r(t):e.point_focus_expand_r?e.point_focus_expand_r:1.75*this.pointR(t):this.pointR(t)},T.pointSelectR=function(t){var e=this.config;return o(e.point_select_r)?e.point_select_r(t):e.point_select_r?e.point_select_r:4*this.pointR(t)},T.isWithinCircle=function(t,e){var i=this.d3,n=i.mouse(t),a=i.select(t),r=+a.attr("cx"),s=+a.attr("cy");return Math.sqrt(Math.pow(r-n[0],2)+Math.pow(s-n[1],2))<e},T.isWithinStep=function(t,e){return Math.abs(e-this.d3.mouse(t)[1])<30},T.getCurrentWidth=function(){var t=this.config;return t.size_width?t.size_width:this.getParentWidth()},T.getCurrentHeight=function(){var t=this.config,e=t.size_height?t.size_height:this.getParentHeight();return e>0?e:320/(this.hasType("gauge")&&!t.gauge_fullCircle?2:1)},T.getCurrentPaddingTop=function(){var t=this.config,e=s(t.padding_top)?t.padding_top:0;return this.title&&this.title.node()&&(e+=this.getTitlePadding()),e},T.getCurrentPaddingBottom=function(){var t=this.config;return s(t.padding_bottom)?t.padding_bottom:0},T.getCurrentPaddingLeft=function(t){var e=this.config;return s(e.padding_left)?e.padding_left:e.axis_rotated?!e.axis_x_show||e.axis_x_inner?1:Math.max(h(this.getAxisWidthByAxisId("x",t)),40):!e.axis_y_show||e.axis_y_inner?this.axis.getYAxisLabelPosition().isOuter?30:1:h(this.getAxisWidthByAxisId("y",t))},T.getCurrentPaddingRight=function(){var t=this,e=t.config,i=t.isLegendRight?t.getLegendWidth()+20:0;return s(e.padding_right)?e.padding_right+1:e.axis_rotated?10+i:!e.axis_y2_show||e.axis_y2_inner?2+i+(t.axis.getY2AxisLabelPosition().isOuter?20:0):h(t.getAxisWidthByAxisId("y2"))+i},T.getParentRectValue=function(t){for(var e,i=this.selectChart.node();i&&"BODY"!==i.tagName;){try{e=i.getBoundingClientRect()[t]}catch(n){"width"===t&&(e=i.offsetWidth)}if(e)break;i=i.parentNode}return e},T.getParentWidth=function(){return this.getParentRectValue("width")},T.getParentHeight=function(){var t=this.selectChart.style("height");return t.indexOf("px")>0?+t.replace("px",""):0},T.getSvgLeft=function(t){var e=this,n=e.config,a=n.axis_rotated||!n.axis_rotated&&!n.axis_y_inner,r=n.axis_rotated?i.axisX:i.axisY,s=e.main.select("."+r).node(),o=s&&a?s.getBoundingClientRect():{right:0},c=e.selectChart.node().getBoundingClientRect(),d=e.hasArcType(),u=o.right-c.left-(d?0:e.getCurrentPaddingLeft(t));return u>0?u:0},T.getAxisWidthByAxisId=function(t,e){var i=this.axis.getLabelPositionById(t);return this.axis.getMaxTickWidth(t,e)+(i.isInner?20:40)},T.getHorizontalAxisHeight=function(t){var e=this,i=e.config,n=30;return"x"!==t||i.axis_x_show?"x"===t&&i.axis_x_height?i.axis_x_height:"y"!==t||i.axis_y_show?"y2"!==t||i.axis_y2_show?("x"===t&&!i.axis_rotated&&i.axis_x_tick_rotate&&(n=30+e.axis.getMaxTickWidth(t)*Math.cos(Math.PI*(90-Math.abs(i.axis_x_tick_rotate))/180)),"y"===t&&i.axis_rotated&&i.axis_y_tick_rotate&&(n=30+e.axis.getMaxTickWidth(t)*Math.cos(Math.PI*(90-Math.abs(i.axis_y_tick_rotate))/180)),n+(e.axis.getLabelPositionById(t).isInner?0:10)+("y2"===t?-10:0)):e.rotated_padding_top:!i.legend_show||e.isLegendRight||e.isLegendInset?1:10:8},T.initBrush=function(t){var e=this,n=e.d3;return e.brush=(e.config.axis_rotated?n.brushY():n.brushX()).on("brush",function(){var t=n.event.sourceEvent;t&&"zoom"===t.type||e.redrawForBrush()}).on("end",function(){var t=n.event.sourceEvent;t&&"zoom"===t.type||e.brush.empty()&&t&&"end"!==t.type&&e.brush.clear()}),e.brush.updateExtent=function(){var t,i=this.scale.range();return t=e.config.axis_rotated?[[0,i[0]],[e.width2,i[1]]]:[[i[0],0],[i[1],e.height2]],this.extent(t),this},e.brush.updateScale=function(t){return this.scale=t,this},e.brush.update=function(t){this.updateScale(t||e.subX).updateExtent(),e.context.select("."+i.brush).call(this)},e.brush.clear=function(){e.context.select("."+i.brush).call(e.brush.move,null)},e.brush.selection=function(){return n.brushSelection(e.context.select("."+i.brush).node())},e.brush.selectionAsValue=function(t,n){var a,r;return t?(e.context&&(a=[this.scale(t[0]),this.scale(t[1])],r=e.context.select("."+i.brush),n&&(r=r.transition()),e.brush.move(r,a)),[]):(a=e.brush.selection()||[0,0],[this.scale.invert(a[0]),this.scale.invert(a[1])])},e.brush.empty=function(){var t=e.brush.selection();return!t||t[0]===t[1]},e.brush.updateScale(t)},T.initSubchart=function(){var t=this,e=t.config,n=t.context=t.svg.append("g").attr("transform",t.getTranslate("context")),a=e.subchart_show?"visible":"hidden";n.style("visibility",a),n.append("g").attr("clip-path",t.clipPathForSubchart).attr("class",i.chart),n.select("."+i.chart).append("g").attr("class",i.chartBars),n.select("."+i.chart).append("g").attr("class",i.chartLines),n.append("g").attr("clip-path",t.clipPath).attr("class",i.brush),t.axes.subx=n.append("g").attr("class",i.axisX).attr("transform",t.getTranslate("subx")).attr("clip-path",e.axis_rotated?"":t.clipPathForXAxis)},T.initSubchartBrush=function(){this.initBrush(this.subX).updateExtent(),this.context.select("."+i.brush).call(this.brush)},T.updateTargetsForSubchart=function(t){var e,n,a,r,s=this,o=s.context,c=s.config,d=s.classChartBar.bind(s),u=s.classBars.bind(s),l=s.classChartLine.bind(s),h=s.classLines.bind(s),g=s.classAreas.bind(s);c.subchart_show&&((a=(r=o.select("."+i.chartBars).selectAll("."+i.chartBar).data(t)).enter().append("g").style("opacity",0)).merge(r).attr("class",d),a.append("g").attr("class",u),(e=(n=o.select("."+i.chartLines).selectAll("."+i.chartLine).data(t)).enter().append("g").style("opacity",0)).merge(n).attr("class",l),e.append("g").attr("class",h),e.append("g").attr("class",g),o.selectAll("."+i.brush+" rect").attr(c.axis_rotated?"width":"height",c.axis_rotated?s.width2:s.height2))},T.updateBarForSubchart=function(t){var e=this,n=e.context.selectAll("."+i.bars).selectAll("."+i.bar).data(e.barData.bind(e)),a=n.enter().append("path").attr("class",e.classBar.bind(e)).style("stroke","none").style("fill",e.color);n.exit().transition().duration(t).style("opacity",0).remove(),e.contextBar=a.merge(n).style("opacity",e.initialOpacity.bind(e))},T.redrawBarForSubchart=function(t,e,i){(e?this.contextBar.transition(Math.random().toString()).duration(i):this.contextBar).attr("d",t).style("opacity",1)},T.updateLineForSubchart=function(t){var e=this,n=e.context.selectAll("."+i.lines).selectAll("."+i.line).data(e.lineData.bind(e)),a=n.enter().append("path").attr("class",e.classLine.bind(e)).style("stroke",e.color);n.exit().transition().duration(t).style("opacity",0).remove(),e.contextLine=a.merge(n).style("opacity",e.initialOpacity.bind(e))},T.redrawLineForSubchart=function(t,e,i){(e?this.contextLine.transition(Math.random().toString()).duration(i):this.contextLine).attr("d",t).style("opacity",1)},T.updateAreaForSubchart=function(t){var e=this,n=e.d3,a=e.context.selectAll("."+i.areas).selectAll("."+i.area).data(e.lineData.bind(e)),r=a.enter().append("path").attr("class",e.classArea.bind(e)).style("fill",e.color).style("opacity",function(){return e.orgAreaOpacity=+n.select(this).style("opacity"),0});a.exit().transition().duration(t).style("opacity",0).remove(),e.contextArea=r.merge(a).style("opacity",0)},T.redrawAreaForSubchart=function(t,e,i){(e?this.contextArea.transition(Math.random().toString()).duration(i):this.contextArea).attr("d",t).style("fill",this.color).style("opacity",this.orgAreaOpacity)},T.redrawSubchart=function(t,e,i,n,a,r,s){var o,c,d,u=this,l=u.d3,h=u.config;u.context.style("visibility",h.subchart_show?"visible":"hidden"),h.subchart_show&&(l.event&&"zoom"===l.event.type&&u.brush.selectionAsValue(u.x.orgDomain()),t&&(u.brush.empty()||u.brush.selectionAsValue(u.x.orgDomain()),o=u.generateDrawArea(a,!0),c=u.generateDrawBar(r,!0),d=u.generateDrawLine(s,!0),u.updateBarForSubchart(i),u.updateLineForSubchart(i),u.updateAreaForSubchart(i),u.redrawBarForSubchart(c,i,i),u.redrawLineForSubchart(d,i,i),u.redrawAreaForSubchart(o,i,i)))},T.redrawForBrush=function(){var t,e=this,n=e.x,a=e.d3;e.redraw({withTransition:!1,withY:e.config.zoom_rescale,withSubchart:!1,withUpdateXDomain:!0,withEventRect:!1,withDimension:!1}),t=a.event.selection||e.brush.scale.range(),e.main.select("."+i.eventRect).call(e.zoom.transform,a.zoomIdentity.scale(e.width/(t[1]-t[0])).translate(-t[0],0)),e.config.subchart_onbrush.call(e.api,n.orgDomain())},T.transformContext=function(t,e){var n;e&&e.axisSubX?n=e.axisSubX:(n=this.context.select("."+i.axisX),t&&(n=n.transition())),this.context.attr("transform",this.getTranslate("context")),n.attr("transform",this.getTranslate("subx"))},T.getDefaultSelection=function(){var t=this,e=t.config,i=o(e.axis_x_selection)?e.axis_x_selection(t.getXDomain(t.data.targets)):e.axis_x_selection;return t.isTimeSeries()&&(i=[t.parseDate(i[0]),t.parseDate(i[1])]),i},T.initText=function(){this.main.select("."+i.chart).append("g").attr("class",i.chartTexts),this.mainText=this.d3.selectAll([])},T.updateTargetsForText=function(t){var e=this,n=e.classChartText.bind(e),a=e.classTexts.bind(e),r=e.classFocus.bind(e),s=e.main.select("."+i.chartTexts).selectAll("."+i.chartText).data(t),o=s.enter().append("g").attr("class",n).style("opacity",0).style("pointer-events","none");o.append("g").attr("class",a),o.merge(s).attr("class",function(t){return n(t)+r(t)})},T.updateText=function(t,e,n){var a=this,r=a.config,s=a.barOrLineData.bind(a),o=a.classText.bind(a),c=a.main.selectAll("."+i.texts).selectAll("."+i.text).data(s),d=c.enter().append("text").attr("class",o).attr("text-anchor",function(t){return r.axis_rotated?t.value<0?"end":"start":"middle"}).style("stroke","none").attr("x",t).attr("y",e).style("fill",function(t){return a.color(t)}).style("fill-opacity",0);a.mainText=d.merge(c).text(function(t,e,i){return a.dataLabelFormat(t.id)(t.value,t.id,e,i)}),c.exit().transition().duration(n).style("fill-opacity",0).remove()},T.redrawText=function(t,e,i,n,a){return[(n?this.mainText.transition(a):this.mainText).attr("x",t).attr("y",e).style("fill",this.color).style("fill-opacity",i?0:this.opacityForText.bind(this))]},T.getTextRect=function(t,e,i){var n,a=this.d3.select("body").append("div").classed("c3",!0),r=a.append("svg").style("visibility","hidden").style("position","fixed").style("top",0).style("left",0),s=this.d3.select(i).style("font");return r.selectAll(".dummy").data([t]).enter().append("text").classed(e||"",!0).style("font",s).text(t).each(function(){n=this.getBoundingClientRect()}),a.remove(),n},T.generateXYForText=function(t,e,i,n){var a=this,r=a.generateGetAreaPoints(t,!1),s=a.generateGetBarPoints(e,!1),o=a.generateGetLinePoints(i,!1),c=n?a.getXForText:a.getYForText;return function(t,e){var i=a.isAreaType(t)?r:a.isBarType(t)?s:o;return c.call(a,i(t,e),t,this)}},T.getXForText=function(t,e,i){var n,a,r=this,s=i.getBoundingClientRect();return r.config.axis_rotated?(a=r.isBarType(e)?4:6,n=t[2][1]+a*(e.value<0?-1:1)):n=r.hasType("bar")?(t[2][0]+t[0][0])/2:t[0][0],null===e.value&&(n>r.width?n=r.width-s.width:n<0&&(n=4)),n},T.getYForText=function(t,e,i){var n,a=this,r=i.getBoundingClientRect();return a.config.axis_rotated?n=(t[0][0]+t[2][0]+.6*r.height)/2:(n=t[2][1],e.value<0||0===e.value&&!a.hasPositiveValue?(n+=r.height,a.isBarType(e)&&a.isSafari()?n-=3:!a.isBarType(e)&&a.isChrome()&&(n+=3)):n+=a.isBarType(e)?-3:-6),null!==e.value||a.config.axis_rotated||(n<r.height?n=r.height:n>this.height&&(n=this.height-4)),n},T.initTitle=function(){this.title=this.svg.append("text").text(this.config.title_text).attr("class",this.CLASS.title)},T.redrawTitle=function(){var t=this;t.title.attr("x",t.xForTitle.bind(t)).attr("y",t.yForTitle.bind(t))},T.xForTitle=function(){var t=this,e=t.config,i=e.title_position||"left";return i.indexOf("right")>=0?t.currentWidth-t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).width-e.title_padding.right:i.indexOf("center")>=0?(t.currentWidth-t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).width)/2:e.title_padding.left},T.yForTitle=function(){var t=this;return t.config.title_padding.top+t.getTextRect(t.title.node().textContent,t.CLASS.title,t.title.node()).height},T.getTitlePadding=function(){return this.yForTitle()+this.config.title_padding.bottom},T.initTooltip=function(){var t,e=this,n=e.config;if(e.tooltip=e.selectChart.style("position","relative").append("div").attr("class",i.tooltipContainer).style("position","absolute").style("pointer-events","none").style("display","none"),n.tooltip_init_show){if(e.isTimeSeries()&&d(n.tooltip_init_x)){for(n.tooltip_init_x=e.parseDate(n.tooltip_init_x),t=0;t<e.data.targets[0].values.length&&e.data.targets[0].values[t].x-n.tooltip_init_x!=0;t++);n.tooltip_init_x=t}e.tooltip.html(n.tooltip_contents.call(e,e.data.targets.map(function(t){return e.addName(t.values[n.tooltip_init_x])}),e.axis.getXAxisTickFormat(),e.getYFormat(e.hasArcType()),e.color)),e.tooltip.style("top",n.tooltip_init_position.top).style("left",n.tooltip_init_position.left).style("display","block")}},T.getTooltipSortFunction=function(){var t=this,e=t.config;if(0!==e.data_groups.length&&void 0===e.tooltip_order){var i=t.orderTargets(t.data.targets).map(function(t){return t.id});return(t.isOrderAsc()||t.isOrderDesc())&&(i=i.reverse()),function(t,e){return i.indexOf(t.id)-i.indexOf(e.id)}}var n=e.tooltip_order;void 0===n&&(n=e.data_order);var a=function(t){return t?t.value:null};if(d(n)&&"asc"===n.toLowerCase())return function(t,e){return a(t)-a(e)};if(d(n)&&"desc"===n.toLowerCase())return function(t,e){return a(e)-a(t)};if(o(n)){var r=n;return void 0===e.tooltip_order&&(r=function(t,e){return n(t?{id:t.id,values:[t]}:null,e?{id:e.id,values:[e]}:null)}),r}return c(n)?function(t,e){return n.indexOf(t.id)-n.indexOf(e.id)}:void 0},T.getTooltipContent=function(t,e,i,n){var a,r,s,o,c,d,u=this,l=u.config,h=l.tooltip_format_title||e,g=l.tooltip_format_name||function(t){return t},f=l.tooltip_format_value||i,p=this.getTooltipSortFunction();for(p&&t.sort(p),r=0;r<t.length;r++)if(t[r]&&(t[r].value||0===t[r].value)&&(a||(s=m(h?h(t[r].x):t[r].x),a="<table class='"+u.CLASS.tooltip+"'>"+(s||0===s?"<tr><th colspan='2'>"+s+"</th></tr>":"")),void 0!==(o=m(f(t[r].value,t[r].ratio,t[r].id,t[r].index,t))))){if(null===t[r].name)continue;c=m(g(t[r].name,t[r].ratio,t[r].id,t[r].index)),d=u.levelColor?u.levelColor(t[r].value):n(t[r].id),a+="<tr class='"+u.CLASS.tooltipName+"-"+u.getTargetSelectorSuffix(t[r].id)+"'>",a+="<td class='name'><span style='background-color:"+d+"'></span>"+c+"</td>",a+="<td class='value'>"+o+"</td>",a+="</tr>"}return a+"</table>"},T.tooltipPosition=function(t,e,i,n){var a,r,s,o,c,d=this,u=d.config,l=d.d3,h=d.hasArcType(),g=l.mouse(n);return h?(r=(d.width-(d.isLegendRight?d.getLegendWidth():0))/2+g[0],o=(d.hasType("gauge")?d.height:d.height/2)+g[1]+20):(a=d.getSvgLeft(!0),u.axis_rotated?(s=(r=a+g[0]+100)+e,c=d.currentWidth-d.getCurrentPaddingRight(),o=d.x(t[0].x)+20):(s=(r=a+d.getCurrentPaddingLeft(!0)+d.x(t[0].x)+20)+e,c=a+d.currentWidth-d.getCurrentPaddingRight(),o=g[1]+15),s>c&&(r-=s-c+20),o+i>d.currentHeight&&(o-=i+30)),o<0&&(o=0),{top:o,left:r}},T.showTooltip=function(t,e){var i,n,a,r=this,o=r.config,c=r.hasArcType(),d=t.filter(function(t){return t&&s(t.value)}),u=o.tooltip_position||T.tooltipPosition;0!==d.length&&o.tooltip_show&&(r.tooltip.html(o.tooltip_contents.call(r,t,r.axis.getXAxisTickFormat(),r.getYFormat(c),r.color)).style("display","block"),i=r.tooltip.property("offsetWidth"),n=r.tooltip.property("offsetHeight"),a=u.call(this,d,i,n,e),r.tooltip.style("top",a.top+"px").style("left",a.left+"px"))},T.hideTooltip=function(){this.tooltip.style("display","none")},T.setTargetType=function(t,e){var i=this,n=i.config;i.mapToTargetIds(t).forEach(function(t){i.withoutFadeIn[t]=e===n.data_types[t],n.data_types[t]=e}),t||(n.data_type=e)},T.hasType=function(t,e){var i=this.config.data_types,n=!1;return(e=e||this.data.targets)&&e.length?e.forEach(function(e){var a=i[e.id];(a&&a.indexOf(t)>=0||!a&&"line"===t)&&(n=!0)}):Object.keys(i).length?Object.keys(i).forEach(function(e){i[e]===t&&(n=!0)}):n=this.config.data_type===t,n},T.hasArcType=function(t){return this.hasType("pie",t)||this.hasType("donut",t)||this.hasType("gauge",t)},T.isLineType=function(t){var e=this.config,i=d(t)?t:t.id;return!e.data_types[i]||["line","spline","area","area-spline","step","area-step"].indexOf(e.data_types[i])>=0},T.isStepType=function(t){var e=d(t)?t:t.id;return["step","area-step"].indexOf(this.config.data_types[e])>=0},T.isSplineType=function(t){var e=d(t)?t:t.id;return["spline","area-spline"].indexOf(this.config.data_types[e])>=0},T.isAreaType=function(t){var e=d(t)?t:t.id;return["area","area-spline","area-step"].indexOf(this.config.data_types[e])>=0},T.isBarType=function(t){var e=d(t)?t:t.id;return"bar"===this.config.data_types[e]},T.isScatterType=function(t){var e=d(t)?t:t.id;return"scatter"===this.config.data_types[e]},T.isPieType=function(t){var e=d(t)?t:t.id;return"pie"===this.config.data_types[e]},T.isGaugeType=function(t){var e=d(t)?t:t.id;return"gauge"===this.config.data_types[e]},T.isDonutType=function(t){var e=d(t)?t:t.id;return"donut"===this.config.data_types[e]},T.isArcType=function(t){return this.isPieType(t)||this.isDonutType(t)||this.isGaugeType(t)},T.lineData=function(t){return this.isLineType(t)?[t]:[]},T.arcData=function(t){return this.isArcType(t.data)?[t]:[]},T.barData=function(t){return this.isBarType(t)?t.values:[]},T.lineOrScatterData=function(t){return this.isLineType(t)||this.isScatterType(t)?t.values:[]},T.barOrLineData=function(t){return this.isBarType(t)||this.isLineType(t)?t.values:[]},T.isSafari=function(){var t=window.navigator.userAgent;return t.indexOf("Safari")>=0&&t.indexOf("Chrome")<0},T.isChrome=function(){return window.navigator.userAgent.indexOf("Chrome")>=0},T.initZoom=function(){var t,e=this,i=e.d3,n=e.config;return e.zoom=i.zoom().on("start",function(){var a=i.event.sourceEvent;a&&"brush"===a.type||(t=a,n.zoom_onzoomstart.call(e.api,a))}).on("zoom",function(){var t=i.event.sourceEvent;t&&"brush"===t.type||e.redrawForZoom.call(e)}).on("end",function(){var a=i.event.sourceEvent;a&&"brush"===a.type||a&&t.clientX===a.clientX&&t.clientY===a.clientY||n.zoom_onzoomend.call(e.api,e.x.orgDomain())}),e.zoom.updateDomain=function(){return i.event&&i.event.transform&&e.x.domain(i.event.transform.rescaleX(e.subX).domain()),this},e.zoom.updateExtent=function(){return this.scaleExtent([1,1/0]).translateExtent([[0,0],[e.width,e.height]]).extent([[0,0],[e.width,e.height]]),this},e.zoom.update=function(){return this.updateExtent().updateDomain()},e.zoom.updateExtent()},T.zoomTransform=function(t){var e=[this.x(t[0]),this.x(t[1])];return this.d3.zoomIdentity.scale(this.width/(e[1]-e[0])).translate(-e[0],0)},T.getZoomDomain=function(){var t=this.config,e=this.d3;return[e.min([this.orgXDomain[0],t.zoom_x_min]),e.max([this.orgXDomain[1],t.zoom_x_max])]},T.redrawForZoom=function(){var t=this,e=t.d3,i=t.config,n=t.zoom,a=t.x;i.zoom_enabled&&0!==t.filterTargetsToShow(t.data.targets).length&&(n.update(),t.isCategorized()&&a.orgDomain()[0]===t.orgXDomain[0]&&a.domain([t.orgXDomain[0]-1e-10,a.orgDomain()[1]]),t.redraw({withTransition:!1,withY:i.zoom_rescale,withSubchart:!1,withEventRect:!1,withDimension:!1}),e.event.sourceEvent&&"mousemove"===e.event.sourceEvent.type&&(t.cancelClick=!0),i.zoom_onzoom.call(t.api,a.orgDomain()))},A});
diff --git a/server/site/js/d3.js b/server/site/js/d3.js
new file mode 100644
index 0000000..5845717
--- /dev/null
+++ b/server/site/js/d3.js
@@ -0,0 +1,2 @@
+// https://d3js.org Version 4.13.0. Copyright 2018 Mike Bostock.
+(function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})})(this,function(t){"use strict";function n(t,n){return t<n?-1:t>n?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=function(t){return function(e,r){return n(t(e),r)}}(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r<i;){var o=r+i>>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r<i;){var o=r+i>>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t,n){return[t,n]}function i(t){return null===t?NaN:+t}function o(t,n){var e,r,o=t.length,u=0,a=-1,c=0,s=0;if(null==n)for(;++a<o;)isNaN(e=i(t[a]))||(s+=(r=e-c)*(e-(c+=r/++u)));else for(;++a<o;)isNaN(e=i(n(t[a],a,t)))||(s+=(r=e-c)*(e-(c+=r/++u)));if(u>1)return s/(u-1)}function u(t,n){var e=o(t,n);return e?Math.sqrt(e):e}function a(t,n){var e,r,i,o=t.length,u=-1;if(null==n){for(;++u<o;)if(null!=(e=t[u])&&e>=e)for(r=i=e;++u<o;)null!=(e=t[u])&&(r>e&&(r=e),i<e&&(i=e))}else for(;++u<o;)if(null!=(e=n(t[u],u,t))&&e>=e)for(r=i=e;++u<o;)null!=(e=n(t[u],u,t))&&(r>e&&(r=e),i<e&&(i=e));return[r,i]}function c(t){return function(){return t}}function s(t){return t}function f(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r<i;)o[r]=t+r*e;return o}function l(t,n,e){var r,i,o,u,a=-1;if(n=+n,t=+t,e=+e,t===n&&e>0)return[t];if((r=n<t)&&(i=t,t=n,n=i),0===(u=h(t,n,e))||!isFinite(u))return[];if(u>0)for(t=Math.ceil(t/u),n=Math.floor(n/u),o=new Array(i=Math.ceil(n-t+1));++a<i;)o[a]=(t+a)*u;else for(t=Math.floor(t*u),n=Math.ceil(n*u),o=new Array(i=Math.ceil(t-n+1));++a<i;)o[a]=(t-a)/u;return r&&o.reverse(),o}function h(t,n,e){var r=(n-t)/Math.max(0,e),i=Math.floor(Math.log(r)/Math.LN10),o=r/Math.pow(10,i);return i>=0?(o>=Hs?10:o>=js?5:o>=Xs?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=Hs?10:o>=js?5:o>=Xs?2:1)}function p(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=Hs?i*=10:o>=js?i*=5:o>=Xs&&(i*=2),n<t?-i:i}function d(t){return Math.ceil(Math.log(t.length)/Math.LN2)+1}function v(t,n,e){if(null==e&&(e=i),r=t.length){if((n=+n)<=0||r<2)return+e(t[0],0,t);if(n>=1)return+e(t[r-1],r-1,t);var r,o=(r-1)*n,u=Math.floor(o),a=+e(t[u],u,t);return a+(+e(t[u+1],u+1,t)-a)*(o-u)}}function g(t){for(var n,e,r,i=t.length,o=-1,u=0;++o<i;)u+=t[o].length;for(e=new Array(u);--i>=0;)for(n=(r=t[i]).length;--n>=0;)e[--u]=r[n];return e}function _(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o<i;)if(null!=(e=t[o])&&e>=e)for(r=e;++o<i;)null!=(e=t[o])&&r>e&&(r=e)}else for(;++o<i;)if(null!=(e=n(t[o],o,t))&&e>=e)for(r=e;++o<i;)null!=(e=n(t[o],o,t))&&r>e&&(r=e);return r}function y(t){if(!(i=t.length))return[];for(var n=-1,e=_(t,m),r=new Array(e);++n<e;)for(var i,o=-1,u=r[n]=new Array(i);++o<i;)u[o]=t[o][n];return r}function m(t){return t.length}function x(t){return t}function b(t){return"translate("+(t+.5)+",0)"}function w(t){return"translate(0,"+(t+.5)+")"}function M(){return!this.__axis}function T(t,n){function e(e){var h=null==i?n.ticks?n.ticks.apply(n,r):n.domain():i,p=null==o?n.tickFormat?n.tickFormat.apply(n,r):x:o,d=Math.max(u,0)+c,v=n.range(),g=+v[0]+.5,_=+v[v.length-1]+.5,y=(n.bandwidth?function(t){var n=Math.max(0,t.bandwidth()-1)/2;return t.round()&&(n=Math.round(n)),function(e){return+t(e)+n}}:function(t){return function(n){return+t(n)}})(n.copy()),m=e.selection?e.selection():e,b=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(h,n).order(),T=w.exit(),N=w.enter().append("g").attr("class","tick"),k=w.select("line"),S=w.select("text");b=b.merge(b.enter().insert("path",".tick").attr("class","domain").attr("stroke","#000")),w=w.merge(N),k=k.merge(N.append("line").attr("stroke","#000").attr(f+"2",s*u)),S=S.merge(N.append("text").attr("fill","#000").attr(f,s*d).attr("dy",t===$s?"0em":t===Zs?"0.71em":"0.32em")),e!==m&&(b=b.transition(e),w=w.transition(e),k=k.transition(e),S=S.transition(e),T=T.transition(e).attr("opacity",Qs).attr("transform",function(t){return isFinite(t=y(t))?l(t):this.getAttribute("transform")}),N.attr("opacity",Qs).attr("transform",function(t){var n=this.parentNode.__axis;return l(n&&isFinite(n=n(t))?n:y(t))})),T.remove(),b.attr("d",t===Gs||t==Ws?"M"+s*a+","+g+"H0.5V"+_+"H"+s*a:"M"+g+","+s*a+"V0.5H"+_+"V"+s*a),w.attr("opacity",1).attr("transform",function(t){return l(y(t))}),k.attr(f+"2",s*u),S.attr(f,s*d).text(p),m.filter(M).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===Ws?"start":t===Gs?"end":"middle"),m.each(function(){this.__axis=y})}var r=[],i=null,o=null,u=6,a=6,c=3,s=t===$s||t===Gs?-1:1,f=t===Gs||t===Ws?"x":"y",l=t===$s||t===Zs?b:w;return e.scale=function(t){return arguments.length?(n=t,e):n},e.ticks=function(){return r=Vs.call(arguments),e},e.tickArguments=function(t){return arguments.length?(r=null==t?[]:Vs.call(t),e):r.slice()},e.tickValues=function(t){return arguments.length?(i=null==t?null:Vs.call(t),e):i&&i.slice()},e.tickFormat=function(t){return arguments.length?(o=t,e):o},e.tickSize=function(t){return arguments.length?(u=a=+t,e):u},e.tickSizeInner=function(t){return arguments.length?(u=+t,e):u},e.tickSizeOuter=function(t){return arguments.length?(a=+t,e):a},e.tickPadding=function(t){return arguments.length?(c=+t,e):c},e}function N(){for(var t,n=0,e=arguments.length,r={};n<e;++n){if(!(t=arguments[n]+"")||t in r)throw new Error("illegal type: "+t);r[t]=[]}return new k(r)}function k(t){this._=t}function S(t,n,e){for(var r=0,i=t.length;r<i;++r)if(t[r].name===n){t[r]=Js,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=e&&t.push({name:n,value:e}),t}function E(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),tf.hasOwnProperty(n)?{space:tf[n],local:t}:t}function A(t){var n=E(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===Ks&&n.documentElement.namespaceURI===Ks?n.createElement(t):n.createElementNS(e,t)}})(n)}function C(){}function z(t){return null==t?C:function(){return this.querySelector(t)}}function P(){return[]}function R(t){return null==t?P:function(){return this.querySelectorAll(t)}}function L(t){return new Array(t.length)}function q(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function D(t,n,e,r,i,o){for(var u,a=0,c=n.length,s=o.length;a<s;++a)(u=n[a])?(u.__data__=o[a],r[a]=u):e[a]=new q(t,o[a]);for(;a<c;++a)(u=n[a])&&(i[a]=u)}function U(t,n,e,r,i,o,u){var a,c,s,f={},l=n.length,h=o.length,p=new Array(l);for(a=0;a<l;++a)(c=n[a])&&(p[a]=s=uf+u.call(c,c.__data__,a,n),s in f?i[a]=c:f[s]=c);for(a=0;a<h;++a)(c=f[s=uf+u.call(t,o[a],a,o)])?(r[a]=c,c.__data__=o[a],f[s]=null):e[a]=new q(t,o[a]);for(a=0;a<l;++a)(c=n[a])&&f[p[a]]===c&&(i[a]=c)}function O(t,n){return t<n?-1:t>n?1:t>=n?0:NaN}function F(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function I(t,n){return t.style.getPropertyValue(n)||F(t).getComputedStyle(t,null).getPropertyValue(n)}function Y(t){return t.trim().split(/^|\s+/)}function B(t){return t.classList||new H(t)}function H(t){this._node=t,this._names=Y(t.getAttribute("class")||"")}function j(t,n){for(var e=B(t),r=-1,i=n.length;++r<i;)e.add(n[r])}function X(t,n){for(var e=B(t),r=-1,i=n.length;++r<i;)e.remove(n[r])}function V(){this.textContent=""}function $(){this.innerHTML=""}function W(){this.nextSibling&&this.parentNode.appendChild(this)}function Z(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function G(){return null}function Q(){var t=this.parentNode;t&&t.removeChild(this)}function J(){return this.parentNode.insertBefore(this.cloneNode(!1),this.nextSibling)}function K(){return this.parentNode.insertBefore(this.cloneNode(!0),this.nextSibling)}function tt(t,n,e){return t=nt(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function nt(n,e,r){return function(i){var o=t.event;t.event=i;try{n.call(this,this.__data__,e,r)}finally{t.event=o}}}function et(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r<o;++r)e=n[r],t.type&&e.type!==t.type||e.name!==t.name?n[++i]=e:this.removeEventListener(e.type,e.listener,e.capture);++i?n.length=i:delete this.__on}}}function rt(t,n,e){var r=af.hasOwnProperty(t.type)?tt:nt;return function(i,o,u){var a,c=this.__on,s=r(n,o,u);if(c)for(var f=0,l=c.length;f<l;++f)if((a=c[f]).type===t.type&&a.name===t.name)return this.removeEventListener(a.type,a.listener,a.capture),this.addEventListener(a.type,a.listener=s,a.capture=e),void(a.value=n);this.addEventListener(t.type,s,e),a={type:t.type,name:t.name,value:n,listener:s,capture:e},c?c.push(a):this.__on=[a]}}function it(n,e,r,i){var o=t.event;n.sourceEvent=t.event,t.event=n;try{return e.apply(r,i)}finally{t.event=o}}function ot(t,n,e){var r=F(t),i=r.CustomEvent;"function"==typeof i?i=new i(n,e):(i=r.document.createEvent("Event"),e?(i.initEvent(n,e.bubbles,e.cancelable),i.detail=e.detail):i.initEvent(n,!1,!1)),t.dispatchEvent(i)}function ut(t,n){this._groups=t,this._parents=n}function at(){return new ut([[document.documentElement]],cf)}function ct(t){return"string"==typeof t?new ut([[document.querySelector(t)]],[document.documentElement]):new ut([[t]],cf)}function st(){return new ft}function ft(){this._="@"+(++sf).toString(36)}function lt(){for(var n,e=t.event;n=e.sourceEvent;)e=n;return e}function ht(t,n){var e=t.ownerSVGElement||t;if(e.createSVGPoint){var r=e.createSVGPoint();return r.x=n.clientX,r.y=n.clientY,r=r.matrixTransform(t.getScreenCTM().inverse()),[r.x,r.y]}var i=t.getBoundingClientRect();return[n.clientX-i.left-t.clientLeft,n.clientY-i.top-t.clientTop]}function pt(t){var n=lt();return n.changedTouches&&(n=n.changedTouches[0]),ht(t,n)}function dt(t,n,e){arguments.length<3&&(e=n,n=lt().changedTouches);for(var r,i=0,o=n?n.length:0;i<o;++i)if((r=n[i]).identifier===e)return ht(t,r);return null}function vt(){t.event.stopImmediatePropagation()}function gt(){t.event.preventDefault(),t.event.stopImmediatePropagation()}function _t(t){var n=t.document.documentElement,e=ct(t).on("dragstart.drag",gt,!0);"onselectstart"in n?e.on("selectstart.drag",gt,!0):(n.__noselect=n.style.MozUserSelect,n.style.MozUserSelect="none")}function yt(t,n){var e=t.document.documentElement,r=ct(t).on("dragstart.drag",null);n&&(r.on("click.drag",gt,!0),setTimeout(function(){r.on("click.drag",null)},0)),"onselectstart"in e?r.on("selectstart.drag",null):(e.style.MozUserSelect=e.__noselect,delete e.__noselect)}function mt(t){return function(){return t}}function xt(t,n,e,r,i,o,u,a,c,s){this.target=t,this.type=n,this.subject=e,this.identifier=r,this.active=i,this.x=o,this.y=u,this.dx=a,this.dy=c,this._=s}function bt(){return!t.event.button}function wt(){return this.parentNode}function Mt(n){return null==n?{x:t.event.x,y:t.event.y}:n}function Tt(){return"ontouchstart"in this}function Nt(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function kt(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function St(){}function Et(t){var n;return t=(t+"").trim().toLowerCase(),(n=pf.exec(t))?(n=parseInt(n[1],16),new Rt(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=df.exec(t))?At(parseInt(n[1],16)):(n=vf.exec(t))?new Rt(n[1],n[2],n[3],1):(n=gf.exec(t))?new Rt(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=_f.exec(t))?Ct(n[1],n[2],n[3],n[4]):(n=yf.exec(t))?Ct(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=mf.exec(t))?Lt(n[1],n[2]/100,n[3]/100,1):(n=xf.exec(t))?Lt(n[1],n[2]/100,n[3]/100,n[4]):bf.hasOwnProperty(t)?At(bf[t]):"transparent"===t?new Rt(NaN,NaN,NaN,0):null}function At(t){return new Rt(t>>16&255,t>>8&255,255&t,1)}function Ct(t,n,e,r){return r<=0&&(t=n=e=NaN),new Rt(t,n,e,r)}function zt(t){return t instanceof St||(t=Et(t)),t?(t=t.rgb(),new Rt(t.r,t.g,t.b,t.opacity)):new Rt}function Pt(t,n,e,r){return 1===arguments.length?zt(t):new Rt(t,n,e,null==r?1:r)}function Rt(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Lt(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Dt(t,n,e,r)}function qt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Dt)return new Dt(t.h,t.s,t.l,t.opacity);if(t instanceof St||(t=Et(t)),!t)return new Dt;if(t instanceof Dt)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e<r):e===o?(r-n)/a+2:(n-e)/a+4,a/=c<.5?o+i:2-o-i,u*=60):a=c>0&&c<1?0:u,new Dt(u,a,c,t.opacity)}(t):new Dt(t,n,e,null==r?1:r)}function Dt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Ut(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Ot(t){if(t instanceof It)return new It(t.l,t.a,t.b,t.opacity);if(t instanceof Vt){var n=t.h*wf;return new It(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof Rt||(t=zt(t));var e=jt(t.r),r=jt(t.g),i=jt(t.b),o=Yt((.4124564*e+.3575761*r+.1804375*i)/Tf),u=Yt((.2126729*e+.7151522*r+.072175*i)/Nf);return new It(116*u-16,500*(o-u),200*(u-Yt((.0193339*e+.119192*r+.9503041*i)/kf)),t.opacity)}function Ft(t,n,e,r){return 1===arguments.length?Ot(t):new It(t,n,e,null==r?1:r)}function It(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Yt(t){return t>Cf?Math.pow(t,1/3):t/Af+Sf}function Bt(t){return t>Ef?t*t*t:Af*(t-Sf)}function Ht(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function jt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Xt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Vt)return new Vt(t.h,t.c,t.l,t.opacity);t instanceof It||(t=Ot(t));var n=Math.atan2(t.b,t.a)*Mf;return new Vt(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new Vt(t,n,e,null==r?1:r)}function Vt(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function $t(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Wt)return new Wt(t.h,t.s,t.l,t.opacity);t instanceof Rt||(t=zt(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Df*r+Lf*n-qf*e)/(Df+Lf-qf),o=r-i,u=(Rf*(e-i)-zf*o)/Pf,a=Math.sqrt(u*u+o*o)/(Rf*i*(1-i)),c=a?Math.atan2(u,o)*Mf-120:NaN;return new Wt(c<0?c+360:c,a,i,t.opacity)}(t):new Wt(t,n,e,null==r?1:r)}function Wt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Zt(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Gt(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=r<n-1?t[r+2]:2*o-i;return Zt((e-r/n)*n,u,i,o,a)}}function Qt(t){var n=t.length;return function(e){var r=Math.floor(((e%=1)<0?++e:e)*n),i=t[(r+n-1)%n],o=t[r%n],u=t[(r+1)%n],a=t[(r+2)%n];return Zt((e-r/n)*n,i,o,u,a)}}function Jt(t){return function(){return t}}function Kt(t,n){return function(e){return t+e*n}}function tn(t,n){var e=n-t;return e?Kt(t,e>180||e<-180?e-360*Math.round(e/360):e):Jt(isNaN(t)?n:t)}function nn(t){return 1==(t=+t)?en:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Jt(isNaN(n)?e:n)}}function en(t,n){var e=n-t;return e?Kt(t,e):Jt(isNaN(t)?n:t)}function rn(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e<i;++e)r=Pt(n[e]),o[e]=r.r||0,u[e]=r.g||0,a[e]=r.b||0;return o=t(o),u=t(u),a=t(a),r.opacity=1,function(t){return r.r=o(t),r.g=u(t),r.b=a(t),r+""}}}function on(t,n){var e,r=n?n.length:0,i=t?Math.min(r,t.length):0,o=new Array(i),u=new Array(r);for(e=0;e<i;++e)o[e]=fn(t[e],n[e]);for(;e<r;++e)u[e]=n[e];return function(t){for(e=0;e<i;++e)u[e]=o[e](t);return u}}function un(t,n){var e=new Date;return t=+t,n-=t,function(r){return e.setTime(t+n*r),e}}function an(t,n){return t=+t,n-=t,function(e){return t+n*e}}function cn(t,n){var e,r={},i={};null!==t&&"object"==typeof t||(t={}),null!==n&&"object"==typeof n||(n={});for(e in n)e in t?r[e]=fn(t[e],n[e]):i[e]=n[e];return function(t){for(e in r)i[e]=r[e](t);return i}}function sn(t,n){var e,r,i,o=Vf.lastIndex=$f.lastIndex=0,u=-1,a=[],c=[];for(t+="",n+="";(e=Vf.exec(t))&&(r=$f.exec(n));)(i=r.index)>o&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:an(e,r)})),o=$f.lastIndex;return o<n.length&&(i=n.slice(o),a[u]?a[u]+=i:a[++u]=i),a.length<2?c[0]?function(t){return function(n){return t(n)+""}}(c[0].x):function(t){return function(){return t}}(n):(n=c.length,function(t){for(var e,r=0;r<n;++r)a[(e=c[r]).i]=e.x(t);return a.join("")})}function fn(t,n){var e,r=typeof n;return null==n||"boolean"===r?Jt(n):("number"===r?an:"string"===r?(e=Et(n))?(n=e,Hf):sn:n instanceof Et?Hf:n instanceof Date?un:Array.isArray(n)?on:"function"!=typeof n.valueOf&&"function"!=typeof n.toString||isNaN(n)?cn:an)(t,n)}function ln(t,n){return t=+t,n-=t,function(e){return Math.round(t+n*e)}}function hn(t,n,e,r,i,o){var u,a,c;return(u=Math.sqrt(t*t+n*n))&&(t/=u,n/=u),(c=t*e+n*r)&&(e-=t*c,r-=n*c),(a=Math.sqrt(e*e+r*r))&&(e/=a,r/=a,c/=a),t*r<n*e&&(t=-t,n=-n,c=-c,u=-u),{translateX:i,translateY:o,rotate:Math.atan2(n,t)*Wf,skewX:Math.atan(c)*Wf,scaleX:u,scaleY:a}}function pn(t,n,e,r){function i(t){return t.length?t.pop()+" ":""}return function(o,u){var a=[],c=[];return o=t(o),u=t(u),function(t,r,i,o,u,a){if(t!==i||r!==o){var c=u.push("translate(",null,n,null,e);a.push({i:c-4,x:an(t,i)},{i:c-2,x:an(r,o)})}else(i||o)&&u.push("translate("+i+n+o+e)}(o.translateX,o.translateY,u.translateX,u.translateY,a,c),function(t,n,e,o){t!==n?(t-n>180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:an(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:an(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,c),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:an(t,e)},{i:a-2,x:an(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,c),o=u=null,function(t){for(var n,e=-1,r=c.length;++e<r;)a[(n=c[e]).i]=n.x(t);return a.join("")}}}function dn(t){return((t=Math.exp(t))+1/t)/2}function vn(t,n){var e,r,i=t[0],o=t[1],u=t[2],a=n[0],c=n[1],s=n[2],f=a-i,l=c-o,h=f*f+l*l;if(h<nl)r=Math.log(s/u)/Jf,e=function(t){return[i+t*f,o+t*l,u*Math.exp(Jf*t*r)]};else{var p=Math.sqrt(h),d=(s*s-u*u+tl*h)/(2*u*Kf*p),v=(s*s-u*u-tl*h)/(2*s*Kf*p),g=Math.log(Math.sqrt(d*d+1)-d),_=Math.log(Math.sqrt(v*v+1)-v);r=(_-g)/Jf,e=function(t){var n=t*r,e=dn(g),a=u/(Kf*p)*(e*function(t){return((t=Math.exp(2*t))-1)/(t+1)}(Jf*n+g)-function(t){return((t=Math.exp(t))-1/t)/2}(g));return[i+a*f,o+a*l,u*e/dn(Jf*n+g)]}}return e.duration=1e3*r,e}function gn(t){return function(n,e){var r=t((n=qt(n)).h,(e=qt(e)).h),i=en(n.s,e.s),o=en(n.l,e.l),u=en(n.opacity,e.opacity);return function(t){return n.h=r(t),n.s=i(t),n.l=o(t),n.opacity=u(t),n+""}}}function _n(t){return function(n,e){var r=t((n=Xt(n)).h,(e=Xt(e)).h),i=en(n.c,e.c),o=en(n.l,e.l),u=en(n.opacity,e.opacity);return function(t){return n.h=r(t),n.c=i(t),n.l=o(t),n.opacity=u(t),n+""}}}function yn(t){return function n(e){function r(n,r){var i=t((n=$t(n)).h,(r=$t(r)).h),o=en(n.s,r.s),u=en(n.l,r.l),a=en(n.opacity,r.opacity);return function(t){return n.h=i(t),n.s=o(t),n.l=u(Math.pow(t,e)),n.opacity=a(t),n+""}}return e=+e,r.gamma=n,r}(1)}function mn(){return pl||(gl(xn),pl=vl.now()+dl)}function xn(){pl=0}function bn(){this._call=this._time=this._next=null}function wn(t,n,e){var r=new bn;return r.restart(t,n,e),r}function Mn(){mn(),++cl;for(var t,n=Yf;n;)(t=pl-n._time)>=0&&n._call.call(null,t),n=n._next;--cl}function Tn(){pl=(hl=vl.now())+dl,cl=sl=0;try{Mn()}finally{cl=0,function(){var t,n,e=Yf,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Yf=n);Bf=t,kn(r)}(),pl=0}}function Nn(){var t=vl.now(),n=t-hl;n>ll&&(dl-=n,hl=t)}function kn(t){if(!cl){sl&&(sl=clearTimeout(sl));t-pl>24?(t<1/0&&(sl=setTimeout(Tn,t-vl.now()-dl)),fl&&(fl=clearInterval(fl))):(fl||(hl=vl.now(),fl=setInterval(Nn,ll)),cl=1,gl(Tn))}}function Sn(t,n,e){var r=new bn;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}function En(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};(function(t,n,e){function r(c){var s,f,l,h;if(e.state!==xl)return o();for(s in a)if((h=a[s]).name===e.name){if(h.state===wl)return Sn(r);h.state===Ml?(h.state=Nl,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete a[s]):+s<n&&(h.state=Nl,h.timer.stop(),delete a[s])}if(Sn(function(){e.state===wl&&(e.state=Ml,e.timer.restart(i,e.delay,e.time),i(c))}),e.state=bl,e.on.call("start",t,t.__data__,e.index,e.group),e.state===bl){for(e.state=wl,u=new Array(l=e.tween.length),s=0,f=-1;s<l;++s)(h=e.tween[s].value.call(t,t.__data__,e.index,e.group))&&(u[++f]=h);u.length=f+1}}function i(n){for(var r=n<e.duration?e.ease.call(null,n/e.duration):(e.timer.restart(o),e.state=Tl,1),i=-1,a=u.length;++i<a;)u[i].call(null,r);e.state===Tl&&(e.on.call("end",t,t.__data__,e.index,e.group),o())}function o(){e.state=Nl,e.timer.stop(),delete a[n];for(var r in a)return;delete t.__transition}var u,a=t.__transition;a[n]=e,e.timer=wn(function(t){e.state=xl,e.timer.restart(r,e.delay,e.time),e.delay<=t&&r(t-e.delay)},0,e.time)})(t,e,{name:n,index:r,group:i,on:_l,tween:yl,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:ml})}function An(t,n){var e=zn(t,n);if(e.state>ml)throw new Error("too late; already scheduled");return e}function Cn(t,n){var e=zn(t,n);if(e.state>bl)throw new Error("too late; already started");return e}function zn(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Pn(t,n){var e,r,i,o=t.__transition,u=!0;if(o){n=null==n?null:n+"";for(i in o)(e=o[i]).name===n?(r=e.state>bl&&e.state<Tl,e.state=Nl,e.timer.stop(),r&&e.on.call("interrupt",t,t.__data__,e.index,e.group),delete o[i]):u=!1;u&&delete t.__transition}}function Rn(t,n,e){var r=t._id;return t.each(function(){var t=Cn(this,r);(t.value||(t.value={}))[n]=e.apply(this,arguments)}),function(t){return zn(t,r).value[n]}}function Ln(t,n){var e;return("number"==typeof n?an:n instanceof Et?Hf:(e=Et(n))?(n=e,Hf):sn)(t,n)}function qn(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Dn(t){return at().transition(t)}function Un(){return++Sl}function On(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}function Fn(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}function In(t){return(1-Math.cos(Pl*t))/2}function Yn(t){return((t*=2)<=1?Math.pow(2,10*t-10):2-Math.pow(2,10-10*t))/2}function Bn(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}function Hn(t){return(t=+t)<Ll?Hl*t*t:t<Dl?Hl*(t-=ql)*t+Ul:t<Fl?Hl*(t-=Ol)*t+Il:Hl*(t-=Yl)*t+Bl}function jn(t,n){for(var e;!(e=t.__transition)||!(e=e[n]);)if(!(t=t.parentNode))return Ql.time=mn(),Ql;return e}function Xn(t){return function(){return t}}function Vn(){t.event.stopImmediatePropagation()}function $n(){t.event.preventDefault(),t.event.stopImmediatePropagation()}function Wn(t){return{type:t}}function Zn(){return!t.event.button}function Gn(){var t=this.ownerSVGElement||this;return[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function Qn(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Jn(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Kn(n){function e(t){var e=t.property("__brush",a).selectAll(".overlay").data([Wn("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",uh.overlay).merge(e).each(function(){var t=Qn(this).extent;ct(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])}),t.selectAll(".selection").data([Wn("selection")]).enter().append("rect").attr("class","selection").attr("cursor",uh.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var i=t.selectAll(".handle").data(n.handles,function(t){return t.type});i.exit().remove(),i.enter().append("rect").attr("class",function(t){return"handle handle--"+t.type}).attr("cursor",function(t){return uh[t.type]}),t.each(r).attr("fill","none").attr("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush touchstart.brush",u)}function r(){var t=ct(this),n=Qn(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",function(t){return"e"===t.type[t.type.length-1]?n[1][0]-h/2:n[0][0]-h/2}).attr("y",function(t){return"s"===t.type[0]?n[1][1]-h/2:n[0][1]-h/2}).attr("width",function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+h:h}).attr("height",function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+h:h})):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function i(t,n){return t.__brush.emitter||new o(t,n)}function o(t,n){this.that=t,this.args=n,this.state=t.__brush,this.active=0}function u(){function e(){var t=pt(w);!L||x||b||(Math.abs(t[0]-D[0])>Math.abs(t[1]-D[1])?b=!0:x=!0),D=t,m=!0,$n(),o()}function o(){var t;switch(_=D[0]-q[0],y=D[1]-q[1],T){case th:case Kl:N&&(_=Math.max(C-a,Math.min(P-p,_)),s=a+_,d=p+_),k&&(y=Math.max(z-l,Math.min(R-v,y)),h=l+y,g=v+y);break;case nh:N<0?(_=Math.max(C-a,Math.min(P-a,_)),s=a+_,d=p):N>0&&(_=Math.max(C-p,Math.min(P-p,_)),s=a,d=p+_),k<0?(y=Math.max(z-l,Math.min(R-l,y)),h=l+y,g=v):k>0&&(y=Math.max(z-v,Math.min(R-v,y)),h=l,g=v+y);break;case eh:N&&(s=Math.max(C,Math.min(P,a-_*N)),d=Math.max(C,Math.min(P,p+_*N))),k&&(h=Math.max(z,Math.min(R,l-y*k)),g=Math.max(z,Math.min(R,v+y*k)))}d<s&&(N*=-1,t=a,a=p,p=t,t=s,s=d,d=t,M in ah&&F.attr("cursor",uh[M=ah[M]])),g<h&&(k*=-1,t=l,l=v,v=t,t=h,h=g,g=t,M in ch&&F.attr("cursor",uh[M=ch[M]])),S.selection&&(A=S.selection),x&&(s=A[0][0],d=A[1][0]),b&&(h=A[0][1],g=A[1][1]),A[0][0]===s&&A[0][1]===h&&A[1][0]===d&&A[1][1]===g||(S.selection=[[s,h],[d,g]],r.call(w),U.brush())}function u(){if(Vn(),t.event.touches){if(t.event.touches.length)return;c&&clearTimeout(c),c=setTimeout(function(){c=null},500),O.on("touchmove.brush touchend.brush touchcancel.brush",null)}else yt(t.event.view,m),I.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);O.attr("pointer-events","all"),F.attr("cursor",uh.overlay),S.selection&&(A=S.selection),Jn(A)&&(S.selection=null,r.call(w)),U.end()}if(t.event.touches){if(t.event.changedTouches.length<t.event.touches.length)return $n()}else if(c)return;if(f.apply(this,arguments)){var a,s,l,h,p,d,v,g,_,y,m,x,b,w=this,M=t.event.target.__data__.type,T="selection"===(t.event.metaKey?M="overlay":M)?Kl:t.event.altKey?eh:nh,N=n===ih?null:sh[M],k=n===rh?null:fh[M],S=Qn(w),E=S.extent,A=S.selection,C=E[0][0],z=E[0][1],P=E[1][0],R=E[1][1],L=N&&k&&t.event.shiftKey,q=pt(w),D=q,U=i(w,arguments).beforestart();"overlay"===M?S.selection=A=[[a=n===ih?C:q[0],l=n===rh?z:q[1]],[p=n===ih?P:a,v=n===rh?R:l]]:(a=A[0][0],l=A[0][1],p=A[1][0],v=A[1][1]),s=a,h=l,d=p,g=v;var O=ct(w).attr("pointer-events","none"),F=O.selectAll(".overlay").attr("cursor",uh[M]);if(t.event.touches)O.on("touchmove.brush",e,!0).on("touchend.brush touchcancel.brush",u,!0);else{var I=ct(t.event.view).on("keydown.brush",function(){switch(t.event.keyCode){case 16:L=N&&k;break;case 18:T===nh&&(N&&(p=d-_*N,a=s+_*N),k&&(v=g-y*k,l=h+y*k),T=eh,o());break;case 32:T!==nh&&T!==eh||(N<0?p=d-_:N>0&&(a=s-_),k<0?v=g-y:k>0&&(l=h-y),T=th,F.attr("cursor",uh.selection),o());break;default:return}$n()},!0).on("keyup.brush",function(){switch(t.event.keyCode){case 16:L&&(x=b=L=!1,o());break;case 18:T===eh&&(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=nh,o());break;case 32:T===th&&(t.event.altKey?(N&&(p=d-_*N,a=s+_*N),k&&(v=g-y*k,l=h+y*k),T=eh):(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=nh),F.attr("cursor",uh[M]),o());break;default:return}$n()},!0).on("mousemove.brush",e,!0).on("mouseup.brush",u,!0);_t(t.event.view)}Vn(),Pn(w),r.call(w),U.start()}}function a(){var t=this.__brush||{selection:null};return t.extent=s.apply(this,arguments),t.dim=n,t}var c,s=Gn,f=Zn,l=N(e,"start","brush","end"),h=6;return e.move=function(t,e){t.selection?t.on("start.brush",function(){i(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){i(this,arguments).end()}).tween("brush",function(){function t(t){u.selection=1===t&&Jn(s)?null:f(t),r.call(o),a.brush()}var o=this,u=o.__brush,a=i(o,arguments),c=u.selection,s=n.input("function"==typeof e?e.apply(this,arguments):e,u.extent),f=fn(c,s);return c&&s?t:t(1)}):t.each(function(){var t=arguments,o=this.__brush,u=n.input("function"==typeof e?e.apply(this,t):e,o.extent),a=i(this,t).beforestart();Pn(this),o.selection=null==u||Jn(u)?null:u,r.call(this),a.start().brush().end()})},o.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(t){it(new function(t,n,e){this.target=t,this.type=n,this.selection=e}(e,t,n.output(this.state.selection)),l.apply,l,[t,this.that,this.args])}},e.extent=function(t){return arguments.length?(s="function"==typeof t?t:Xn([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),e):s},e.filter=function(t){return arguments.length?(f="function"==typeof t?t:Xn(!!t),e):f},e.handleSize=function(t){return arguments.length?(h=+t,e):h},e.on=function(){var t=l.on.apply(l,arguments);return t===l?e:t},e}function te(t){return function(){return t}}function ne(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function ee(){return new ne}function re(t){return t.source}function ie(t){return t.target}function oe(t){return t.radius}function ue(t){return t.startAngle}function ae(t){return t.endAngle}function ce(){}function se(t,n){var e=new ce;if(t instanceof ce)t.each(function(t,n){e.set(n,t)});else if(Array.isArray(t)){var r,i=-1,o=t.length;if(null==n)for(;++i<o;)e.set(i,t[i]);else for(;++i<o;)e.set(n(r=t[i],i,t),r)}else if(t)for(var u in t)e.set(u,t[u]);return e}function fe(){return{}}function le(t,n,e){t[n]=e}function he(){return se()}function pe(t,n,e){t.set(n,e)}function de(){}function ve(t,n){var e=new de;if(t instanceof de)t.each(function(t){e.add(t)});else if(t){var r=-1,i=t.length;if(null==n)for(;++r<i;)e.add(t[r]);else for(;++r<i;)e.add(n(t[r],r,t))}return e}function ge(t){return new Function("d","return {"+t.map(function(t,n){return JSON.stringify(t)+": d["+n+"]"}).join(",")+"}")}function _e(t){function n(t,n){function e(){if(s)return Mh;if(f)return f=!1,wh;var n,e,r=a;if(t.charCodeAt(r)===Th){for(;a++<u&&t.charCodeAt(a)!==Th||t.charCodeAt(++a)===Th;);return(n=a)>=u?s=!0:(e=t.charCodeAt(a++))===Nh?f=!0:e===kh&&(f=!0,t.charCodeAt(a)===Nh&&++a),t.slice(r+1,n-1).replace(/""/g,'"')}for(;a<u;){if((e=t.charCodeAt(n=a++))===Nh)f=!0;else if(e===kh)f=!0,t.charCodeAt(a)===Nh&&++a;else if(e!==o)continue;return t.slice(r,n)}return s=!0,t.slice(r,u)}var r,i=[],u=t.length,a=0,c=0,s=u<=0,f=!1;for(t.charCodeAt(u-1)===Nh&&--u,t.charCodeAt(u-1)===kh&&--u;(r=e())!==Mh;){for(var l=[];r!==wh&&r!==Mh;)l.push(r),r=e();n&&null==(l=n(l,c++))||i.push(l)}return i}function e(n){return n.map(r).join(t)}function r(t){return null==t?"":i.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}var i=new RegExp('["'+t+"\n\r]"),o=t.charCodeAt(0);return{parse:function(t,e){var r,i,o=n(t,function(t,n){if(r)return r(t,n-1);i=t,r=e?function(t,n){var e=ge(t);return function(r,i){return n(e(r),i,t)}}(t,e):ge(t)});return o.columns=i||[],o},parseRows:n,format:function(n,e){return null==e&&(e=function(t){var n=Object.create(null),e=[];return t.forEach(function(t){for(var r in t)r in n||e.push(n[r]=r)}),e}(n)),[e.map(r).join(t)].concat(n.map(function(n){return e.map(function(t){return r(n[t])}).join(t)})).join("\n")},formatRows:function(t){return t.map(e).join("\n")}}}function ye(t){return function(){return t}}function me(){return 1e-6*(Math.random()-.5)}function xe(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,u,a,c,s,f,l,h,p=t._root,d={data:r},v=t._x0,g=t._y0,_=t._x1,y=t._y1;if(!p)return t._root=d,t;for(;p.length;)if((s=n>=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u}while((l=f<<1|s)==(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function be(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function we(t){return t[0]}function Me(t){return t[1]}function Te(t,n,e){var r=new Ne(null==n?we:n,null==e?Me:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Ne(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function ke(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}function Se(t){return t.x+t.vx}function Ee(t){return t.y+t.vy}function Ae(t){return t.index}function Ce(t,n){var e=t.get(n);if(!e)throw new Error("missing: "+n);return e}function ze(t){return t.x}function Pe(t){return t.y}function Re(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Le(t){return(t=Re(Math.abs(t)))?t[1]:NaN}function qe(t,n){var e=Re(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}function De(t){return new Ue(t)}function Ue(t){if(!(n=Bh.exec(t)))throw new Error("invalid format: "+t);var n,e=n[1]||" ",r=n[2]||">",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):Yh[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function Oe(t){return t}function Fe(t){function n(t){function n(t){var n,r,u,f=g,x=_;if("c"===v)x=y(t)+x,t="";else{var b=(t=+t)<0;if(t=y(Math.abs(t),d),b&&0==+t&&(b=!1),f=(b?"("===s?s:"-":"-"===s||"("===s?"":s)+f,x=("s"===v?jh[8+Oh/3]:"")+x+(b&&"("===s?")":""),m)for(n=-1,r=t.length;++n<r;)if(48>(u=t.charCodeAt(n))||u>57){x=(46===u?i+t.slice(n+1):t.slice(n))+x,t=t.slice(0,n);break}}p&&!l&&(t=e(t,1/0));var w=f.length+t.length+x.length,M=w<h?new Array(h-w+1).join(a):"";switch(p&&l&&(t=e(M+t,M.length?h-x.length:1/0),M=""),c){case"<":t=f+t+x+M;break;case"=":t=f+M+t+x;break;case"^":t=M.slice(0,w=M.length>>1)+f+t+x+M.slice(w);break;default:t=M+f+t+x}return o(t)}var a=(t=De(t)).fill,c=t.align,s=t.sign,f=t.symbol,l=t.zero,h=t.width,p=t.comma,d=t.precision,v=t.type,g="$"===f?r[0]:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?r[1]:/[%p]/.test(v)?u:"",y=Yh[v],m=!v||/[defgprs%]/.test(v);return d=null==d?v?6:12:/[gprs]/.test(v)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),n.toString=function(){return t+""},n}var e=t.grouping&&t.thousands?function(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}}(t.grouping,t.thousands):Oe,r=t.currency,i=t.decimal,o=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):Oe,u=t.percent||"%";return{format:n,formatPrefix:function(t,e){var r=n((t=De(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Le(e)/3))),o=Math.pow(10,-i),u=jh[8+i/3];return function(t){return r(o*t)+u}}}}function Ie(n){return Hh=Fe(n),t.format=Hh.format,t.formatPrefix=Hh.formatPrefix,Hh}function Ye(t){return Math.max(0,-Le(Math.abs(t)))}function Be(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Le(n)/3)))-Le(Math.abs(t)))}function He(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Le(n)-Le(t))+1}function je(){return new Xe}function Xe(){this.reset()}function Ve(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}function $e(t){return t>1?0:t<-1?Np:Math.acos(t)}function We(t){return t>1?kp:t<-1?-kp:Math.asin(t)}function Ze(t){return(t=Fp(t/2))*t}function Ge(){}function Qe(t,n){t&&jp.hasOwnProperty(t.type)&&jp[t.type](t,n)}function Je(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i<o;)r=t[i],n.point(r[0],r[1],r[2]);n.lineEnd()}function Ke(t,n){var e=-1,r=t.length;for(n.polygonStart();++e<r;)Je(t[e],n,1);n.polygonEnd()}function tr(t,n){t&&Hp.hasOwnProperty(t.type)?Hp[t.type](t,n):Qe(t,n)}function nr(){$p.point=rr}function er(){ir(Xh,Vh)}function rr(t,n){$p.point=ir,Xh=t,Vh=n,$h=t*=Cp,Wh=Lp(n=(n*=Cp)/2+Sp),Zh=Fp(n)}function ir(t,n){n=(n*=Cp)/2+Sp;var e=(t*=Cp)-$h,r=e>=0?1:-1,i=r*e,o=Lp(n),u=Fp(n),a=Zh*u,c=Wh*o+a*Lp(i),s=a*r*Fp(i);Xp.add(Rp(s,c)),$h=t,Wh=o,Zh=u}function or(t){return[Rp(t[1],t[0]),We(t[2])]}function ur(t){var n=t[0],e=t[1],r=Lp(e);return[r*Lp(n),r*Fp(n),Fp(e)]}function ar(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function cr(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function sr(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function fr(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function lr(t){var n=Yp(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function hr(t,n){ip.push(op=[Gh=t,Jh=t]),n<Qh&&(Qh=n),n>Kh&&(Kh=n)}function pr(t,n){var e=ur([t*Cp,n*Cp]);if(rp){var r=cr(rp,e),i=cr([r[1],-r[0],0],r);lr(i),i=or(i);var o,u=t-tp,a=u>0?1:-1,c=i[0]*Ap*a,s=zp(u)>180;s^(a*tp<c&&c<a*t)?(o=i[1]*Ap)>Kh&&(Kh=o):(c=(c+360)%360-180,s^(a*tp<c&&c<a*t)?(o=-i[1]*Ap)<Qh&&(Qh=o):(n<Qh&&(Qh=n),n>Kh&&(Kh=n))),s?t<tp?mr(Gh,t)>mr(Gh,Jh)&&(Jh=t):mr(t,Jh)>mr(Gh,Jh)&&(Gh=t):Jh>=Gh?(t<Gh&&(Gh=t),t>Jh&&(Jh=t)):t>tp?mr(Gh,t)>mr(Gh,Jh)&&(Jh=t):mr(t,Jh)>mr(Gh,Jh)&&(Gh=t)}else ip.push(op=[Gh=t,Jh=t]);n<Qh&&(Qh=n),n>Kh&&(Kh=n),rp=e,tp=t}function dr(){Zp.point=pr}function vr(){op[0]=Gh,op[1]=Jh,Zp.point=hr,rp=null}function gr(t,n){if(rp){var e=t-tp;Wp.add(zp(e)>180?e+(e>0?360:-360):e)}else np=t,ep=n;$p.point(t,n),pr(t,n)}function _r(){$p.lineStart()}function yr(){gr(np,ep),$p.lineEnd(),zp(Wp)>Mp&&(Gh=-(Jh=180)),op[0]=Gh,op[1]=Jh,rp=null}function mr(t,n){return(n-=t)<0?n+360:n}function xr(t,n){return t[0]-n[0]}function br(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}function wr(t,n){t*=Cp;var e=Lp(n*=Cp);Mr(e*Lp(t),e*Fp(t),Fp(n))}function Mr(t,n,e){cp+=(t-cp)/++up,sp+=(n-sp)/up,fp+=(e-fp)/up}function Tr(){Gp.point=Nr}function Nr(t,n){t*=Cp;var e=Lp(n*=Cp);mp=e*Lp(t),xp=e*Fp(t),bp=Fp(n),Gp.point=kr,Mr(mp,xp,bp)}function kr(t,n){t*=Cp;var e=Lp(n*=Cp),r=e*Lp(t),i=e*Fp(t),o=Fp(n),u=Rp(Yp((u=xp*o-bp*i)*u+(u=bp*r-mp*o)*u+(u=mp*i-xp*r)*u),mp*r+xp*i+bp*o);ap+=u,lp+=u*(mp+(mp=r)),hp+=u*(xp+(xp=i)),pp+=u*(bp+(bp=o)),Mr(mp,xp,bp)}function Sr(){Gp.point=wr}function Er(){Gp.point=Cr}function Ar(){zr(_p,yp),Gp.point=wr}function Cr(t,n){_p=t,yp=n,t*=Cp,n*=Cp,Gp.point=zr;var e=Lp(n);mp=e*Lp(t),xp=e*Fp(t),bp=Fp(n),Mr(mp,xp,bp)}function zr(t,n){t*=Cp;var e=Lp(n*=Cp),r=e*Lp(t),i=e*Fp(t),o=Fp(n),u=xp*o-bp*i,a=bp*r-mp*o,c=mp*i-xp*r,s=Yp(u*u+a*a+c*c),f=We(s),l=s&&-f/s;dp+=l*u,vp+=l*a,gp+=l*c,ap+=f,lp+=f*(mp+(mp=r)),hp+=f*(xp+(xp=i)),pp+=f*(bp+(bp=o)),Mr(mp,xp,bp)}function Pr(t){return function(){return t}}function Rr(t,n){function e(e,r){return e=t(e,r),n(e[0],e[1])}return t.invert&&n.invert&&(e.invert=function(e,r){return(e=n.invert(e,r))&&t.invert(e[0],e[1])}),e}function Lr(t,n){return[t>Np?t-Ep:t<-Np?t+Ep:t,n]}function qr(t,n,e){return(t%=Ep)?n||e?Rr(Ur(t),Or(n,e)):Ur(t):n||e?Or(n,e):Lr}function Dr(t){return function(n,e){return n+=t,[n>Np?n-Ep:n<-Np?n+Ep:n,e]}}function Ur(t){var n=Dr(t);return n.invert=Dr(-t),n}function Or(t,n){function e(t,n){var e=Lp(n),a=Lp(t)*e,c=Fp(t)*e,s=Fp(n),f=s*r+a*i;return[Rp(c*o-f*u,a*r-s*i),We(f*o+c*u)]}var r=Lp(t),i=Fp(t),o=Lp(n),u=Fp(n);return e.invert=function(t,n){var e=Lp(n),a=Lp(t)*e,c=Fp(t)*e,s=Fp(n),f=s*o-c*u;return[Rp(c*o+s*u,a*r+f*i),We(f*r-a*i)]},e}function Fr(t){function n(n){return n=t(n[0]*Cp,n[1]*Cp),n[0]*=Ap,n[1]*=Ap,n}return t=qr(t[0]*Cp,t[1]*Cp,t.length>2?t[2]*Cp:0),n.invert=function(n){return n=t.invert(n[0]*Cp,n[1]*Cp),n[0]*=Ap,n[1]*=Ap,n},n}function Ir(t,n,e,r,i,o){if(e){var u=Lp(n),a=Fp(n),c=r*e;null==i?(i=n+r*Ep,o=n-c/2):(i=Yr(u,i),o=Yr(u,o),(r>0?i<o:i>o)&&(i+=r*Ep));for(var s,f=i;r>0?f>o:f<o;f-=c)s=or([u,-a*Lp(f),-a*Fp(f)]),t.point(s[0],s[1])}}function Yr(t,n){(n=ur(n))[0]-=t,lr(n);var e=$e(-n[1]);return((-n[2]<0?-e:e)+Ep-Mp)%Ep}function Br(){var t,n=[];return{point:function(n,e){t.push([n,e])},lineStart:function(){n.push(t=[])},lineEnd:Ge,rejoin:function(){n.length>1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function Hr(t,n){return zp(t[0]-n[0])<Mp&&zp(t[1]-n[1])<Mp}function jr(t,n,e,r){this.x=t,this.z=n,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Xr(t,n,e,r,i){var o,u,a=[],c=[];if(t.forEach(function(t){if(!((n=t.length-1)<=0)){var n,e,r=t[0],u=t[n];if(Hr(r,u)){for(i.lineStart(),o=0;o<n;++o)i.point((r=t[o])[0],r[1]);i.lineEnd()}else a.push(e=new jr(r,t,null,!0)),c.push(e.o=new jr(r,null,e,!1)),a.push(e=new jr(u,t,null,!1)),c.push(e.o=new jr(u,null,e,!0))}}),a.length){for(c.sort(n),Vr(a),Vr(c),o=0,u=c.length;o<u;++o)c[o].e=e=!e;for(var s,f,l=a[0];;){for(var h=l,p=!0;h.v;)if((h=h.n)===l)return;s=h.z,i.lineStart();do{if(h.v=h.o.v=!0,h.e){if(p)for(o=0,u=s.length;o<u;++o)i.point((f=s[o])[0],f[1]);else r(h.x,h.n.x,1,i);h=h.n}else{if(p)for(s=h.p.z,o=s.length-1;o>=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}s=(h=h.o).z,p=!p}while(!h.v);i.lineEnd()}}}function Vr(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r<n;)i.n=e=t[r],e.p=i,i=e;i.n=e=t[0],e.p=i}}function $r(t,n){var e=n[0],r=n[1],i=[Fp(e),-Lp(e),0],o=0,u=0;cd.reset();for(var a=0,c=t.length;a<c;++a)if(f=(s=t[a]).length)for(var s,f,l=s[f-1],h=l[0],p=l[1]/2+Sp,d=Fp(p),v=Lp(p),g=0;g<f;++g,h=y,d=x,v=b,l=_){var _=s[g],y=_[0],m=_[1]/2+Sp,x=Fp(m),b=Lp(m),w=y-h,M=w>=0?1:-1,T=M*w,N=T>Np,k=d*x;if(cd.add(Rp(k*M*Fp(T),v*b+k*Lp(T))),o+=N?w+M*Ep:w,N^h>=e^y>=e){var S=cr(ur(l),ur(_));lr(S);var E=cr(i,S);lr(E);var A=(N^w>=0?-1:1)*We(E[2]);(r>A||r===A&&(S[0]||S[1]))&&(u+=N^w>=0?1:-1)}}return(o<-Mp||o<Mp&&cd<-Mp)^1&u}function Wr(t,n,e,r){return function(i){function o(n,e){t(n,e)&&i.point(n,e)}function u(t,n){v.point(t,n)}function a(){x.point=u,v.lineStart()}function c(){x.point=o,v.lineEnd()}function s(t,n){d.push([t,n]),y.point(t,n)}function f(){y.lineStart(),d=[]}function l(){s(d[0][0],d[0][1]),y.lineEnd();var t,n,e,r,o=y.clean(),u=_.result(),a=u.length;if(d.pop(),h.push(d),d=null,a)if(1&o){if(e=u[0],(n=e.length-1)>0){for(m||(i.polygonStart(),m=!0),i.lineStart(),t=0;t<n;++t)i.point((r=e[t])[0],r[1]);i.lineEnd()}}else a>1&&2&o&&u.push(u.pop().concat(u.shift())),p.push(u.filter(Zr))}var h,p,d,v=n(i),_=Br(),y=n(_),m=!1,x={point:o,lineStart:a,lineEnd:c,polygonStart:function(){x.point=s,x.lineStart=f,x.lineEnd=l,p=[],h=[]},polygonEnd:function(){x.point=o,x.lineStart=a,x.lineEnd=c,p=g(p);var t=$r(h,r);p.length?(m||(i.polygonStart(),m=!0),Xr(p,Gr,t,e,i)):t&&(m||(i.polygonStart(),m=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),m&&(i.polygonEnd(),m=!1),p=h=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}};return x}}function Zr(t){return t.length>1}function Gr(t,n){return((t=t.x)[0]<0?t[1]-kp-Mp:kp-t[1])-((n=n.x)[0]<0?n[1]-kp-Mp:kp-n[1])}function Qr(t){function n(t,n){return Lp(t)*Lp(n)>i}function e(t,n,e){var r=[1,0,0],o=cr(ur(t),ur(n)),u=ar(o,o),a=o[0],c=u-a*a;if(!c)return!e&&t;var s=i*u/c,f=-i*a/c,l=cr(r,o),h=fr(r,s);sr(h,fr(o,f));var p=l,d=ar(h,p),v=ar(p,p),g=d*d-v*(ar(h,h)-1);if(!(g<0)){var _=Yp(g),y=fr(p,(-d-_)/v);if(sr(y,h),y=or(y),!e)return y;var m,x=t[0],b=n[0],w=t[1],M=n[1];b<x&&(m=x,x=b,b=m);var T=b-x,N=zp(T-Np)<Mp;if(!N&&M<w&&(m=w,w=M,M=m),N||T<Mp?N?w+M>0^y[1]<(zp(y[0]-x)<Mp?w:M):w<=y[1]&&y[1]<=M:T>Np^(x<=y[0]&&y[0]<=b)){var k=fr(p,(-d+_)/v);return sr(k,h),[y,or(k)]}}}function r(n,e){var r=u?t:Np-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var i=Lp(t),o=6*Cp,u=i>0,a=zp(i)>Mp;return Wr(n,function(t){var i,o,c,s,f;return{lineStart:function(){s=c=!1,f=1},point:function(l,h){var p,d=[l,h],v=n(l,h),g=u?v?0:r(l,h):v?r(l+(l<0?Np:-Np),h):0;if(!i&&(s=c=v)&&t.lineStart(),v!==c&&(!(p=e(i,d))||Hr(i,p)||Hr(d,p))&&(d[0]+=Mp,d[1]+=Mp,v=n(d[0],d[1])),v!==c)f=0,v?(t.lineStart(),p=e(d,i),t.point(p[0],p[1])):(p=e(i,d),t.point(p[0],p[1]),t.lineEnd()),i=p;else if(a&&i&&u^v){var _;g&o||!(_=e(d,i,!0))||(f=0,u?(t.lineStart(),t.point(_[0][0],_[0][1]),t.point(_[1][0],_[1][1]),t.lineEnd()):(t.point(_[1][0],_[1][1]),t.lineEnd(),t.lineStart(),t.point(_[0][0],_[0][1])))}!v||i&&Hr(i,d)||t.point(d[0],d[1]),i=d,c=v,o=g},lineEnd:function(){c&&t.lineEnd(),i=null},clean:function(){return f|(s&&c)<<1}}},function(n,e,r,i){Ir(i,t,o,r,n,e)},u?[0,-t]:[-Np,t-Np])}function Jr(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,a,s){var f=0,l=0;if(null==i||(f=u(i,a))!==(l=u(o,a))||c(i,o)<0^a>0)do{s.point(0===f||3===f?t:e,f>1?r:n)}while((f=(f+a+4)%4)!==l);else s.point(o[0],o[1])}function u(r,i){return zp(r[0]-t)<Mp?i>0?0:3:zp(r[0]-e)<Mp?i>0?2:1:zp(r[1]-n)<Mp?i>0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&w.point(t,n)}function s(o,u){var a=i(o,u);if(l&&h.push([o,u]),x)p=o,d=u,v=a,x=!1,a&&(w.lineStart(),w.point(o,u));else if(a&&m)w.point(o,u);else{var c=[_=Math.max(ld,Math.min(fd,_)),y=Math.max(ld,Math.min(fd,y))],s=[o=Math.max(ld,Math.min(fd,o)),u=Math.max(ld,Math.min(fd,u))];!function(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=0,f=1,l=n[0]-a,h=n[1]-c;if(u=e-a,l||!(u>0)){if(u/=l,l<0){if(u<s)return;u<f&&(f=u)}else if(l>0){if(u>f)return;u>s&&(s=u)}if(u=i-a,l||!(u<0)){if(u/=l,l<0){if(u>f)return;u>s&&(s=u)}else if(l>0){if(u<s)return;u<f&&(f=u)}if(u=r-c,h||!(u>0)){if(u/=h,h<0){if(u<s)return;u<f&&(f=u)}else if(h>0){if(u>f)return;u>s&&(s=u)}if(u=o-c,h||!(u<0)){if(u/=h,h<0){if(u>f)return;u>s&&(s=u)}else if(h>0){if(u<s)return;u<f&&(f=u)}return s>0&&(t[0]=a+s*l,t[1]=c+s*h),f<1&&(n[0]=a+f*l,n[1]=c+f*h),!0}}}}}(c,s,t,n,e,r)?a&&(w.lineStart(),w.point(o,u),b=!1):(m||(w.lineStart(),w.point(c[0],c[1])),w.point(s[0],s[1]),a||w.lineEnd(),b=!1)}_=o,y=u,m=a}var f,l,h,p,d,v,_,y,m,x,b,w=u,M=Br(),T={point:c,lineStart:function(){T.point=s,l&&l.push(h=[]),x=!0,m=!1,_=y=NaN},lineEnd:function(){f&&(s(p,d),v&&m&&M.rejoin(),f.push(M.result())),T.point=c,m&&w.lineEnd()},polygonStart:function(){w=M,f=[],l=[],b=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=l.length;e<i;++e)for(var o,u,a=l[e],c=1,s=a.length,f=a[0],h=f[0],p=f[1];c<s;++c)o=h,u=p,h=(f=a[c])[0],p=f[1],u<=r?p>r&&(h-o)*(r-u)>(p-u)*(t-o)&&++n:p<=r&&(h-o)*(r-u)<(p-u)*(t-o)&&--n;return n}(),e=b&&n,i=(f=g(f)).length;(e||i)&&(u.polygonStart(),e&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),i&&Xr(f,a,n,o,u),u.polygonEnd()),w=u,f=l=h=null}};return T}}function Kr(){pd.point=pd.lineEnd=Ge}function ti(t,n){Qp=t*=Cp,Jp=Fp(n*=Cp),Kp=Lp(n),pd.point=ni}function ni(t,n){t*=Cp;var e=Fp(n*=Cp),r=Lp(n),i=zp(t-Qp),o=Lp(i),u=r*Fp(i),a=Kp*e-Jp*r*o,c=Jp*e+Kp*r*o;hd.add(Rp(Yp(u*u+a*a),c)),Qp=t,Jp=e,Kp=r}function ei(t){return hd.reset(),tr(t,pd),+hd}function ri(t,n){return dd[0]=t,dd[1]=n,ei(vd)}function ii(t,n){return!(!t||!_d.hasOwnProperty(t.type))&&_d[t.type](t,n)}function oi(t,n){return 0===ri(t,n)}function ui(t,n){var e=ri(t[0],t[1]);return ri(t[0],n)+ri(n,t[1])<=e+Mp}function ai(t,n){return!!$r(t.map(ci),si(n))}function ci(t){return(t=t.map(si)).pop(),t}function si(t){return[t[0]*Cp,t[1]*Cp]}function fi(t,n,e){var r=f(t,n-Mp,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function li(t,n,e){var r=f(t,n-Mp,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function hi(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return f(qp(o/_)*_,i,_).map(p).concat(f(qp(s/y)*y,c,y).map(d)).concat(f(qp(r/v)*v,e,v).filter(function(t){return zp(t%_)>Mp}).map(l)).concat(f(qp(a/g)*g,u,g).filter(function(t){return zp(t%y)>Mp}).map(h))}var e,r,i,o,u,a,c,s,l,h,p,d,v=10,g=v,_=90,y=360,m=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[p(o).concat(d(c).slice(1),p(i).reverse().slice(1),d(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(m)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(m)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(_=+n[0],y=+n[1],t):[_,y]},t.stepMinor=function(n){return arguments.length?(v=+n[0],g=+n[1],t):[v,g]},t.precision=function(n){return arguments.length?(m=+n,l=fi(a,u,90),h=li(r,e,m),p=fi(s,c,90),d=li(o,i,m),t):m},t.extentMajor([[-180,-90+Mp],[180,90-Mp]]).extentMinor([[-180,-80-Mp],[180,80+Mp]])}function pi(t){return t}function di(){xd.point=vi}function vi(t,n){xd.point=gi,td=ed=t,nd=rd=n}function gi(t,n){md.add(rd*t-ed*n),ed=t,rd=n}function _i(){gi(td,nd)}function yi(t,n){kd+=t,Sd+=n,++Ed}function mi(){qd.point=xi}function xi(t,n){qd.point=bi,yi(ud=t,ad=n)}function bi(t,n){var e=t-ud,r=n-ad,i=Yp(e*e+r*r);Ad+=i*(ud+t)/2,Cd+=i*(ad+n)/2,zd+=i,yi(ud=t,ad=n)}function wi(){qd.point=yi}function Mi(){qd.point=Ni}function Ti(){ki(id,od)}function Ni(t,n){qd.point=ki,yi(id=ud=t,od=ad=n)}function ki(t,n){var e=t-ud,r=n-ad,i=Yp(e*e+r*r);Ad+=i*(ud+t)/2,Cd+=i*(ad+n)/2,zd+=i,Pd+=(i=ad*t-ud*n)*(ud+t),Rd+=i*(ad+n),Ld+=3*i,yi(ud=t,ad=n)}function Si(t){this._context=t}function Ei(t,n){Bd.point=Ai,Ud=Fd=t,Od=Id=n}function Ai(t,n){Fd-=t,Id-=n,Yd.add(Yp(Fd*Fd+Id*Id)),Fd=t,Id=n}function Ci(){this._string=[]}function zi(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function Pi(t){return function(n){var e=new Ri;for(var r in t)e[r]=t[r];return e.stream=n,e}}function Ri(){}function Li(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),tr(e,t.stream(Nd)),n(Nd.result()),null!=r&&t.clipExtent(r),t}function qi(t,n,e){return Li(t,function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),u=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,a=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([u,a])},e)}function Di(t,n,e){return qi(t,[[0,0],n],e)}function Ui(t,n,e){return Li(t,function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,u=-i*e[0][1];t.scale(150*i).translate([o,u])},e)}function Oi(t,n,e){return Li(t,function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],u=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,u])},e)}function Fi(t,n){return+n?function(t,n){function e(r,i,o,u,a,c,s,f,l,h,p,d,v,g){var _=s-r,y=f-i,m=_*_+y*y;if(m>4*n&&v--){var x=u+h,b=a+p,w=c+d,M=Yp(x*x+b*b+w*w),T=We(w/=M),N=zp(zp(w)-1)<Mp||zp(o-l)<Mp?(o+l)/2:Rp(b,x),k=t(N,T),S=k[0],E=k[1],A=S-r,C=E-i,z=y*A-_*C;(z*z/m>n||zp((_*A+y*C)/m-.5)>.3||u*h+a*p+c*d<jd)&&(e(r,i,o,u,a,c,S,E,N,x/=M,b/=M,w,v,g),g.point(S,E),e(S,E,N,x,b,w,s,f,l,h,p,d,v,g))}}return function(n){function r(e,r){e=t(e,r),n.point(e[0],e[1])}function i(){_=NaN,w.point=o,n.lineStart()}function o(r,i){var o=ur([r,i]),u=t(r,i);e(_,y,g,m,x,b,_=u[0],y=u[1],g=r,m=o[0],x=o[1],b=o[2],Hd,n),n.point(_,y)}function u(){w.point=r,n.lineEnd()}function a(){i(),w.point=c,w.lineEnd=s}function c(t,n){o(f=t,n),l=_,h=y,p=m,d=x,v=b,w.point=o}function s(){e(_,y,g,m,x,b,l,h,f,p,d,v,Hd,n),w.lineEnd=u,u()}var f,l,h,p,d,v,g,_,y,m,x,b,w={point:r,lineStart:i,lineEnd:u,polygonStart:function(){n.polygonStart(),w.lineStart=a},polygonEnd:function(){n.polygonEnd(),w.lineStart=i}};return w}}(t,n):function(t){return Pi({point:function(n,e){n=t(n,e),this.stream.point(n[0],n[1])}})}(t)}function Ii(t){return Yi(function(){return t})()}function Yi(t){function n(t){return t=s(t[0]*Cp,t[1]*Cp),[t[0]*v+u,a-t[1]*v]}function e(t,n){return t=o(t,n),[t[0]*v+u,a-t[1]*v]}function r(){s=Rr(c=qr(x,b,w),o);var t=o(y,m);return u=g-t[0]*v,a=_+t[1]*v,i()}function i(){return p=d=null,n}var o,u,a,c,s,f,l,h,p,d,v=150,g=480,_=250,y=0,m=0,x=0,b=0,w=0,M=null,T=sd,N=null,k=pi,S=.5,E=Fi(e,S);return n.stream=function(t){return p&&d===t?p:p=Xd(function(t){return Pi({point:function(n,e){var r=t(n,e);return this.stream.point(r[0],r[1])}})}(c)(T(E(k(d=t)))))},n.preclip=function(t){return arguments.length?(T=t,M=void 0,i()):T},n.postclip=function(t){return arguments.length?(k=t,N=f=l=h=null,i()):k},n.clipAngle=function(t){return arguments.length?(T=+t?Qr(M=t*Cp):(M=null,sd),i()):M*Ap},n.clipExtent=function(t){return arguments.length?(k=null==t?(N=f=l=h=null,pi):Jr(N=+t[0][0],f=+t[0][1],l=+t[1][0],h=+t[1][1]),i()):null==N?null:[[N,f],[l,h]]},n.scale=function(t){return arguments.length?(v=+t,r()):v},n.translate=function(t){return arguments.length?(g=+t[0],_=+t[1],r()):[g,_]},n.center=function(t){return arguments.length?(y=t[0]%360*Cp,m=t[1]%360*Cp,r()):[y*Ap,m*Ap]},n.rotate=function(t){return arguments.length?(x=t[0]%360*Cp,b=t[1]%360*Cp,w=t.length>2?t[2]%360*Cp:0,r()):[x*Ap,b*Ap,w*Ap]},n.precision=function(t){return arguments.length?(E=Fi(e,S=t*t),i()):Yp(S)},n.fitExtent=function(t,e){return qi(n,t,e)},n.fitSize=function(t,e){return Di(n,t,e)},n.fitWidth=function(t,e){return Ui(n,t,e)},n.fitHeight=function(t,e){return Oi(n,t,e)},function(){return o=t.apply(this,arguments),n.invert=o.invert&&function(t){return(t=s.invert((t[0]-u)/v,(a-t[1])/v))&&[t[0]*Ap,t[1]*Ap]},r()}}function Bi(t){var n=0,e=Np/3,r=Yi(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Cp,e=t[1]*Cp):[n*Ap,e*Ap]},i}function Hi(t,n){function e(t,n){var e=Yp(o-2*i*Fp(n))/i;return[e*Fp(t*=i),u-e*Lp(t)]}var r=Fp(t),i=(r+Fp(n))/2;if(zp(i)<Mp)return function(t){function n(t,n){return[t*e,Fp(n)/e]}var e=Lp(t);return n.invert=function(t,n){return[t/e,We(n*e)]},n}(t);var o=1+r*(2*i-r),u=Yp(o)/i;return e.invert=function(t,n){var e=u-n;return[Rp(t,zp(e))/i*Ip(e),We((o-(t*t+e*e)*i*i)/(2*i))]},e}function ji(){return Bi(Hi).scale(155.424).center([0,33.6442])}function Xi(){return ji().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])}function Vi(t){return function(n,e){var r=Lp(n),i=Lp(e),o=t(r*i);return[o*i*Fp(n),o*Fp(e)]}}function $i(t){return function(n,e){var r=Yp(n*n+e*e),i=t(r),o=Fp(i),u=Lp(i);return[Rp(n*o,r*u),We(r&&e*o/r)]}}function Wi(t,n){return[t,Up(Bp((kp+n)/2))]}function Zi(t){function n(){var n=Np*a(),u=o(Fr(o.rotate()).invert([0,0]));return s(null==f?[[u[0]-n,u[1]-n],[u[0]+n,u[1]+n]]:t===Wi?[[Math.max(u[0]-n,f),e],[Math.min(u[0]+n,r),i]]:[[f,Math.max(u[1]-n,e)],[r,Math.min(u[1]+n,i)]])}var e,r,i,o=Ii(t),u=o.center,a=o.scale,c=o.translate,s=o.clipExtent,f=null;return o.scale=function(t){return arguments.length?(a(t),n()):a()},o.translate=function(t){return arguments.length?(c(t),n()):c()},o.center=function(t){return arguments.length?(u(t),n()):u()},o.clipExtent=function(t){return arguments.length?(null==t?f=e=r=i=null:(f=+t[0][0],e=+t[0][1],r=+t[1][0],i=+t[1][1]),n()):null==f?null:[[f,e],[r,i]]},n()}function Gi(t){return Bp((kp+t)/2)}function Qi(t,n){function e(t,n){o>0?n<-kp+Mp&&(n=-kp+Mp):n>kp-Mp&&(n=kp-Mp);var e=o/Op(Gi(n),i);return[e*Fp(i*t),o-e*Lp(i*t)]}var r=Lp(t),i=t===n?Fp(t):Up(r/Lp(n))/Up(Gi(n)/Gi(t)),o=r*Op(Gi(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=Ip(i)*Yp(t*t+e*e);return[Rp(t,zp(e))/i*Ip(e),2*Pp(Op(o/r,1/i))-kp]},e):Wi}function Ji(t,n){return[t,n]}function Ki(t,n){function e(t,n){var e=o-n,r=i*t;return[e*Fp(r),o-e*Lp(r)]}var r=Lp(t),i=t===n?Fp(t):(r-Lp(n))/(n-t),o=r/i+t;return zp(i)<Mp?Ji:(e.invert=function(t,n){var e=o-n;return[Rp(t,zp(e))/i*Ip(e),o-Ip(i)*Yp(t*t+e*e)]},e)}function to(t,n){var e=Lp(n),r=Lp(t)*e;return[e*Fp(t)/r,Fp(n)/r]}function no(t,n,e,r){return 1===t&&1===n&&0===e&&0===r?pi:Pi({point:function(i,o){this.stream.point(i*t+e,o*n+r)}})}function eo(t,n){var e=n*n,r=e*e;return[t*(.8707-.131979*e+r*(r*(.003971*e-.001529*r)-.013791)),n*(1.007226+e*(.015085+r*(.028874*e-.044475-.005916*r)))]}function ro(t,n){return[Lp(n)*Fp(t),Fp(n)]}function io(t,n){var e=Lp(n),r=1+Lp(t)*e;return[e*Fp(t)/r,Fp(n)/r]}function oo(t,n){return[Up(Bp((kp+n)/2)),-t]}function uo(t,n){return t.parent===n.parent?1:2}function ao(t,n){return t+n.x}function co(t,n){return Math.max(t,n.y)}function so(t){var n=0,e=t.children,r=e&&e.length;if(r)for(;--r>=0;)n+=e[r].value;else n=1;t.value=n}function fo(t,n){var e,r,i,o,u,a=new vo(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=lo);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new vo(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(po)}function lo(t){return t.children}function ho(t){t.data=t.data.data}function po(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function vo(t){this.data=t,this.depth=this.height=0,this.parent=null}function go(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Wd.call(t))).length,o=[];r<i;)n=t[r],e&&yo(e,n)?++r:(e=function(t){switch(t.length){case 1:return function(t){return{x:t.x,y:t.y,r:t.r}}(t[0]);case 2:return xo(t[0],t[1]);case 3:return bo(t[0],t[1],t[2])}}(o=function(t,n){var e,r;if(mo(n,t))return[n];for(e=0;e<t.length;++e)if(_o(n,t[e])&&mo(xo(t[e],n),t))return[t[e],n];for(e=0;e<t.length-1;++e)for(r=e+1;r<t.length;++r)if(_o(xo(t[e],t[r]),n)&&_o(xo(t[e],n),t[r])&&_o(xo(t[r],n),t[e])&&mo(bo(t[e],t[r],n),t))return[t[e],t[r],n];throw new Error}(o,n)),r=0);return e}function _o(t,n){var e=t.r-n.r,r=n.x-t.x,i=n.y-t.y;return e<0||e*e<r*r+i*i}function yo(t,n){var e=t.r-n.r+1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function mo(t,n){for(var e=0;e<n.length;++e)if(!yo(t,n[e]))return!1;return!0}function xo(t,n){var e=t.x,r=t.y,i=t.r,o=n.x,u=n.y,a=n.r,c=o-e,s=u-r,f=a-i,l=Math.sqrt(c*c+s*s);return{x:(e+o+c/l*f)/2,y:(r+u+s/l*f)/2,r:(l+i+a)/2}}function bo(t,n,e){var r=t.x,i=t.y,o=t.r,u=n.x,a=n.y,c=n.r,s=e.x,f=e.y,l=e.r,h=r-u,p=r-s,d=i-a,v=i-f,g=c-o,_=l-o,y=r*r+i*i-o*o,m=y-u*u-a*a+c*c,x=y-s*s-f*f+l*l,b=p*d-h*v,w=(d*x-v*m)/(2*b)-r,M=(v*g-d*_)/b,T=(p*m-h*x)/(2*b)-i,N=(h*_-p*g)/b,k=M*M+N*N-1,S=2*(o+w*M+T*N),E=w*w+T*T-o*o,A=-(k?(S+Math.sqrt(S*S-4*k*E))/(2*k):E/S);return{x:r+w+M*A,y:i+T+N*A,r:A}}function wo(t,n,e){var r=t.x,i=t.y,o=n.r+e.r,u=t.r+e.r,a=n.x-r,c=n.y-i,s=a*a+c*c;if(s){var f=.5+((u*=u)-(o*=o))/(2*s),l=Math.sqrt(Math.max(0,2*o*(u+s)-(u-=s)*u-o*o))/(2*s);e.x=r+f*a+l*c,e.y=i+f*c-l*a}else e.x=r+u,e.y=i}function Mo(t,n){var e=n.x-t.x,r=n.y-t.y,i=t.r+n.r;return i*i-1e-6>e*e+r*r}function To(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function No(t){this._=t,this.next=null,this.previous=null}function ko(t){if(!(i=t.length))return 0;var n,e,r,i,o,u,a,c,s,f,l;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;wo(e,n,r=t[2]),n=new No(n),e=new No(e),r=new No(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;a<i;++a){wo(n._,e._,r=t[a]),r=new No(r),c=e.next,s=n.previous,f=e._.r,l=n._.r;do{if(f<=l){if(Mo(c._,r._)){e=c,n.next=e,e.previous=n,--a;continue t}f+=c._.r,c=c.next}else{if(Mo(s._,r._)){(n=s).next=e,e.previous=n,--a;continue t}l+=s._.r,s=s.previous}}while(c!==s.next);for(r.previous=n,r.next=e,n.next=e.previous=e=r,o=To(n);(r=r.next)!==e;)(u=To(r))<o&&(n=r,o=u);e=n.next}for(n=[e._],r=e;(r=r.next)!==e;)n.push(r._);for(r=go(n),a=0;a<i;++a)n=t[a],n.x-=r.x,n.y-=r.y;return r.r}function So(t){if("function"!=typeof t)throw new Error;return t}function Eo(){return 0}function Ao(t){return function(){return t}}function Co(t){return Math.sqrt(t.value)}function zo(t){return function(n){n.children||(n.r=Math.max(0,+t(n)||0))}}function Po(t,n){return function(e){if(r=e.children){var r,i,o,u=r.length,a=t(e)*n||0;if(a)for(i=0;i<u;++i)r[i].r+=a;if(o=ko(r),a)for(i=0;i<u;++i)r[i].r-=a;e.r=o+a}}}function Ro(t){return function(n){var e=n.parent;n.r*=t,e&&(n.x=e.x+t*n.x,n.y=e.y+t*n.y)}}function Lo(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function qo(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(r-n)/t.value;++a<c;)(o=u[a]).y0=e,o.y1=i,o.x0=n,o.x1=n+=o.value*s}function Do(t){return t.id}function Uo(t){return t.parentId}function Oo(t,n){return t.parent===n.parent?1:2}function Fo(t){var n=t.children;return n?n[0]:t.t}function Io(t){var n=t.children;return n?n[n.length-1]:t.t}function Yo(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function Bo(t,n,e){return t.a.parent===n.parent?t.a:e}function Ho(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function jo(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(i-e)/t.value;++a<c;)(o=u[a]).x0=n,o.x1=r,o.y0=e,o.y1=e+=o.value*s}function Xo(t,n,e,r,i,o){for(var u,a,c,s,f,l,h,p,d,v,g,_=[],y=n.children,m=0,x=0,b=y.length,w=n.value;m<b;){c=i-e,s=o-r;do{f=y[x++].value}while(!f&&x<b);for(l=h=f,g=f*f*(v=Math.max(s/c,c/s)/(w*t)),d=Math.max(h/g,g/l);x<b;++x){if(f+=a=y[x].value,a<l&&(l=a),a>h&&(h=a),g=f*f*v,(p=Math.max(h/g,g/l))>d){f-=a;break}d=p}_.push(u={value:f,dice:c<s,children:y.slice(m,x)}),u.dice?qo(u,e,r,i,w?r+=s*f/w:o):jo(u,e,r,w?e+=c*f/w:i,o),w-=f,m=x}return _}function Vo(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function $o(t,n){return t[0]-n[0]||t[1]-n[1]}function Wo(t){for(var n=t.length,e=[0,1],r=2,i=2;i<n;++i){for(;r>1&&Vo(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function Zo(t){this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function Go(t){if(!t._start)try{(function(t){for(;t._start=t._waiting&&t._active<t._size;){var n=t._ended+t._active,e=t._tasks[n],r=e.length-1,i=e[r];e[r]=function(t,n){return function(e,r){t._tasks[n]&&(--t._active,++t._ended,t._tasks[n]=null,null==t._error&&(null!=e?Qo(t,e):(t._data[n]=r,t._waiting?Go(t):Jo(t))))}}(t,n),--t._waiting,++t._active,e=i.apply(null,e),t._tasks[n]&&(t._tasks[n]=e||ev)}})(t)}catch(n){if(t._tasks[t._ended+t._active-1])Qo(t,n);else if(!t._data)throw n}}function Qo(t,n){var e,r=t._tasks.length;for(t._error=n,t._data=void 0,t._waiting=NaN;--r>=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(n){}t._active=NaN,Jo(t)}function Jo(t){if(!t._active&&t._call){var n=t._data;t._data=void 0,t._call(t._error,n)}}function Ko(t){if(null==t)t=1/0;else if(!((t=+t)>=1))throw new Error("invalid concurrency");return new Zo(t)}function tu(){return Math.random()}function nu(t,n){function e(t){var n,e=s.status;if(!e&&function(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}(s)||e>=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(t){return void a.call("error",r,t)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=N("beforesend","progress","load","error"),c=se(),s=new XMLHttpRequest,f=null,l=null,h=0;if("undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),null==o&&"function"==typeof e&&(o=e,e=null),null!=o&&1===o.length&&(o=function(t){return function(n,e){t(null==n?e:null)}}(o)),null!=o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},null!=n){if("function"!=typeof n)throw new Error("invalid callback: "+n);return r.get(n)}return r}function eu(t,n){return function(e,r){var i=nu(e).mimeType(t).response(n);if(null!=r){if("function"!=typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}}function ru(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=nu(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(function(t,n){return function(e){return t(e.responseText,n)}}(n,r=t)):r},o.row(r),i?o.get(i):o}}function iu(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==yv)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=se(),r=[],i=yv;return t=null==t?[]:_v.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=se();for(var i,o,u=-1,a=t.length;++u<a;)e.has(o=(i=t[u])+"")||e.set(o,r.push(i));return n},n.range=function(e){return arguments.length?(t=_v.call(e),n):t.slice()},n.unknown=function(t){return arguments.length?(i=t,n):i},n.copy=function(){return iu().domain(r).range(t).unknown(i)},n}function ou(){function t(){var t=i().length,r=u[1]<u[0],h=u[r-0],p=u[1-r];n=(p-h)/Math.max(1,t-c+2*s),a&&(n=Math.floor(n)),h+=(p-h-n*(t-c))*l,e=n*(1-c),a&&(h=Math.round(h),e=Math.round(e));var d=f(t).map(function(t){return h+n*t});return o(r?d.reverse():d)}var n,e,r=iu().unknown(void 0),i=r.domain,o=r.range,u=[0,1],a=!1,c=0,s=0,l=.5;return delete r.unknown,r.domain=function(n){return arguments.length?(i(n),t()):i()},r.range=function(n){return arguments.length?(u=[+n[0],+n[1]],t()):u.slice()},r.rangeRound=function(n){return u=[+n[0],+n[1]],a=!0,t()},r.bandwidth=function(){return e},r.step=function(){return n},r.round=function(n){return arguments.length?(a=!!n,t()):a},r.padding=function(n){return arguments.length?(c=s=Math.max(0,Math.min(1,n)),t()):c},r.paddingInner=function(n){return arguments.length?(c=Math.max(0,Math.min(1,n)),t()):c},r.paddingOuter=function(n){return arguments.length?(s=Math.max(0,Math.min(1,n)),t()):s},r.align=function(n){return arguments.length?(l=Math.max(0,Math.min(1,n)),t()):l},r.copy=function(){return ou().domain(i()).range(u).round(a).paddingInner(c).paddingOuter(s).align(l)},t()}function uu(t){var n=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return uu(n())},t}function au(t){return function(){return t}}function cu(t){return+t}function su(t,n){return(n-=t=+t)?function(e){return(e-t)/n}:au(n)}function fu(t,n,e,r){var i=t[0],o=t[1],u=n[0],a=n[1];return o<i?(i=e(o,i),u=r(a,u)):(i=e(i,o),u=r(u,a)),function(t){return u(i(t))}}function lu(t,n,e,r){var i=Math.min(t.length,n.length)-1,o=new Array(i),u=new Array(i),a=-1;for(t[i]<t[0]&&(t=t.slice().reverse(),n=n.slice().reverse());++a<i;)o[a]=e(t[a],t[a+1]),u[a]=r(n[a],n[a+1]);return function(n){var e=Os(t,n,1,i)-1;return u[e](o[e](n))}}function hu(t,n){return n.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp())}function pu(t,n){function e(){return i=Math.min(a.length,c.length)>2?lu:fu,o=u=null,r}function r(n){return(o||(o=i(a,c,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=n?0:t>=e?1:r(t)}}}(t):t,s)))(+n)}var i,o,u,a=mv,c=mv,s=fn,f=!1;return r.invert=function(t){return(u||(u=i(c,a,su,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=gv.call(t,cu),e()):a.slice()},r.range=function(t){return arguments.length?(c=_v.call(t),e()):c.slice()},r.rangeRound=function(t){return c=_v.call(t),s=ln,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function du(n){var e=n.domain;return n.ticks=function(t){var n=e();return l(n[0],n[n.length-1],null==t?10:t)},n.tickFormat=function(n,r){return function(n,e,r){var i,o=n[0],u=n[n.length-1],a=p(o,u,null==e?10:e);switch((r=De(null==r?",f":r)).type){case"s":var c=Math.max(Math.abs(o),Math.abs(u));return null!=r.precision||isNaN(i=Be(a,c))||(r.precision=i),t.formatPrefix(r,c);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=He(a,Math.max(Math.abs(o),Math.abs(u))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=Ye(a))||(r.precision=i-2*("%"===r.type))}return t.format(r)}(e(),n,r)},n.nice=function(t){null==t&&(t=10);var r,i=e(),o=0,u=i.length-1,a=i[o],c=i[u];return c<a&&(r=a,a=c,c=r,r=o,o=u,u=r),(r=h(a,c,t))>0?r=h(a=Math.floor(a/r)*r,c=Math.ceil(c/r)*r,t):r<0&&(r=h(a=Math.ceil(a*r)/r,c=Math.floor(c*r)/r,t)),r>0?(i[o]=Math.floor(a/r)*r,i[u]=Math.ceil(c/r)*r,e(i)):r<0&&(i[o]=Math.ceil(a*r)/r,i[u]=Math.floor(c*r)/r,e(i)),n},n}function vu(){var t=pu(su,an);return t.copy=function(){return hu(t,vu())},du(t)}function gu(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=gv.call(e,cu),t):n.slice()},t.copy=function(){return gu().domain(n)},du(t)}function _u(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],u=t[i];return u<o&&(e=r,r=i,i=e,e=o,o=u,u=e),t[r]=n.floor(o),t[i]=n.ceil(u),t}function yu(t,n){return(n=Math.log(n/t))?function(e){return Math.log(e/t)/n}:au(n)}function mu(t,n){return t<0?function(e){return-Math.pow(-n,e)*Math.pow(-t,1-e)}:function(e){return Math.pow(n,e)*Math.pow(t,1-e)}}function xu(t){return isFinite(t)?+("1e"+t):t<0?0:t}function bu(t){return 10===t?xu:t===Math.E?Math.exp:function(n){return Math.pow(t,n)}}function wu(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(n){return Math.log(n)/t})}function Mu(t){return function(n){return-t(-n)}}function Tu(){function n(){return o=wu(i),u=bu(i),r()[0]<0&&(o=Mu(o),u=Mu(u)),e}var e=pu(yu,mu).domain([1,10]),r=e.domain,i=10,o=wu(10),u=bu(10);return e.base=function(t){return arguments.length?(i=+t,n()):i},e.domain=function(t){return arguments.length?(r(t),n()):r()},e.ticks=function(t){var n,e=r(),a=e[0],c=e[e.length-1];(n=c<a)&&(p=a,a=c,c=p);var s,f,h,p=o(a),d=o(c),v=null==t?10:+t,g=[];if(!(i%1)&&d-p<v){if(p=Math.round(p)-1,d=Math.round(d)+1,a>0){for(;p<d;++p)for(f=1,s=u(p);f<i;++f)if(!((h=s*f)<a)){if(h>c)break;g.push(h)}}else for(;p<d;++p)for(f=i-1,s=u(p);f>=1;--f)if(!((h=s*f)<a)){if(h>c)break;g.push(h)}}else g=l(p,d,Math.min(d-p,v)).map(u);return n?g.reverse():g},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i<i-.5&&(n*=i),n<=a?r(t):""}},e.nice=function(){return r(_u(r(),{floor:function(t){return u(Math.floor(o(t)))},ceil:function(t){return u(Math.ceil(o(t)))}}))},e.copy=function(){return hu(e,Tu().base(i))},e}function Nu(t,n){return t<0?-Math.pow(-t,n):Math.pow(t,n)}function ku(){var t=1,n=pu(function(n,e){return(e=Nu(e,t)-(n=Nu(n,t)))?function(r){return(Nu(r,t)-n)/e}:au(e)},function(n,e){return e=Nu(e,t)-(n=Nu(n,t)),function(r){return Nu(n+e*r,1/t)}}),e=n.domain;return n.exponent=function(n){return arguments.length?(t=+n,e(e())):t},n.copy=function(){return hu(n,ku().exponent(t))},du(n)}function Su(){function t(){var t=0,n=Math.max(1,i.length);for(o=new Array(n-1);++t<n;)o[t-1]=v(r,t/n);return e}function e(t){if(!isNaN(t=+t))return i[Os(o,t)]}var r=[],i=[],o=[];return e.invertExtent=function(t){var n=i.indexOf(t);return n<0?[NaN,NaN]:[n>0?o[n-1]:r[0],n<o.length?o[n]:r[r.length-1]]},e.domain=function(e){if(!arguments.length)return r.slice();r=[];for(var i,o=0,u=e.length;o<u;++o)null==(i=e[o])||isNaN(i=+i)||r.push(i);return r.sort(n),t()},e.range=function(n){return arguments.length?(i=_v.call(n),t()):i.slice()},e.quantiles=function(){return o.slice()},e.copy=function(){return Su().domain(r).range(i)},e}function Eu(){function t(t){if(t<=t)return u[Os(o,t,0,i)]}function n(){var n=-1;for(o=new Array(i);++n<i;)o[n]=((n+1)*r-(n-i)*e)/(i+1);return t}var e=0,r=1,i=1,o=[.5],u=[0,1];return t.domain=function(t){return arguments.length?(e=+t[0],r=+t[1],n()):[e,r]},t.range=function(t){return arguments.length?(i=(u=_v.call(t)).length-1,n()):u.slice()},t.invertExtent=function(t){var n=u.indexOf(t);return n<0?[NaN,NaN]:n<1?[e,o[0]]:n>=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return Eu().domain([e,r]).range(u)},du(t)}function Au(){function t(t){if(t<=t)return e[Os(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=_v.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=_v.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Au().domain(n).range(e)},t}function Cu(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n<e-t?n:e},i.offset=function(t,e){return n(t=new Date(+t),null==e?1:Math.floor(e)),t},i.range=function(e,r,o){var u,a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e<r&&o>0))return a;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(u<e&&e<r);return a},i.filter=function(e){return Cu(function(n){if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return xv.setTime(+n),bv.setTime(+r),t(xv),t(bv),Math.floor(e(xv,bv))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}function zu(t){return Cu(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Tv)/Nv})}function Pu(t){return Cu(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/Nv})}function Ru(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Lu(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function qu(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function Du(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date(+e));++a<s;)37===t.charCodeAt(a)&&(u.push(t.slice(c,a)),null!=(i=Mg[r=t.charAt(++a)])?r=t.charAt(++a):i="e"===r?" ":"0",(o=n[r])&&(r=o(e,i)),u.push(r),c=a+1);return u.push(t.slice(c,a)),u.join("")}}function e(t,n){return function(e){var i,o,u=qu(1900);if(r(u,t,e+="",0)!=e.length)return null;if("Q"in u)return new Date(u.Q);if("p"in u&&(u.H=u.H%12+12*u.p),"V"in u){if(u.V<1||u.V>53)return null;"w"in u||(u.w=1),"Z"in u?(i=(o=(i=Lu(qu(u.y))).getUTCDay())>4||0===o?og.ceil(i):og(i),i=eg.offset(i,7*(u.V-1)),u.y=i.getUTCFullYear(),u.m=i.getUTCMonth(),u.d=i.getUTCDate()+(u.w+6)%7):(i=(o=(i=n(qu(u.y))).getDay())>4||0===o?qv.ceil(i):qv(i),i=Pv.offset(i,7*(u.V-1)),u.y=i.getFullYear(),u.m=i.getMonth(),u.d=i.getDate()+(u.w+6)%7)}else("W"in u||"U"in u)&&("w"in u||(u.w="u"in u?u.u%7:"W"in u?1:0),o="Z"in u?Lu(qu(u.y)).getUTCDay():n(qu(u.y)).getDay(),u.m=0,u.d="W"in u?(u.w+6)%7+7*u.W-(o+5)%7:u.w+7*u.U-(o+6)%7);return"Z"in u?(u.H+=u.Z/100|0,u.M+=u.Z%100,Lu(u)):n(u)}}function r(t,n,e,r){for(var i,o,u=0,a=n.length,c=e.length;u<a;){if(r>=c)return-1;if(37===(i=n.charCodeAt(u++))){if(i=n.charAt(u++),!(o=T[i in Mg?n.charAt(u++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}var i=t.dateTime,o=t.date,u=t.time,a=t.periods,c=t.days,s=t.shortDays,f=t.months,l=t.shortMonths,h=Fu(a),p=Iu(a),d=Fu(c),v=Iu(c),g=Fu(s),_=Iu(s),y=Fu(f),m=Iu(f),x=Fu(l),b=Iu(l),w={a:function(t){return s[t.getDay()]},A:function(t){return c[t.getDay()]},b:function(t){return l[t.getMonth()]},B:function(t){return f[t.getMonth()]},c:null,d:ua,e:ua,f:la,H:aa,I:ca,j:sa,L:fa,m:ha,M:pa,p:function(t){return a[+(t.getHours()>=12)]},Q:Ya,s:Ba,S:da,u:va,U:ga,V:_a,w:ya,W:ma,x:null,X:null,y:xa,Y:ba,Z:wa,"%":Ia},M={a:function(t){return s[t.getUTCDay()]},A:function(t){return c[t.getUTCDay()]},b:function(t){return l[t.getUTCMonth()]},B:function(t){return f[t.getUTCMonth()]},c:null,d:Ma,e:Ma,f:Ea,H:Ta,I:Na,j:ka,L:Sa,m:Aa,M:Ca,p:function(t){return a[+(t.getUTCHours()>=12)]},Q:Ya,s:Ba,S:za,u:Pa,U:Ra,V:La,w:qa,W:Da,x:null,X:null,y:Ua,Y:Oa,Z:Fa,"%":Ia},T={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=_[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=v[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=b[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=m[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,n,e){return r(t,i,n,e)},d:Gu,e:Gu,f:ea,H:Ju,I:Ju,j:Qu,L:na,m:Zu,M:Ku,p:function(t,n,e){var r=h.exec(n.slice(e));return r?(t.p=p[r[0].toLowerCase()],e+r[0].length):-1},Q:ia,s:oa,S:ta,u:Bu,U:Hu,V:ju,w:Yu,W:Xu,x:function(t,n,e){return r(t,o,n,e)},X:function(t,n,e){return r(t,u,n,e)},y:$u,Y:Vu,Z:Wu,"%":ra};return w.x=n(o,w),w.X=n(u,w),w.c=n(i,w),M.x=n(o,M),M.X=n(u,M),M.c=n(i,M),{format:function(t){var e=n(t+="",w);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",Ru);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",M);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,Lu);return n.toString=function(){return t},n}}}function Uu(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o<e?new Array(e-o+1).join(n)+i:i)}function Ou(t){return t.replace(kg,"\\$&")}function Fu(t){return new RegExp("^(?:"+t.map(Ou).join("|")+")","i")}function Iu(t){for(var n={},e=-1,r=t.length;++e<r;)n[t[e].toLowerCase()]=e;return n}function Yu(t,n,e){var r=Tg.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function Bu(t,n,e){var r=Tg.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function Hu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function ju(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function Xu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function Vu(t,n,e){var r=Tg.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function $u(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function Wu(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Zu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Gu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Qu(t,n,e){var r=Tg.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ju(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ku(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function ta(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function na(t,n,e){var r=Tg.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ea(t,n,e){var r=Tg.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function ra(t,n,e){var r=Ng.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function ia(t,n,e){var r=Tg.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function oa(t,n,e){var r=Tg.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function ua(t,n){return Uu(t.getDate(),n,2)}function aa(t,n){return Uu(t.getHours(),n,2)}function ca(t,n){return Uu(t.getHours()%12||12,n,2)}function sa(t,n){return Uu(1+Pv.count(Gv(t),t),n,3)}function fa(t,n){return Uu(t.getMilliseconds(),n,3)}function la(t,n){return fa(t,n)+"000"}function ha(t,n){return Uu(t.getMonth()+1,n,2)}function pa(t,n){return Uu(t.getMinutes(),n,2)}function da(t,n){return Uu(t.getSeconds(),n,2)}function va(t){var n=t.getDay();return 0===n?7:n}function ga(t,n){return Uu(Lv.count(Gv(t),t),n,2)}function _a(t,n){var e=t.getDay();return t=e>=4||0===e?Ov(t):Ov.ceil(t),Uu(Ov.count(Gv(t),t)+(4===Gv(t).getDay()),n,2)}function ya(t){return t.getDay()}function ma(t,n){return Uu(qv.count(Gv(t),t),n,2)}function xa(t,n){return Uu(t.getFullYear()%100,n,2)}function ba(t,n){return Uu(t.getFullYear()%1e4,n,4)}function wa(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+Uu(n/60|0,"0",2)+Uu(n%60,"0",2)}function Ma(t,n){return Uu(t.getUTCDate(),n,2)}function Ta(t,n){return Uu(t.getUTCHours(),n,2)}function Na(t,n){return Uu(t.getUTCHours()%12||12,n,2)}function ka(t,n){return Uu(1+eg.count(xg(t),t),n,3)}function Sa(t,n){return Uu(t.getUTCMilliseconds(),n,3)}function Ea(t,n){return Sa(t,n)+"000"}function Aa(t,n){return Uu(t.getUTCMonth()+1,n,2)}function Ca(t,n){return Uu(t.getUTCMinutes(),n,2)}function za(t,n){return Uu(t.getUTCSeconds(),n,2)}function Pa(t){var n=t.getUTCDay();return 0===n?7:n}function Ra(t,n){return Uu(ig.count(xg(t),t),n,2)}function La(t,n){var e=t.getUTCDay();return t=e>=4||0===e?cg(t):cg.ceil(t),Uu(cg.count(xg(t),t)+(4===xg(t).getUTCDay()),n,2)}function qa(t){return t.getUTCDay()}function Da(t,n){return Uu(og.count(xg(t),t),n,2)}function Ua(t,n){return Uu(t.getUTCFullYear()%100,n,2)}function Oa(t,n){return Uu(t.getUTCFullYear()%1e4,n,4)}function Fa(){return"+0000"}function Ia(){return"%"}function Ya(t){return+t}function Ba(t){return Math.floor(+t/1e3)}function Ha(n){return bg=Du(n),t.timeFormat=bg.format,t.timeParse=bg.parse,t.utcFormat=bg.utcFormat,t.utcParse=bg.utcParse,bg}function ja(t){return new Date(t)}function Xa(t){return t instanceof Date?+t:+new Date(+t)}function Va(t,n,r,i,o,u,a,c,s){function f(e){return(a(e)<e?g:u(e)<e?_:o(e)<e?y:i(e)<e?m:n(e)<e?r(e)<e?x:b:t(e)<e?w:M)(e)}function l(n,r,i,o){if(null==n&&(n=10),"number"==typeof n){var u=Math.abs(i-r)/n,a=e(function(t){return t[2]}).right(T,u);a===T.length?(o=p(r/Dg,i/Dg,n),n=t):a?(o=(a=T[u/T[a-1][2]<T[a][2]/u?a-1:a])[1],n=a[0]):(o=Math.max(p(r,i,n),1),n=c)}return null==o?n:n.every(o)}var h=pu(su,an),d=h.invert,v=h.domain,g=s(".%L"),_=s(":%S"),y=s("%I:%M"),m=s("%I %p"),x=s("%a %d"),b=s("%b %d"),w=s("%B"),M=s("%Y"),T=[[a,1,Cg],[a,5,5*Cg],[a,15,15*Cg],[a,30,30*Cg],[u,1,zg],[u,5,5*zg],[u,15,15*zg],[u,30,30*zg],[o,1,Pg],[o,3,3*Pg],[o,6,6*Pg],[o,12,12*Pg],[i,1,Rg],[i,2,2*Rg],[r,1,Lg],[n,1,qg],[n,3,3*qg],[t,1,Dg]];return h.invert=function(t){return new Date(d(t))},h.domain=function(t){return arguments.length?v(gv.call(t,Xa)):v().map(ja)},h.ticks=function(t,n){var e,r=v(),i=r[0],o=r[r.length-1],u=o<i;return u&&(e=i,i=o,o=e),e=l(t,i,o,n),e=e?e.range(i,o+1):[],u?e.reverse():e},h.tickFormat=function(t,n){return null==n?f:s(n)},h.nice=function(t,n){var e=v();return(t=l(t,e[0],e[e.length-1],n))?v(_u(e,t)):h},h.copy=function(){return hu(h,Va(t,n,r,i,o,u,a,c,s))},h}function $a(t){return t.match(/.{6}/g).map(function(t){return"#"+t})}function Wa(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}function Za(t){function n(n){var o=(n-e)/(r-e);return t(i?Math.max(0,Math.min(1,o)):o)}var e=0,r=1,i=!1;return n.domain=function(t){return arguments.length?(e=+t[0],r=+t[1],n):[e,r]},n.clamp=function(t){return arguments.length?(i=!!t,n):i},n.interpolator=function(e){return arguments.length?(t=e,n):t},n.copy=function(){return Za(t).domain([e,r]).clamp(i)},du(n)}function Ga(t){return function(){return t}}function Qa(t){return t>=1?i_:t<=-1?-i_:Math.asin(t)}function Ja(t){return t.innerRadius}function Ka(t){return t.outerRadius}function tc(t){return t.startAngle}function nc(t){return t.endAngle}function ec(t){return t&&t.padAngle}function rc(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/n_(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,g=(h+d)/2,_=(p+v)/2,y=d-h,m=v-p,x=y*y+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*n_(Jg(0,b*b*x-w*w)),T=(w*m-y*M)/x,N=(-w*y-m*M)/x,k=(w*m+y*M)/x,S=(-w*y+m*M)/x,E=T-g,A=N-_,C=k-g,z=S-_;return E*E+A*A>C*C+z*z&&(T=k,N=S),{cx:T,cy:N,x01:-f,y01:-l,x11:T*(i/b-1),y11:N*(i/b-1)}}function ic(t){this._context=t}function oc(t){return new ic(t)}function uc(t){return t[0]}function ac(t){return t[1]}function cc(){function t(t){var a,c,s,f=t.length,l=!1;for(null==i&&(u=o(s=ee())),a=0;a<=f;++a)!(a<f&&r(c=t[a],a,t))===l&&((l=!l)?u.lineStart():u.lineEnd()),l&&u.point(+n(c,a,t),+e(c,a,t));if(s)return u=null,s+""||null}var n=uc,e=ac,r=Ga(!0),i=null,o=oc,u=null;return t.x=function(e){return arguments.length?(n="function"==typeof e?e:Ga(+e),t):n},t.y=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),t):e},t.defined=function(n){return arguments.length?(r="function"==typeof n?n:Ga(!!n),t):r},t.curve=function(n){return arguments.length?(o=n,null!=i&&(u=o(i)),t):o},t.context=function(n){return arguments.length?(null==n?i=u=null:u=o(i=n),t):i},t}function sc(){function t(t){var n,f,l,h,p,d=t.length,v=!1,g=new Array(d),_=new Array(d);for(null==a&&(s=c(p=ee())),n=0;n<=d;++n){if(!(n<d&&u(h=t[n],n,t))===v)if(v=!v)f=n,s.areaStart(),s.lineStart();else{for(s.lineEnd(),s.lineStart(),l=n-1;l>=f;--l)s.point(g[l],_[l]);s.lineEnd(),s.areaEnd()}v&&(g[n]=+e(h,n,t),_[n]=+i(h,n,t),s.point(r?+r(h,n,t):g[n],o?+o(h,n,t):_[n]))}if(p)return s=null,p+""||null}function n(){return cc().defined(u).curve(c).context(a)}var e=uc,r=null,i=Ga(0),o=ac,u=Ga(!0),a=null,c=oc,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Ga(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Ga(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Ga(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Ga(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Ga(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t}function fc(t,n){return n<t?-1:n>t?1:n>=t?0:NaN}function lc(t){return t}function hc(t){this._curve=t}function pc(t){function n(n){return new hc(t(n))}return n._curve=t,n}function dc(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(pc(t)):n()._curve},t}function vc(){return dc(cc().curve(u_))}function gc(){var t=sc().curve(u_),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return dc(e())},delete t.lineX0,t.lineEndAngle=function(){return dc(r())},delete t.lineX1,t.lineInnerRadius=function(){return dc(i())},delete t.lineY0,t.lineOuterRadius=function(){return dc(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(pc(t)):n()._curve},t}function _c(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function yc(t){return t.source}function mc(t){return t.target}function xc(t){function n(){var n,a=a_.call(arguments),c=e.apply(this,a),s=r.apply(this,a);if(u||(u=n=ee()),t(u,+i.apply(this,(a[0]=c,a)),+o.apply(this,a),+i.apply(this,(a[0]=s,a)),+o.apply(this,a)),n)return u=null,n+""||null}var e=yc,r=mc,i=uc,o=ac,u=null;return n.source=function(t){return arguments.length?(e=t,n):e},n.target=function(t){return arguments.length?(r=t,n):r},n.x=function(t){return arguments.length?(i="function"==typeof t?t:Ga(+t),n):i},n.y=function(t){return arguments.length?(o="function"==typeof t?t:Ga(+t),n):o},n.context=function(t){return arguments.length?(u=null==t?null:t,n):u},n}function bc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function wc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function Mc(t,n,e,r,i){var o=_c(n,e),u=_c(n,e=(e+i)/2),a=_c(r,e),c=_c(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(u[0],u[1],a[0],a[1],c[0],c[1])}function Tc(){}function Nc(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function kc(t){this._context=t}function Sc(t){this._context=t}function Ec(t){this._context=t}function Ac(t,n){this._basis=new kc(t),this._beta=n}function Cc(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function zc(t,n){this._context=t,this._k=(1-n)/6}function Pc(t,n){this._context=t,this._k=(1-n)/6}function Rc(t,n){this._context=t,this._k=(1-n)/6}function Lc(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>e_){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>e_){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function qc(t,n){this._context=t,this._alpha=n}function Dc(t,n){this._context=t,this._alpha=n}function Uc(t,n){this._context=t,this._alpha=n}function Oc(t){this._context=t}function Fc(t){return t<0?-1:1}function Ic(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Fc(o)+Fc(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function Yc(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Bc(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function Hc(t){this._context=t}function jc(t){this._context=new Xc(t)}function Xc(t){this._context=t}function Vc(t){this._context=t}function $c(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n<r-1;++n)i[n]=1,o[n]=4,u[n]=4*t[n]+2*t[n+1];for(i[r-1]=2,o[r-1]=7,u[r-1]=8*t[r-1]+t[r],n=1;n<r;++n)e=i[n]/o[n-1],o[n]-=e,u[n]-=e*u[n-1];for(i[r-1]=u[r-1]/o[r-1],n=r-2;n>=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n<r-1;++n)o[n]=2*t[n+1]-i[n+1];return[i,o]}function Wc(t,n){this._context=t,this._t=n}function Zc(t,n){if((i=t.length)>1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o<i;++o)for(r=u,u=t[n[o]],e=0;e<a;++e)u[e][1]+=u[e][0]=isNaN(r[e][1])?r[e][0]:r[e][1]}function Gc(t){for(var n=t.length,e=new Array(n);--n>=0;)e[n]=n;return e}function Qc(t,n){return t[n]}function Jc(t){var n=t.map(Kc);return Gc(t).sort(function(t,e){return n[t]-n[e]})}function Kc(t){for(var n,e=0,r=-1,i=t.length;++r<i;)(n=+t[r][1])&&(e+=n);return e}function ts(t){return function(){return t}}function ns(t){return t[0]}function es(t){return t[1]}function rs(){this._=null}function is(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function os(t,n){var e=n,r=n.R,i=e.U;i?i.L===e?i.L=r:i.R=r:t._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function us(t,n){var e=n,r=n.L,i=e.U;i?i.L===e?i.L=r:i.R=r:t._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function as(t){for(;t.L;)t=t.L;return t}function cs(t,n,e,r){var i=[null,null],o=D_.push(i)-1;return i.left=t,i.right=n,e&&fs(i,t,n,e),r&&fs(i,n,t,r),L_[t.index].halfedges.push(o),L_[n.index].halfedges.push(o),i}function ss(t,n,e){var r=[n,e];return r.left=t,r}function fs(t,n,e,r){t[0]||t[1]?t.left===e?t[1]=r:t[0]=r:(t[0]=r,t.left=n,t.right=e)}function ls(t,n,e,r,i){var o,u=t[0],a=t[1],c=u[0],s=u[1],f=0,l=1,h=a[0]-c,p=a[1]-s;if(o=n-c,h||!(o>0)){if(o/=h,h<0){if(o<f)return;o<l&&(l=o)}else if(h>0){if(o>l)return;o>f&&(f=o)}if(o=r-c,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>f&&(f=o)}else if(h>0){if(o<f)return;o<l&&(l=o)}if(o=e-s,p||!(o>0)){if(o/=p,p<0){if(o<f)return;o<l&&(l=o)}else if(p>0){if(o>l)return;o>f&&(f=o)}if(o=i-s,p||!(o<0)){if(o/=p,p<0){if(o>l)return;o>f&&(f=o)}else if(p>0){if(o<f)return;o<l&&(l=o)}return!(f>0||l<1)||(f>0&&(t[0]=[c+f*h,s+f*p]),l<1&&(t[1]=[c+l*h,s+l*p]),!0)}}}}}function hs(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,g=(h+d)/2;if(d===h){if(v<n||v>=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]<e)return}else c=[v,i];o=[v,e]}}else if(u=(l-p)/(d-h),a=g-u*v,u<-1||u>1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]<e)return}else c=[(i-a)/u,i];o=[(e-a)/u,e]}else if(h<d){if(c){if(c[0]>=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]<n)return}else c=[r,u*r+a];o=[n,u*n+a]}return t[0]=c,t[1]=o,!0}function ps(t,n){var e=t.site,r=n.left,i=n.right;return e===i&&(i=r,r=e),i?Math.at