<?PHP
/* Copyright 2005-2025, Lime Technology
 * Copyright 2012-2025, Bergware International.
 * Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/webGui/include/Helpers.php";
extract(parse_plugin_cfg('dynamix',true));

$var = parse_ini_file('state/var.ini');
ignore_user_abort(true);

$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();

#   ███████╗██╗   ██╗███╗   ██╗ ██████╗████████╗██╗ ██████╗ ███╗   ██╗███████╗
#   ██╔════╝██║   ██║████╗  ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗  ██║██╔════╝
#   █████╗  ██║   ██║██╔██╗ ██║██║        ██║   ██║██║   ██║██╔██╗ ██║███████╗
#   ██╔══╝  ██║   ██║██║╚██╗██║██║        ██║   ██║██║   ██║██║╚██╗██║╚════██║
#   ██║     ╚██████╔╝██║ ╚████║╚██████╗   ██║   ██║╚██████╔╝██║ ╚████║███████║
#   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝

$custom = DockerUtil::custom();
$subnet = DockerUtil::network($custom);
$cpus   = DockerUtil::cpus();

function cpu_pinning() {
  global $xml,$cpus;
  $vcpu = explode(',',_var($xml,'CPUset'));
  $total = count($cpus);
  $loop = floor(($total-1)/16)+1;
  for ($c = 0; $c < $loop; $c++) {
    $row1 = $row2 = [];
    $max = ($c == $loop-1 ? ($total%16?:16) : 16);
    for ($n = 0; $n < $max; $n++) {
      unset($cpu1,$cpu2);
      [$cpu1, $cpu2] = my_preg_split('/[,-]/',$cpus[$c*16+$n]);
      $check1 = in_array($cpu1, $vcpu) ? ' checked':'';
      $check2 = $cpu2 ? (in_array($cpu2, $vcpu) ? ' checked':''):'';
      $row1[] = "<label id='cpu$cpu1' class='checkbox'>$cpu1<input type='checkbox' id='box$cpu1'$check1><span class='checkmark'></span></label>";
      if ($cpu2) $row2[] = "<label id='cpu$cpu2' class='checkbox'>$cpu2<input type='checkbox' id='box$cpu2'$check2><span class='checkmark'></span></label>";
    }
    if ($c) echo '<hr>';
    echo "<span class='cpu'>"._('CPU').":</span>".implode($row1);
    if ($row2) echo "<br><span class='cpu'>"._('HT').":</span>".implode($row2);
  }
}

#    ██████╗ ██████╗ ██████╗ ███████╗
#   ██╔════╝██╔═══██╗██╔══██╗██╔════╝
#   ██║     ██║   ██║██║  ██║█████╗
#   ██║     ██║   ██║██║  ██║██╔══╝
#   ╚██████╗╚██████╔╝██████╔╝███████╗
#    ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝

##########################
##   CREATE CONTAINER   ##
##########################

if (isset($_POST['contName'])) {
  $postXML = postToXML($_POST, true);
  $dry_run = isset($_POST['dryRun']) && $_POST['dryRun']=='true';
  $existing = _var($_POST,'existingContainer',false);
  $create_paths = $dry_run ? false : true;
  // Get the command line
  [$cmd, $Name, $Repository] = xmlToCommand($postXML, $create_paths);
  readfile("$docroot/plugins/dynamix.docker.manager/log.htm");
  @flush();
  // Saving the generated configuration file.
  $userTmplDir = $dockerManPaths['templates-user'];
  if (!is_dir($userTmplDir)) mkdir($userTmplDir, 0777, true);
  if ($Name) {
    $filename = sprintf('%s/my-%s.xml', $userTmplDir, $Name);
    if (is_file($filename)) {
      $oldXML = simplexml_load_file($filename);
      if ($oldXML->Icon != $_POST['contIcon']) {
        if (!strpos($Repository,":")) $Repository .= ":latest";
        $iconPath = $DockerTemplates->getIcon($Repository,$Name);
        @unlink("$docroot/$iconPath");
        @unlink("{$dockerManPaths['images']}/".basename($iconPath));
      }
    }
    file_put_contents($filename, $postXML);
  }
  // Run dry
  if ($dry_run) {
    echo "<h2>XML</h2>";
    echo "<pre>".htmlspecialchars($postXML)."</pre>";
    echo "<h2>COMMAND:</h2>";
    echo "<pre>".htmlspecialchars($cmd)."</pre>";
    echo "<div style='text-align:center'><button type='button' onclick='window.location=window.location.pathname+window.location.hash+\"?xmlTemplate=edit:$filename\"'>"._('Back')."</button>";
    echo "<button type='button' onclick='done()'>"._('Done')."</button></div><br>";
    goto END;
  }
  // Will only pull image if it's absent
  if (!$DockerClient->doesImageExist($Repository)) {
    // Pull image
    if (!pullImage($Name, $Repository)) {
      echo '<div style="text-align:center"><button type="button" onclick="done()">'._('Done').'</button></div><br>';
      goto END;
    }
  }
  $startContainer = true;
  // Remove existing container
  if ($DockerClient->doesContainerExist($Name)) {
    // attempt graceful stop of container first
    $oldContainerInfo = $DockerClient->getContainerDetails($Name);
    if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['State']['Running'])) {
      // attempt graceful stop of container first
      stopContainer($Name);
    }
    // force kill container if still running after 10 seconds
    removeContainer($Name);
  }
  // Remove old container if renamed
  if ($existing && $DockerClient->doesContainerExist($existing)) {
    // determine if the container is still running
    $oldContainerInfo = $DockerClient->getContainerDetails($existing);
    if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['State']['Running'])) {
      // attempt graceful stop of container first
      stopContainer($existing);
    } else {
      // old container was stopped already, ensure newly created container doesn't start up automatically
      $startContainer = false;
    }
    // force kill container if still running after 10 seconds
    removeContainer($existing,1);
    // remove old template
    if (strtolower($filename) != strtolower("$userTmplDir/my-$existing.xml")) {
      @unlink("$userTmplDir/my-$existing.xml");
    }
  }
  // Extract real Entrypoint and Cmd from container for Tailscale
  if (isset($_POST['contTailscale']) && $_POST['contTailscale'] == 'on') {
    // Create preliminary base container but don't run it
    exec("/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/docker create --name '" . escapeshellarg($Name) . "' '" . escapeshellarg($Repository) . "'");
    // Get Entrypoint and Cmd from docker inspect
    $containerInfo = $DockerClient->getContainerDetails($Name);
    $ts_env  = isset($containerInfo['Config']['Entrypoint']) ? '-e ORG_ENTRYPOINT="' . implode(' ', $containerInfo['Config']['Entrypoint']) . '" ' : '';
    $ts_env .= isset($containerInfo['Config']['Cmd']) ? '-e ORG_CMD="' . implode(' ', $containerInfo['Config']['Cmd']) . '" ' : '';
    // Insert Entrypoint and Cmd to docker command
    $cmd = str_replace('-l net.unraid.docker.managed=dockerman', $ts_env . '-l net.unraid.docker.managed=dockerman' , $cmd);
    // Remove preliminary container
    exec("/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/docker rm '" . escapeshellarg($Name) . "'");
  }
  if ($startContainer) $cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
  execCommand($cmd);
  if ($startContainer) addRoute($Name); // add route for remote WireGuard access

  echo '<div style="text-align:center"><button type="button" onclick="openTerminal(\'docker\',\''.addslashes($Name).'\',\'.log\')">'._('View Container Log').'</button> <button type="button" onclick="done()">'._('Done').'</button></div><br>';
  goto END;
}

##########################
##   UPDATE CONTAINER   ##
##########################

if (isset($_GET['updateContainer'])){
  $echo = empty($_GET['mute']);
  if ($echo) {
    readfile("$docroot/plugins/dynamix.docker.manager/log.htm");
    @flush();
  }
  foreach ($_GET['ct'] as $value) {
    $tmpl = $DockerTemplates->getUserTemplate(unscript(urldecode($value)));
    if ($echo && !$tmpl) {
      echo "<script>addLog('<p>"._('Configuration not found').". "._('Was this container created using this plugin')."?</p>');</script>";
      @flush();
      continue;
    }
    $xml = file_get_contents($tmpl);
    [$cmd, $Name, $Repository] = xmlToCommand($tmpl);
    $Registry = getXmlVal($xml, "Registry");
    $ExtraParams = getXmlVal($xml, "ExtraParams");
    $Network = getXmlVal($xml, "Network");
    $TS_Enabled = getXmlVal($xml, "TailscaleEnabled");
    $oldImageID = $DockerClient->getImageID($Repository);
    // pull image
    if ($echo && !pullImage($Name, $Repository)) continue;
    $oldContainerInfo = $DockerClient->getContainerDetails($Name);
    // determine if the container is still running
    $startContainer = false;
    if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['State']['Running'])) {
      // since container was already running, put it back it to a running state after update
      $cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
      $startContainer = true;
      // attempt graceful stop of container first
      stopContainer($Name, false, $echo);
    }
    // check if network from another container is specified in xml (Network & ExtraParams)
    if (preg_match('/^container:(.*)/', $Network)) {
      $Net_Container = str_replace("container:", "", $Network);
    } else {
      preg_match("/--(net|network)=container:[^\s]+/", $ExtraParams, $NetworkParam);
      if (!empty($NetworkParam[0])) {
        $Net_Container = explode(':', $NetworkParam[0])[1];
        $Net_Container = str_replace(['"', "'"], '', $Net_Container);
      }
    }
    // check if the container still exists from which the network should be used, if it doesn't exist any more recreate container with network none and don't start it
    if (!empty($Net_Container)) {
      $Net_Container_ID = $DockerClient->getContainerID($Net_Container);
      if (empty($Net_Container_ID)) {
        $cmd = str_replace('/docker run -d ', '/docker create ', $cmd);
        $cmd = preg_replace("/--(net|network)=(['\"]?)container:[^'\"]+\\2/", "--network=none ", $cmd);
      }
    }
    // force kill container if still running after time-out
    if (empty($_GET['communityApplications'])) removeContainer($Name, $echo);
    // Extract real Entrypoint and Cmd from container for Tailscale
    if ($TS_Enabled == 'true') {
      // Create preliminary base container but don't run it
      exec("/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/docker create --name '" . escapeshellarg($Name) . "' '" . escapeshellarg($Repository) . "'");
      // Get Entrypoint and Cmd from docker inspect
      $containerInfo = $DockerClient->getContainerDetails($Name);
      $ts_env  = isset($containerInfo['Config']['Entrypoint']) ? '-e ORG_ENTRYPOINT="' . implode(' ', $containerInfo['Config']['Entrypoint']) . '" ' : '';
      $ts_env .= isset($containerInfo['Config']['Cmd']) ? '-e ORG_CMD="' . implode(' ', $containerInfo['Config']['Cmd']) . '" ' : '';
      // Insert Entrypoint and Cmd to docker command
      $cmd = str_replace('-l net.unraid.docker.managed=dockerman', $ts_env . '-l net.unraid.docker.managed=dockerman' , $cmd);
      // Remove preliminary container
      exec("/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/docker rm '" . escapeshellarg($Name) . "'");
    }
    execCommand($cmd, $echo);
    if ($startContainer) addRoute($Name); // add route for remote WireGuard access
    $DockerClient->flushCaches();
    $newImageID = $DockerClient->getImageID($Repository);
    // remove old orphan image since it's no longer used by this container
    if ($oldImageID && $oldImageID != $newImageID) removeImage($oldImageID, $echo);
  }
  echo '<div style="text-align:center"><button type="button" onclick="window.parent.jQuery(\'#iframe-popup\').dialog(\'close\')">'._('Done').'</button></div><br>';
  goto END;
}

#########################
##   REMOVE TEMPLATE   ##
#########################

if (isset($_POST['rmTemplate'])) {
  if (file_exists($_POST['rmTemplate']) && dirname($_POST['rmTemplate'])==$dockerManPaths['templates-user']) unlink($_POST['rmTemplate']);
}

#########################
##    LOAD TEMPLATE    ##
#########################

$xmlType = $xmlTemplate = '';
if (isset($_GET['xmlTemplate'])) {
  [$xmlType, $xmlTemplate] = my_explode(':', unscript(urldecode($_GET['xmlTemplate'])));
  if (is_file($xmlTemplate)) {
    $xml = xmlToVar($xmlTemplate);
    $templateName = $xml['Name'];
    if (preg_match('/^container:(.*)/', $xml['Network'])) {
      $xml['Network'] = explode(':', $xml['Network'], 2);
    }
    if ($xmlType == 'default') {
      if (!empty($dockercfg['DOCKER_APP_CONFIG_PATH']) && file_exists($dockercfg['DOCKER_APP_CONFIG_PATH'])) {
        // override /config
        foreach ($xml['Config'] as &$arrConfig) {
          if ($arrConfig['Type'] == 'Path' && strtolower($arrConfig['Target']) == '/config') {
            $arrConfig['Default'] = $arrConfig['Value'] = realpath($dockercfg['DOCKER_APP_CONFIG_PATH']).'/'.$xml['Name'];
            if (empty($arrConfig['Display']) || preg_match("/^Host Path\s\d/", $arrConfig['Name'])) {
              $arrConfig['Display'] = 'advanced-hide';
            }
            if (empty($arrConfig['Name']) || preg_match("/^Host Path\s\d/", $arrConfig['Name'])) {
              $arrConfig['Name'] = 'AppData Config Path';
            }
          }
          $arrConfig['Name'] = strip_tags(_var($arrConfig,'Name'));
          $arrConfig['Description'] = strip_tags(_var($arrConfig,'Description'));
          $arrConfig['Requires'] = strip_tags(_var($arrConfig,'Requires'));
        }
      }
      if (!empty($dockercfg['DOCKER_APP_UNRAID_PATH']) && file_exists($dockercfg['DOCKER_APP_UNRAID_PATH'])) {
        // override /unraid
        $boolFound = false;
        foreach ($xml['Config'] as &$arrConfig) {
          if ($arrConfig['Type'] == 'Path' && strtolower($arrConfig['Target']) == '/unraid') {
            $arrConfig['Default'] = $arrConfig['Value'] = realpath($dockercfg['DOCKER_APP_UNRAID_PATH']);
            $arrConfig['Display'] = 'hidden';
            $arrConfig['Name'] = 'Unraid Share Path';
            $boolFound = true;
          }
        }
        if (!$boolFound) {
          $xml['Config'][] = [
            'Name'        => 'Unraid Share Path',
            'Target'      => '/unraid',
            'Default'     => realpath($dockercfg['DOCKER_APP_UNRAID_PATH']),
            'Value'       => realpath($dockercfg['DOCKER_APP_UNRAID_PATH']),
            'Mode'        => 'rw',
            'Description' => '',
            'Type'        => 'Path',
            'Display'     => 'hidden',
            'Required'    => 'false',
            'Mask'        => 'false'
          ];
        }
      }
    }
    $xml['Overview'] = str_replace(['[', ']'], ['<', '>'], $xml['Overview']);
    $xml['Description'] = $xml['Overview'] = strip_tags(str_replace("<br>","\n", $xml['Overview']));
    echo "<script>var Settings=".json_encode($xml).";</script>";
  }
}
echo "<script>var Allocations=".json_encode(getAllocations()).";</script>";
$authoringMode = $dockercfg['DOCKER_AUTHORING_MODE'] == "yes" ? true : false;
$authoring     = $authoringMode ? 'advanced' : 'noshow';
$disableEdit   = $authoringMode ? 'false' : 'true';
$showAdditionalInfo = '';

$bgcolor = $themeHelper->isLightTheme() ? '#f2f2f2' : '#1c1c1c'; // $themeHelper set in DefaultPageLayout.php

# Search for existing TAILSCALE_ entries in the Docker template
$TS_existing_vars = false;
if (isset($xml["Config"]) && is_array($xml["Config"])) {
  foreach ($xml["Config"] as $config) {
    if (isset($config["Target"]) && strpos($config["Target"], "TAILSCALE_") === 0) {
      $TS_existing_vars = true;
      break;
    }
  }
}

# Try to detect port from WebUI and set webui_url
$TSwebuiport = '';
$webui_url = '';
if (empty($xml['TailscalePort'])) {
  if (!empty($xml['WebUI'])) {
    $webui_url = parse_url($xml['WebUI']);
    preg_match('/:(\d+)\]/', $webui_url['host'], $matches);
    $TSwebuiport = $matches[1];
  }
}

$TS_raw = [];
$TS_container_raw = [];
$TS_HostNameWarning = "";
$TS_HTTPSDisabledWarning = "";
$TS_ExitNodeNeedsApproval = false;
$TS_MachinesLink = "https://login.tailscale.com/admin/machines/";
$TS_DirectMachineLink = $TS_MachinesLink;
$TS_HostNameActual = "";
$TS_not_approved = "";
$TS_https_enabled = false;
$ts_exit_nodes = [];
$ts_en_check = false;
// Get Tailscale information and create arrays/variables
!empty($xml) && exec("docker exec -i " . escapeshellarg($xml['Name']) . " /bin/sh -c \"tailscale status --peers=false --json\"", $TS_raw);
$TS_no_peers = json_decode(implode('', $TS_raw),true);
$TS_container = json_decode(implode('', $TS_raw),true);
$TS_container = $TS_container['Self']??'';

# Look for Exit Nodes through Tailscale plugin (if installed) when container is not running
if (empty($TS_container) && file_exists('/usr/local/sbin/tailscale') && exec('pgrep --ns $$ -f "/usr/local/sbin/tailscaled"')) {
  exec('tailscale exit-node list', $ts_exit_node_list, $retval);
  if ($retval === 0) {
    foreach ($ts_exit_node_list as $line) {
      if (!empty(trim($line))) {
        if (preg_match('/^(\d+\.\d+\.\d+\.\d+)\s+(.+)$/', trim($line), $matches)) {
          $parts = preg_split('/\s+/', $matches[2]);
          $ts_exit_nodes[] = [
            'ip' => $matches[1],
            'hostname' => $parts[0],
            'country' => $parts[1],
            'city' => $parts[2],
            'status' => $parts[3]
          ];
          $ts_en_check = true;
        }
      }
    }
  }
}

if (!empty($TS_no_peers) && !empty($TS_container)) {
  // define the direct link to this machine on the Tailscale website
  if (!empty($TS_container['TailscaleIPs']) && !empty($TS_container['TailscaleIPs'][0])) {
    $TS_DirectMachineLink = $TS_MachinesLink.$TS_container['TailscaleIPs'][0];
  }
  // warn if MagicDNS or HTTPS is disabled
  if (isset($TS_no_peers['Self']['Capabilities']) && is_array($TS_no_peers['Self']['Capabilities'])) {
    $TS_https_enabled = in_array("https", $TS_no_peers['Self']['Capabilities'], true) ? true : false;
  }
  if (empty($TS_no_peers['CurrentTailnet']['MagicDNSEnabled']) || !$TS_no_peers['CurrentTailnet']['MagicDNSEnabled'] || $TS_https_enabled !== true) {
    $TS_HTTPSDisabledWarning = "<span><b><a href='https://tailscale.com/kb/1153/enabling-https' target='_blank'>Enable HTTPS</a> on your Tailscale account to use Tailscale Serve/Funnel.</b></span>";
  }
  // In $TS_container, 'HostName' is what the user requested, need to parse 'DNSName' to find the actual HostName in use
  $TS_DNSName = _var($TS_container,'DNSName','');
  $TS_HostNameActual = substr($TS_DNSName, 0, strpos($TS_DNSName, '.'));
  // compare the actual HostName in use to the one in the XML file
  if (strcasecmp($TS_HostNameActual, _var($xml, 'TailscaleHostname')) !== 0 && !empty($TS_DNSName)) {
    // they are different, show a warning
    $TS_HostNameWarning = "<span><b>Warning: the actual Tailscale hostname is '".$TS_HostNameActual."'</b></span>";
  }
  // If this is an Exit Node, show warning if it still needs approval
  if (_var($xml,'TailscaleIsExitNode') == 'true' && _var($TS_container, 'ExitNodeOption') === false) {
    $TS_ExitNodeNeedsApproval = true;
  }
  //Check for key expiry
  if(!empty($TS_container['KeyExpiry'])) {
    $TS_expiry = new DateTime($TS_container['KeyExpiry']);
    $current_Date = new DateTime();
    $TS_expiry_diff = $current_Date->diff($TS_expiry);
  }
  // Check for non approved routes
  if(!empty($xml['TailscaleRoutes'])) {
    $TS_advertise_routes = str_replace(' ', '', $xml['TailscaleRoutes']);
    if (empty($TS_container['PrimaryRoutes'])) {
      $TS_container['PrimaryRoutes'] = [];
    }
    $routes = explode(',', $TS_advertise_routes);
    foreach ($routes as $route) {
      if (!in_array($route, $TS_container['PrimaryRoutes'])) {
        $TS_not_approved .= " " . $route;
      }
    }
  }
  // Check for exit nodes if ts_en_check was not already done
  if (!$ts_en_check) {
    exec("docker exec -i ".$xml['Name']." /bin/sh -c \"tailscale exit-node list\"", $ts_exit_node_list, $retval);
    if ($retval === 0) {
      foreach ($ts_exit_node_list as $line) {
        if (!empty(trim($line))) {
          if (preg_match('/^(\d+\.\d+\.\d+\.\d+)\s+(.+)$/', trim($line), $matches)) {
            $parts = preg_split('/\s+/', $matches[2]);
            $ts_exit_nodes[] = [
              'ip' => $matches[1],
              'hostname' => $parts[0],
              'country' => $parts[1],
              'city' => $parts[2],
              'status' => $parts[3]
            ];
          }
        }
      }
    }
  }
  // Construct WebUI URL on container template page
  // Check if webui_url, Tailscale WebUI and MagicDNS are not empty and make sure that MagicDNS is enabled
  if ( !empty($webui_url) && !empty($xml['TailscaleWebUI']) && (!empty($TS_no_peers['CurrentTailnet']['MagicDNSEnabled']) || ($TS_no_peers['CurrentTailnet']['MagicDNSEnabled']??false))) {
    // Check if serve or funnel are enabled by checking for [hostname] and replace string with TS_DNSName
    if (!empty($xml['TailscaleWebUI']) && strpos($xml['TailscaleWebUI'], '[hostname]') !== false && isset($TS_DNSName)) {
      $TS_webui_url = str_replace("[hostname][magicdns]", rtrim($TS_DNSName, '.'), $xml['TailscaleWebUI']);
      $TS_webui_url = preg_replace('/\[IP\]/', rtrim($TS_DNSName, '.'), $TS_webui_url);
      $TS_webui_url = preg_replace('/\[PORT:(\d{1,5})\]/', '443', $TS_webui_url);
    // Check if serve is disabled, construct url with port, path and query if present and replace [noserve] with url
    } elseif (strpos($xml['TailscaleWebUI'], '[noserve]') !== false && isset($TS_container['TailscaleIPs'])) {
      $ipv4 = '';
      foreach ($TS_container['TailscaleIPs'] as $ip) {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
          $ipv4 = $ip;
          break;
        }
      }
      if (!empty($ipv4)) {
        $webui_url = isset($xml['WebUI']) ? parse_url($xml['WebUI']) : '';
        $webui_port = (preg_match('/\[PORT:(\d+)\]/', $xml['WebUI'], $matches)) ? ':' . $matches[1] : '';
        $webui_path = $webui_url['path'] ?? '';
        $webui_query = isset($webui_url['query']) ? '?' . $webui_url['query'] : '';
        $webui_query = preg_replace('/\[IP\]/', $ipv4, $webui_query);
        $webui_query = preg_replace('/\[PORT:(\d{1,5})\]/', ltrim($webui_port, ':'), $webui_query);
        $TS_webui_url = 'http://' . $ipv4 . $webui_port . $webui_path . $webui_query;
      }
    // Check if TailscaleWebUI in the xml is custom and display instead
    } elseif (strpos($xml['TailscaleWebUI'], '[hostname]') === false && strpos($xml['TailscaleWebUI'], '[noserve]') === false) {
      $TS_webui_url = $xml['TailscaleWebUI'];
    }
  }
}
?>
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.switchbutton.css")?>">
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.filetree.css")?>">
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/sheets/CreateDocker.css")?>">

<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
<script src="<?autov('/webGui/javascript/jquery.filetree.js')?>" charset="utf-8"></script>
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/dynamix.vm.manager.js')?>"></script>
<script src="<?autov('/plugins/dynamix.docker.manager/javascript/markdown.js')?>"></script>
<script>
var confNum = 0;
var drivers = {};
<?foreach ($driver as $d => $v) echo "drivers['$d']='$v';\n";?>

if (!Array.prototype.forEach) {
  Array.prototype.forEach = function(fn, scope) {
    for (var i = 0, len = this.length; i < len; ++i) fn.call(scope, this[i], i, this);
  };
}
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined' ? args[number] : match;
    });
  };
}
if (!String.prototype.replaceAll) {
  String.prototype.replaceAll = function(str1, str2, ignore) {
    return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
  };
}
// Create config nodes using templateDisplayConfig
function makeConfig(opts) {
  confNum += 1;
  var icons = {'Path':'folder-o', 'Port':'minus-square-o', 'Variable':'file-text-o', 'Label':'tags', 'Device':'play-circle-o'};
  var newConfig = $("#templateDisplayConfig").html();
  newConfig =  newConfig.format(
    stripTags(opts.Name),
    opts.Target,
    opts.Default,
    opts.Mode,
    opts.Description,
    opts.Type,
    opts.Display,
    opts.Required,
    opts.Mask,
    escapeQuote(opts.Value),
    opts.Buttons,
    opts.Required=='true' ? 'required' : '',
    sprintf('Container %s',opts.Type),
    icons[opts.Type] || 'question'
  );
  newConfig = "<div id='ConfigNum"+opts.Number+"' class='config_"+opts.Display+"'' >"+newConfig+"</div>";
  newConfig = $($.parseHTML(newConfig));
  value     = newConfig.find("input[name='confValue[]']");
  if (opts.Type == "Path") {
    value.attr("onclick", "openFileBrowser(this,$(this).val(),$(this).val(),'',true,false);");
  } else if (opts.Type == "Device") {
    value.attr("onclick", "openFileBrowser(this,'/dev','/dev','',true,true);")
  } else if (opts.Type == "Variable" && opts.Default.split("|").length > 1) {
    var valueOpts = opts.Default.split("|");
    var newValue = "<select name='confValue[]' class='selectVariable' default='"+valueOpts[0]+"'>";
    for (var i = 0; i < valueOpts.length; i++) {
      newValue += "<option value='"+valueOpts[i]+"' "+(opts.Value == valueOpts[i] ? "selected" : "")+">"+valueOpts[i]+"</option>";
    }
    newValue += "</select>";
    value.replaceWith(newValue);
  } else if (opts.Type == "Port") {
    value.addClass("numbersOnly");
  }
  if (opts.Mask == "true") {
    value.prop("autocomplete","new-password");
    value.prop("type", "password");
  }
  return newConfig.prop('outerHTML');
}

function stripTags(string) {
  return string.replace(/(<([^>]+)>)/ig,"");
}

function escapeQuote(string) {
  return string.replace(new RegExp('"','g'),"&quot;");
}

function makeAllocations(container,current) {
  var html = [];
  for (var i=0,ct; ct=container[i]; i++) {
    var highlight = ct.Name.toLowerCase()==current.toLowerCase() ? "font-weight:bold" : "";
    html.push($("#templateAllocations").html().format(highlight,ct.Name,ct.Port));
  }
  return html.join('');
}

function getVal(el, name) {
  var el = $(el).find("*[name="+name+"]");
  if (el.length) {
    return ($(el).attr('type') == 'checkbox') ? ($(el).is(':checked') ? "on" : "off") : $(el).val();
  } else {
    return "";
  }
}

function dialogStyle() {
  $('.ui-dialog-titlebar-close').css({'display':'none'});
  $('.ui-dialog-title').css({'text-align':'center','width':'100%','font-size':'1.8rem'});
  $('.ui-dialog-content').css({'padding-top':'15px','vertical-align':'bottom'});
  $('.ui-button-text').css({'padding':'0px 5px'});
}

function addConfigPopup() {
  var title = "_(Add Configuration)_";
  var popup = $("#dialogAddConfig");

  // Load popup the popup with the template info
  popup.html($("#templatePopupConfig").html());

  // Add switchButton to checkboxes
  popup.find(".switch").switchButton({labels_placement:"right",on_label:"_(Yes)_",off_label:"_(No)_"});
  popup.find(".switch-button-background").css("margin-top", "6px");

  // Load Mode field if needed and enable field
  toggleMode(popup.find("*[name=Type]:first"),false);

  // Start Dialog section
  popup.dialog({
    title: title,
    height: 'auto',
    width: 'auto',
    resizable: false,
    modal: true,
    buttons: {
    "_(Add)_": function() {
        $(this).dialog("close");
        confNum += 1;
        var Opts = Object;
        var Element = this;
        ["Name","Target","Default","Mode","Description","Type","Display","Required","Mask","Value"].forEach(function(e){
          Opts[e] = getVal(Element, e);
        });
        if (!Opts.Name){
          Opts.Name = makeName(Opts.Type);
        }

        if (Opts.Required == "true") {
          Opts.Buttons  = "<span class='advanced'><button type='button' onclick='editConfigPopup("+confNum+",false)'>_(Edit)_</button>";
          Opts.Buttons += "<button type='button' onclick='removeConfig("+confNum+")'>_(Remove)_</button></span>";
        } else {
          Opts.Buttons  = "<button type='button' onclick='editConfigPopup("+confNum+",false)'>_(Edit)_</button>";
          Opts.Buttons += "<button type='button' onclick='removeConfig("+confNum+")'>_(Remove)_</button>";
        }
        Opts.Number = confNum;
        if (Opts.Type == "Device") {
          Opts.Target = Opts.Value;
        }
        newConf = makeConfig(Opts);
        $("#configLocation").append(newConf);
        reloadTriggers();
        $('input[name="contName"]').trigger('change'); // signal change
      },
    "_(Cancel)_": function() {
        $(this).dialog("close");
      }
    }
  });
  dialogStyle();
}

function editConfigPopup(num,disabled) {
  var title = "_(Edit Configuration)_";
  var popup = $("#dialogAddConfig");

  // Load popup the popup with the template info
  popup.html($("#templatePopupConfig").html());

  // Load existing config info
  var config = $("#ConfigNum"+num);
  config.find("input").each(function(){
    var name = $(this).attr("name").replace("conf", "").replace("[]", "");
    popup.find("*[name='"+name+"']").val($(this).val());
  });

  // Hide passwords if needed
  if (popup.find("*[name='Mask']").val() == "true") {
    popup.find("*[name='Value']").prop("type", "password");
  }

  // Load Mode field if needed
  var mode = config.find("input[name='confMode[]']").val();
  toggleMode(popup.find("*[name=Type]:first"),disabled);
  popup.find("*[name=Mode]:first").val(mode);

  // Add switchButton to checkboxes
  popup.find(".switch").switchButton({labels_placement:"right",on_label:"_(Yes)_",off_label:"_(No)_"});

  // Start Dialog section
  popup.find(".switch-button-background").css("margin-top", "6px");
  popup.dialog({
    title: title,
    height: 'auto',
    width: 'auto',
    resizable: false,
    modal: true,
    buttons: {
    "_(Save)_": function() {
        $(this).dialog("close");
        var Opts = Object;
        var Element = this;
        ["Name","Target","Default","Mode","Description","Type","Display","Required","Mask","Value"].forEach(function(e){
          Opts[e] = getVal(Element, e);
        });
        if (Opts.Display == "always-hide" || Opts.Display == "advanced-hide") {
          Opts.Buttons  = "<span class='advanced'><button type='button' onclick='editConfigPopup("+num+",<?=$disableEdit?>)'>_(Edit)_</button>";
          Opts.Buttons += "<button type='button' onclick='removeConfig("+num+")'>_(Remove)_</button></span>";
        } else {
          Opts.Buttons  = "<button type='button' onclick='editConfigPopup("+num+",<?=$disableEdit?>)'>_(Edit)_</button>";
          Opts.Buttons += "<button type='button' onclick='removeConfig("+num+")'>_(Remove)_</button>";
        }
        if (!Opts.Name){
          Opts.Name = makeName(Opts.Type);
        }

        Opts.Number = num;
        if (Opts.Type == "Device") {
          Opts.Target = Opts.Value;
        }
        newConf = makeConfig(Opts);
        if (config.hasClass("config_"+Opts.Display)) {
          config.html(newConf);
          config.removeClass("config_always config_always-hide config_advanced config_advanced-hide").addClass("config_"+Opts.Display);
        } else {
          config.remove();
          if (Opts.Display == 'advanced' || Opts.Display == 'advanced-hide') {
            $("#configLocationAdvanced").append(newConf);
          } else {
            $("#configLocation").append(newConf);
          }
        }
       reloadTriggers();
        $('input[name="contName"]').trigger('change'); // signal change
      },
    "_(Cancel)_": function() {
        $(this).dialog("close");
      }
    }
  });
  dialogStyle();
  $('.desc_readmore').readmore({maxHeight:10});
}

function removeConfig(num) {
  $('#ConfigNum'+num).fadeOut("fast", function() {$(this).remove();});
  $('input[name="contName"]').trigger('change'); // signal change
}

function prepareConfig(form) {
  var types = [], values = [], targets = [], vcpu = [];
  if ($('select[name="contNetwork"]').val()=='host') {
    $(form).find('input[name="confType[]"]').each(function(){types.push($(this).val());});
    $(form).find('input[name="confValue[]"]').each(function(){values.push($(this));});
    $(form).find('input[name="confTarget[]"]').each(function(){targets.push($(this));});
    for (var i=0; i < types.length; i++) if (types[i]=='Port') $(targets[i]).val($(values[i]).val());
  }
  $(form).find('input[id^="box"]').each(function(){if ($(this).prop('checked')) vcpu.push($('#'+$(this).prop('id').replace('box','cpu')).text());});
  form.contCPUset.value = vcpu.join(',');
}

function makeName(type) {
  var i = $("#configLocation input[name^='confType'][value='"+type+"']").length+1;
  return "Host "+type.replace('Variable','Key')+" "+i;
}

function toggleMode(el,disabled) {
  var div        = $(el).closest('div');
  var targetDiv  = div.find('#Target');
  var valueDiv   = div.find('#Value');
  var defaultDiv = div.find('#Default');
  var mode       = div.find('#Mode');
  var value      = valueDiv.find('input[name=Value]');
  var target     = targetDiv.find('input[name=Target]');
  var driver     = drivers[$('select[name="contNetwork"]')[0].value];
  value.unbind();
  target.unbind();
  valueDiv.css('display', '');
  defaultDiv.css('display', '');
  targetDiv.css('display', '');
  mode.html('');
  $(el).prop('disabled',disabled);
  switch ($(el)[0].selectedIndex) {
  case 0: // Path
    mode.html("<dl><dt>_(Access Mode)_:</dt><dd><select name='Mode'><option value='rw'>_(Read/Write)_</option><option value='rw,slave'>_(Read/Write - Slave)_</option><option value='rw,shared'>_(Read/Write - Shared)_</option><option value='ro'>_(Read Only)_</option><option value='ro,slave'>_(Read Only - Slave)_</option><option value='ro,shared'>_(Read Only - Shared)_</option></select></dd></dl>");
    value.bind("click", function(){openFileBrowser(this,$(this).val(),$(this).val(),'',true,false);});
    targetDiv.find('#dt1').text("_(Container Path)_");
    valueDiv.find('#dt2').text("_(Host Path)_");
    break;
  case 1: // Port
    mode.html("<dl><dt>_(Connection Type)_:</dt><dd><select name='Mode'><option value='tcp'>_(TCP)_</option><option value='udp'>_(UDP)_</option></select></dd></dl>");
    value.addClass("numbersOnly");
    if (driver=='bridge') {
      if (target.val()) target.prop('disabled',<?=$disableEdit?>); else target.addClass("numbersOnly");
      targetDiv.find('#dt1').text("_(Container Port)_");
      targetDiv.show();
    } else {
      targetDiv.hide();
    }
    if (driver!='null') {
      valueDiv.find('#dt2').text("_(Host Port)_");
      valueDiv.show();
    } else {
      valueDiv.hide();
      mode.html('');
    }
    break;
  case 2: // Variable
    targetDiv.find('#dt1').text("_(Key)_");
    valueDiv.find('#dt2').text("_(Value)_");
    break;
  case 3: // Label
    targetDiv.find('#dt1').text("_(Key)_");
    valueDiv.find('#dt2').text("_(Value)_");
    break;
  case 4: // Device
    targetDiv.hide();
    defaultDiv.hide();
    valueDiv.find('#dt2').text("_(Value)_");
    value.bind("click", function(){openFileBrowser(this,'/dev','/dev','',true,true);});
    break;
  }
  reloadTriggers();
}

function loadTemplate(el) {
  var template = $(el).val();
  if (template.length) {
    $('#formTemplate').find("input[name='xmlTemplate']").val(template);
    $('#formTemplate').submit();
  }
}

function rmTemplate(tmpl) {
  var name = tmpl.split(/[\/]+/).pop();
  swal({title:"_(Are you sure)_?",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
}

function openFileBrowser(el, top, root, filter, on_folders, on_files, close_on_select) {
  if (on_folders === undefined) on_folders = true;
  if (on_files   === undefined) on_files = true;
  if (!filter && !on_files) filter = 'HIDE_FILES_FILTER';
  if (!root.trim()) {root = "/mnt/user/"; top = "/mnt/";}
  p = $(el);
  // Skip if fileTree is already open
  if (p.next().hasClass('fileTree')) return null;
  // create a random id
  var r = Math.floor((Math.random()*10000)+1);
  // Add a new span and load fileTree
  p.after("<span id='fileTree"+r+"' class='textarea fileTree'></span>");
  var ft = $('#fileTree'+r);
  ft.fileTree({top:top, root:root, filter:filter, allowBrowsing:true},
    function(file){if(on_files){p.val(file);p.trigger('change');if(close_on_select){ft.slideUp('fast',function(){ft.remove();});}}},
    function(folder){if(on_folders){p.val(folder.replace(/\/\/+/g,'/'));p.trigger('change');if(close_on_select){$(ft).slideUp('fast',function(){$(ft).remove();});}}}
  );
  // Format fileTree according to parent position, height and width
  ft.css({'left':p.position().left,'top':(p.position().top+p.outerHeight()),'width':(p.width())});
  // close if click elsewhere
  $(document).mouseup(function(e){if(!ft.is(e.target) && ft.has(e.target).length === 0){ft.slideUp('fast',function(){$(ft).remove();});}});
  // close if parent changed
  p.bind("keydown", function(){ft.slideUp('fast', function(){$(ft).remove();});});
  // Open fileTree
  ft.slideDown('fast');
}

function resetField(el) {
  var target = $(el).prev();
  reset = target.attr("default");
  if (reset.length) target.val(reset);
}

function prepareCategory() {
  var values = $.map($('#catSelect option'),function(option) {
    if ($(option).is(":selected")) return option.value;
  });
  $("input[name='contCategory']").val(values.join(" "));
}

$(function() {
  var ctrl = "<span class='status'><input type='checkbox' class='advancedview'></span>";
<?if ($tabbed):?>
  $('.tabs').append(ctrl);
<?else:?>
  $('div[class=title] .right').append(ctrl);
<?endif;?>
  $('.advancedview').switchButton({labels_placement:'left', on_label: "_(Advanced View)_", off_label: "_(Basic View)_"});
  $('.advancedview').change(function() {
    var status = $(this).is(':checked');
    toggleRows('advanced', status, 'basic');
    load_contOverview();
    $("#catSelect").dropdownchecklist("destroy");
    $("#catSelect").dropdownchecklist({emptyText:"_(Select categories)_...", maxDropHeight:200, width:300, explicitClose:"..._(close)_"});
  });
});
</script>

<?php
if (isset($xml["Config"])) {
  foreach ($xml["Config"] as $config) {
    if (isset($config["Target"]) && is_array($config) && strpos($config["Target"], "TAILSCALE_") === 0) {
      $tailscaleTargetFound = true;
      break;
    }
  }
}
?>

<div id="canvas">
<form markdown="1" method="POST" autocomplete="off" onsubmit="prepareConfig(this)">
<input type="hidden" name="csrf_token" value="<?=$var['csrf_token']?>">
<input type="hidden" name="contCPUset" value="">
<?if ($xmlType=='edit'):?>
<?if ($DockerClient->doesContainerExist($templateName)):?>
<input type="hidden" name="existingContainer" value="<?=$templateName?>">
<?endif;?>
<?else:?>
<div markdown="1" class="TemplateDropDown">
_(Template)_:
: <select id="TemplateSelect" onchange="loadTemplate(this);">
  <?echo mk_option(0,"",_('Select a template'));
  $rmadd = '';
  $templates = [];
  $templates['default'] = $DockerTemplates->getTemplates('default');
  $templates['user'] = $DockerTemplates->getTemplates('user');
  foreach ($templates as $section => $template) {
    $title = ucfirst($section)." templates";
    printf("<optgroup class='title bold' label='[ %s ]'>", htmlspecialchars($title));
    foreach ($template as $value){
      if ( $value['name'] == "my-ca_profile" || $value['name'] == "ca_profile" ) continue;
      $name = str_replace('my-', '', $value['name']);
      $selected = (isset($xmlTemplate) && $value['path']==$xmlTemplate) ? ' selected ' : '';
      if ($selected && $section=='default') $showAdditionalInfo = 'advanced';
      if ($selected && $section=='user') $rmadd = $value['path'];
      printf("<option class='list' value='%s:%s' $selected>%s</option>", htmlspecialchars($section), htmlspecialchars($value['path']), htmlspecialchars($name));
    }
    if (!$template) echo("<option class='list' disabled>&lt;"._('None')."&gt;</option>");
    printf("</optgroup>");
  }
  ?></select><?if ($rmadd):?><i class="fa fa-window-close button" title="<?=htmlspecialchars($rmadd)?>" onclick="rmTemplate('<?=addslashes(htmlspecialchars($rmadd))?>')"></i><?endif;?>

:docker_client_general_help:

</div>
<?endif;?>

<div markdown="1" class="<?=$showAdditionalInfo?>">
_(Name)_:
: <input type="text" name="contName" pattern="[a-zA-Z0-9][a-zA-Z0-9_.\-]+" required>

:docker_client_name_help:

</div>
<div markdown="1" class="basic">
_(Overview)_:
: <span id="contDescription" class="boxed blue-text"></span>

</div>
<div markdown="1" class="advanced">
_(Overview)_:
: <textarea name="contOverview" spellcheck="false" cols="80" rows="15" style="width:56%"></textarea>

:docker_client_overview_help:

</div>
<div markdown="1" class="basic">
_(Additional Requirements)_:
: <span id="contRequires" class="boxed blue-text"></span>

</div>
<div markdown="1" class="advanced">
_(Additional Requirements)_:
: <textarea name="contRequires" spellcheck="false" cols="80" Rows="3" style="width:56%"></textarea>

:docker_client_additional_requirements_help:

</div>

<div markdown="1" class="<?=$showAdditionalInfo?>">
_(Repository)_:
: <input type="text" name="contRepository" required>

:docker_client_repository_help:

</div>
<div markdown="1" class="<?=$authoring?>">
_(Categories)_:
: <input type="hidden" name="contCategory">
  <select id="catSelect" size="1" multiple="multiple" style="display:none" onchange="prepareCategory();">
  <optgroup label="_(Categories)_">
  <option value="AI:">_(AI)_</option>
  <option value="Backup:">_(Backup)_</option>
  <option value="Cloud:">_(Cloud)_</option>
  <option value="Crypto:">_(Crypto Currency)_</option>
  <option value="Downloaders:">_(Downloaders)_</option>
  <option value="Drivers:">_(Drivers)_</option>
  <option value="GameServers:">_(Game Servers)_</option>
  <option value="HomeAutomation:">_(Home Automation)_</option>
  <option value="Productivity:">_(Productivity)_</option>
  <option value="Security:">_(Security)_</option>
  <option value="Tools:">_(Tools)_</option>
  <option value="Other:">_(Other)_</option>
  </optgroup>
  <optgroup label="_(MediaApp)_">
  <option value="MediaApp:Video">_(MediaApp)_:_(Video)_</option>
  <option value="MediaApp:Music">_(MediaApp)_:_(Music)_</option>
  <option value="MediaApp:Books">_(MediaApp)_:_(Books)_</option>
  <option value="MediaApp:Photos">_(MediaApp)_:_(Photos)_</option>
  <option value="MediaApp:Other">_(MediaApp)_:_(Other)_</option>
  </optgroup>
  <optgroup label="_(MediaServer)_">
  <option value="MediaServer:Video">_(MediaServer)_:_(Video)_</option>
  <option value="MediaServer:Music">_(MediaServer)_:_(Music)_</option>
  <option value="MediaServer:Books">_(MediaServer)_:_(Books)_</option>
  <option value="MediaServer:Photos">_(MediaServer)_:_(Photos)_</option>
  <option value="MediaServer:Other">_(MediaServer)_:_(Other)_</option>
  </optgroup>
  <optgroup label="_(Network)_">
  <option value="Network:Web">_(Network)_:_(Web)_</option>
  <option value="Network:DNS">_(Network)_:_(DNS)_</option>
  <option value="Network:FTP">_(Network)_:_(FTP)_</option>
  <option value="Network:Proxy">_(Network)_:_(Proxy)_</option>
  <option value="Network:Voip">_(Network)_:_(Voip)_</option>
  <option value="Network:Management">_(Network)_:_(Management)_</option>
  <option value="Network:Messenger">_(Network)_:_(Messenger)_</option>
  <option value="Network:VPN">_(Network)_:_(VPN)_</option>
  <option value="Network:Privacy">_(Network)_:_(Privacy)_</option>
  <option value="Network:Other">_(Network)_:_(Other)_</option>
  </optgroup>
  <optgroup label="_(Development Status)_">
  <option value="Status:Stable">_(Status)_:_(Stable)_</option>
  <option value="Status:Beta">_(Status)_:_(Beta)_</option>
  </optgroup>
  </select>

_(Support Thread)_:
: <input type="text" name="contSupport">

:docker_client_support_thread_help:

_(Project Page)_:
: <input type="text" name="contProject">

:docker_client_project_page_help:

_(Read Me First)_:
: <input type="text" name="contReadMe">

:docker_client_readme_help:

</div>
<div markdown="1" class="advanced">
_(Registry URL)_:
: <input type="text" name="contRegistry"></td>

:docker_client_hub_url_help:

</div>
<div markdown="1" class="noshow"> <!-- Deprecated for author to enter or change, but needs to be present -->
Donation Text:
: <input type="text" name="contDonateText">

Donation Link:
: <input type="text" name="contDonateLink">

Template URL:
: <input type="text" name="contTemplateURL">

</div>
<div markdown="1" class="advanced">
_(Icon URL)_:
: <input type="text" name="contIcon">

:docker_client_icon_url_help:

_(WebUI)_:
: <input type="text" name="contWebUI">

:docker_client_webui_help:

_(Extra Parameters)_:
: <input type="text" name="contExtraParams">

:docker_extra_parameters_help:

_(Post Arguments)_:
: <input type="text" name="contPostArgs">

:docker_post_arguments_help:

_(CPU Pinning)_:
: <span style="display:inline-block"><?cpu_pinning()?></span>

:docker_cpu_pinning_help:

</div>
_(Network Type)_:
: <select name="contNetwork" onchange="showSubnet(this.value)">
  <?=mk_option(1,'bridge',_('Bridge'))?>
  <?=mk_option(1,'host',_('Host'))?>
  <?=mk_option(1,'container',_('Container'))?>
  <?=mk_option(1,'none',_('None'))?>
  <?foreach ($custom as $network):?>
  <?$name = $network;
  if (preg_match('/^(br|bond|eth)[0-9]+(\.[0-9]+)?$/',$network)) {
    [$eth,$x] = my_explode('.',$network);
    $eth = str_replace(['br','bond'],'eth',$eth);
    $n = $x ? 1 : 0; while (isset($$eth["VLANID:$n"]) && $$eth["VLANID:$n"] != $x) $n++;
    if (!empty($$eth["DESCRIPTION:$n"])) $name .= ' -- '.compress(trim($$eth["DESCRIPTION:$n"]));
  } elseif (preg_match('/^wg[0-9]+$/',$network)) {
    $conf = is_file("/etc/wireguard/$network.conf") ? file("/etc/wireguard/$network.conf") : [];
    if ( ($conf[1][0]??'')=='#') $name .= ' -- '.compress(trim(substr($conf[1],1)));
  } elseif (substr($network,0,4)=='wlan') {
    $name .= '  -- '._('Wireless interface');
  }
  ?>
  <?=mk_option(1,$network,_('Custom')." : $name")?>
  <?endforeach;?></select>

<div markdown="1" class="myIP noshow">
_(Fixed IP address)_ (_(optional)_):
: <input type="text" name="contMyIP"><span id="myIP"></span>

:docker_fixed_ip_help:

</div>

<div markdown="1" class="netCONT noshow">
_(Container Network)_:
: <select name="netCONT" id="netCONT">
  <?php
  $container_name = !empty($xml['Name']) ? $xml['Name'] : '';
  foreach ($DockerClient->getDockerContainers() as $ct) {
    if ($ct['Name'] !== $container_name) {
      $list[] = $ct['Name'];
      echo mk_option($ct['Name'], $ct['Name'], $ct['Name']);
    }
  }
  ?>
</select>

:docker_container_network_help:

</div>

<div markdown="1" class="TSdivider noshow"><hr></div>

<?if ($TS_existing_vars == 'true'):?>
<div markdown="1" class="TSwarning noshow">
<b style="color:red;">_(WARNING)_</b>:
:  <b>_(Existing TAILSCALE variables found, please remove any existing modifications in the Template for Tailscale before using this function!)_</b>
</div>
<?endif;?>

<?if (empty($xml['TailscaleEnabled'])):?>
<div markdown="1" class="TSdeploy noshow">
<b>_(First deployment)_</b>:
:  <p>_(After deploying the container, open the log and follow the link to register the container to your Tailnet!)_</p>
</div>

<?if (!file_exists('/usr/local/sbin/tailscale')):?>
<div markdown="1" class="TSdeploy noshow">
<b>_(Recommendation)_</b>:
:  <p>_(For the best experience with Tailscale, install "Tailscale (Plugin)" from)_ <a href="/Apps?search=Tailscale%20(Plugin)" target='_blank'> Community Applications</a>.</p>
</div>
<?endif;?>

<?endif;?>

<div markdown="1" class='TSNetworkAllowed'>
_(Use Tailscale)_:
: <span class="flex flex-row items-center">
    <input type="checkbox" class="switch-on-off" name="contTailscale" id="contTailscale" <?php if (!empty($xml['TailscaleEnabled']) && $xml['TailscaleEnabled'] == 'true') echo 'checked'; ?> onchange="showTailscale(this)">
  </span>

:docker_tailscale_help:

</div>

<div markdown="1" class='TSNetworkNotAllowed'>
_(Use Tailscale)_:
: _(Option disabled as Network type is not bridge or custom)_

:docker_tailscale_help:

</div>
<div markdown="1" class="TSdivider noshow">
<b>_(NOTE)_</b>:
:  <i>_(This option will install Tailscale and dependencies into the container.)_</i>
</div>

<?if($TS_ExitNodeNeedsApproval):?>
<div markdown="1" class="TShostname noshow">
<b>_(Warning)_</b>:
: Exit Node not yet approved. Navigate to the <a href="<?=$TS_DirectMachineLink?>" target='_blank'>Tailscale website</a> and approve it.
</div>
<?endif;?>

<?if(!empty($TS_expiry_diff)):?>
<div markdown="1" class="TSdivider noshow">
<?if($TS_expiry_diff->invert):?>
<b>_(Warning)_</b>:
: <span><b>Tailscale Key expired!</b> <a href="<?=$TS_MachinesLink?>" target='_blank'>Renew/Disable key expiry</a> for '<b><?=$TS_HostNameActual?></b>'.</span>
<?else:?>
<b>_(Warning)_</b>:
: <span>Tailscale Key will expire in <b><?=$TS_expiry_diff->days?> days</b>! <a href="<?=$TS_MachinesLink?>" target='_blank'>Disable Key Expiry</a> for '<b><?=$TS_HostNameActual?></b>'.</span>
<?endif;?>
<label>See <a href="https://tailscale.com/kb/1028/key-expiry" target='_blank'>key-expiry</a>.</label>
</div>
<?endif;?>

<?if(!empty($TS_not_approved)):?>
<div markdown="1" class="TSdivider noshow">
<b>_(Warning)_</b>:
: <span>The following route(s) are not approved: <b><?=trim($TS_not_approved)?></b></span>
</div>
<?endif;?>

<div markdown="1" class="TShostname noshow">
_(Tailscale Hostname)_:
: <input type="text" pattern="[A-Za-z0-9_\-]*" name="TShostname" <?php if (!empty($xml['TailscaleHostname'])) echo 'value="' . $xml['TailscaleHostname'] . '"'; ?> placeholder="_(Hostname for the container)_"> <?=$TS_HostNameWarning?>

:docker_tailscale_hostname_help:

</div>

<div markdown="1" class="TSisexitnode noshow">
_(Be a Tailscale Exit Node)_:
: <select name="TSisexitnode" id="TSisexitnode" onchange="showTailscale(this)">
    <?=mk_option(1,'false',_('No'))?>
    <?=mk_option(1,'true',_('Yes'))?>
  </select>
  <span id='TSisexitnode_msg' style='font-style: italic;'></span>

:docker_tailscale_be_exitnode_help:

</div>

<div markdown="1" class="TSexitnodeip noshow">
_(Use a Tailscale Exit Node)_:
: <?if($ts_en_check !== true && empty($ts_exit_nodes)):?>
<input type="text" name="TSexitnodeip" <?php if (!empty($xml['TailscaleExitNodeIP'])) echo 'value="' . $xml['TailscaleExitNodeIP'] . '"'; ?> placeholder="_(IP/Hostname from Exit Node)_" onchange="processExitNodeoptions(this)">
<?else:?>
<select name="TSexitnodeip" id="TSexitnodeip" onchange="processExitNodeoptions(this)">
  <?=mk_option(1,'',_('None'))?>
  <?foreach ($ts_exit_nodes as $ts_exit_node):?>
    <?=$node_offline = $ts_exit_node['status'] === 'offline' ? ' - OFFLINE' : '';?>
    <?=mk_option(1,$ts_exit_node['ip'],$ts_exit_node['ip'] . ' - ' . $ts_exit_node['hostname'] . $node_offline)?>
  <?endforeach;?></select>
<?endif;?>
  </select>
  <span id='TSexitnodeip_msg' style='font-style: italic;'></span>

:docker_tailscale_exitnode_ip_help:

</div>

<div markdown="1" class="TSallowlanaccess noshow">
_(Tailscale Allow LAN Access)_:
: <select name="TSallowlanaccess" id="TSallowlanaccess">
    <?=mk_option(1,'false',_('No'))?>
    <?=mk_option(1,'true',_('Yes'))?>
  </select>

:docker_tailscale_lanaccess_help:

</div>

<div markdown="1" class="TSuserspacenetworking noshow">
_(Tailscale Userspace Networking)_:
: <select name="TSuserspacenetworking" id="TSuserspacenetworking" onchange="setExitNodeoptions()">
    <?=mk_option(1,'true',_('Enabled'))?>
    <?=mk_option(1,'false',_('Disabled'))?>
  </select>
  <span id='TSuserspacenetworking_msg' style='font-style: italic;'></span>

:docker_tailscale_userspace_networking_help:

</div>

<div markdown="1" class="TSssh noshow">
_(Enable Tailscale SSH)_:
: <select name="TSssh" id="TSssh">
    <?=mk_option(1,'false',_('No'))?>
    <?=mk_option(1,'true',_('Yes'))?>
  </select>

:docker_tailscale_ssh_help:

</div>

<div markdown="1" class="TSserve noshow">
_(Tailscale Serve)_:
: <select name="TSserve" id="TSserve" onchange="showServe(this.value)">
    <?=mk_option(1,'no',_('No'))?>
    <?=mk_option(1,'serve',_('Serve'))?>
    <?=mk_option(1,'funnel',_('Funnel'))?>
  </select>
<?=$TS_HTTPSDisabledWarning?><?php if (!empty($TS_webui_url)) echo '<label for="TSserve"><a href="' . $TS_webui_url . '" target="_blank">' . $TS_webui_url . '</a></label>'; ?>

:docker_tailscale_serve_mode_help:

</div>

<div markdown="1" class="TSserveport noshow">
_(Tailscale Serve Port)_:
: <input type="text" name="TSserveport" value="<?php echo !empty($xml['TailscaleServePort']) ? $xml['TailscaleServePort'] : (!empty($TSwebuiport) ? $TSwebuiport : ''); ?>" placeholder="_(Will be detected automatically if possible)_">

:docker_tailscale_serve_port_help:

</div>

<div markdown="1" class="TSadvanced noshow">
_(Tailscale Show Advanced Settings)_:
: <span class="flex flex-row items-center">
    <input type="checkbox" name="TSadvanced" class="switch-on-off" onchange="showTSAdvanced(this.checked)">
  </span>

:docker_tailscale_show_advanced_help:

</div>

<div markdown="1" class="TSservetarget noshow">
_(Tailscale Serve Target)_:
: <input type="text" name="TSservetarget" <?php if (!empty($xml['TailscaleServeTarget'])) echo 'value="' . $xml['TailscaleServeTarget'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_serve_target_help:

</div>

<div markdown="1" class="TSservelocalpath noshow">
_(Tailscale Serve Local Path)_:
: <input type="text" name="TSservelocalpath" <?php if (!empty($xml['TailscaleServeLocalPath'])) echo 'value="' . $xml['TailscaleServeLocalPath'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_serve_local_path_help:

</div>

<div markdown="1" class="TSserveprotocol noshow">
_(Tailscale Serve Protocol)_:
: <input type="text" name="TSserveprotocol" <?php if (!empty($xml['TailscaleServeProtocol'])) echo 'value="' . $xml['TailscaleServeProtocol'] . '"'; ?> placeholder="_(Leave empty if unsure, defaults to https)_">

:docker_tailscale_serve_protocol_help:

</div>

<div markdown="1" class="TSserveprotocolport noshow">
_(Tailscale Serve Protocol Port)_:
: <input type="text" name="TSserveprotocolport" <?php if (!empty($xml['TailscaleServeProtocolPort'])) echo 'value="' . $xml['TailscaleServeProtocolPort'] . '"'; ?> placeholder="_(Leave empty if unsure, defaults to =443)_">

:docker_tailscale_serve_protocol_port_help:

</div>

<div markdown="1" class="TSservepath noshow">
_(Tailscale Serve Path)_:
: <input type="text" name="TSservepath" <?php if (!empty($xml['TailscaleServePath'])) echo 'value="' . $xml['TailscaleServePath'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_serve_path_help:

</div>

<div markdown="1" class="TSwebui noshow">
_(Tailscale WebUI)_:
: <input type="text" name="TSwebui" value="<?php echo !empty($TS_webui_url) ? $TS_webui_url : ''; ?>" placeholder="Will be determined automatically if possible" disabled>
<input type="hidden" name="TSwebui" <?php if (!empty($xml['TailscaleWebUI'])) echo 'value="' . $xml['TailscaleWebUI'] . '"'; ?>>

:docker_tailscale_serve_webui_help:

</div>

<div markdown="1" class="TSroutes noshow">
_(Tailscale Advertise Routes)_:
: <input type="text" pattern="[0-9:., \/]*" name="TSroutes" <?php if (!empty($xml['TailscaleRoutes'])) echo 'value="' . $xml['TailscaleRoutes'] . '"'?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_advertise_routes_help:

</div>

<div markdown="1" class="TSacceptroutes noshow">
_(Tailscale Accept Routes)_:
: <select name="TSacceptroutes" id="TSacceptroutes">
    <?=mk_option(1,'false',_('No'))?>
    <?=mk_option(1,'true',_('Yes'))?>
  </select>

:docker_tailscale_accept_routes_help:

</div>

<div markdown="1" class="TSdaemonparams noshow">
_(Tailscale Daemon Parameters)_:
: <input type="text" name="TSdaemonparams" <?php if (!empty($xml['TailscaleDParams'])) echo 'value="' . $xml['TailscaleDParams'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_daemon_extra_params_help:

</div>

<div markdown="1" class="TSextraparams noshow">
_(Tailscale Extra Parameters)_:
: <input type="text" name="TSextraparams" <?php if (!empty($xml['TailscaleParams'])) echo 'value="' . $xml['TailscaleParams'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_extra_param_help:

</div>

<div markdown="1" class="TSstatedir noshow">
_(Tailscale State Directory)_:
: <input type="text" name="TSstatedir" <?php if (!empty($xml['TailscaleStateDir'])) echo 'value="' . $xml['TailscaleStateDir'] . '"'; ?> placeholder="_(Leave empty if unsure)_">

:docker_tailscale_statedir_help:

</div>

<div markdown="1" class="TStroubleshooting noshow">
_(Tailscale Install Troubleshooting Packages)_:
: <span class="flex flex-row items-center">
    <input type="checkbox" class="switch-on-off" name="TStroubleshooting" <?php if (!empty($xml['TailscaleTroubleshooting']) && $xml['TailscaleTroubleshooting'] == 'true') echo 'checked'; ?>>
  </span>

:docker_tailscale_troubleshooting_packages_help:

</div>

<div markdown="1" class="TSdivider noshow">
  <hr>
</div>

_(Console shell command)_:
: <select name="contShell">
  <?=mk_option(1,'sh',_('Shell'))?>
  <?=mk_option(1,'bash',_('Bash'))?>
  </select>

_(Privileged)_:
: <span class="flex flex-row items-center">
    <input type="checkbox" class="switch-on-off" name="contPrivileged">
  </span>

:docker_privileged_help:

<div id="configLocation"></div>

&nbsp;
: <span id="readmore_toggle" class="readmore_collapsed"><a onclick="toggleReadmore()" style="cursor:pointer"><i class="fa fa-fw fa-chevron-down"></i> _(Show more settings)_ ...</a></span><div id="configLocationAdvanced" style="display:none"></div>

&nbsp;
: <span id="allocations_toggle" class="readmore_collapsed">
    <a onclick="toggleAllocations()" style="cursor:pointer">
      <i class="fa fa-fw fa-chevron-down"></i> _(Show docker allocations)_ ...</a>
  </span>
  <div id="dockerAllocations" style="display:none"></div>

&nbsp;
: <a href="javascript:addConfigPopup()"><i class="fa fa-fw fa-plus"></i> _(Add another Path, Port, Variable, Label or Device)_</a>

&nbsp;
: <span class="inline-block">
    <input type="submit" value="<?=$xmlType=='edit' ? "_(Apply)_" : " _(Apply)_ "?>">
    <input type="button" value="_(Done)_" onclick="done()">
    <?if ($authoringMode):?><button type="submit" name="dryRun" value="true" onclick="$('*[required]').prop('required', null);">_(Save)_</button><?endif;?>
  </span>

</form>
</div>

<form method="GET" id="formTemplate">
  <input type="hidden" id="xmlTemplate" name="xmlTemplate" value="">
</form>
<form method="POST" id="formTemplate1">
  <input type="hidden" name="csrf_token" value="<?=$var['csrf_token']?>">
  <input type="hidden" id="rmTemplate" name="rmTemplate" value="">
</form>

<div id="dialogAddConfig" style="display:none"></div>

<?
#        ██╗███████╗    ████████╗███████╗███╗   ███╗██████╗ ██╗      █████╗ ████████╗███████╗███████╗
#        ██║██╔════╝    ╚══██╔══╝██╔════╝████╗ ████║██╔══██╗██║     ██╔══██╗╚══██╔══╝██╔════╝██╔════╝
#        ██║███████╗       ██║   █████╗  ██╔████╔██║██████╔╝██║     ███████║   ██║   █████╗  ███████╗
#   ██   ██║╚════██║       ██║   ██╔══╝  ██║╚██╔╝██║██╔═══╝ ██║     ██╔══██║   ██║   ██╔══╝  ╚════██║
#   ╚█████╔╝███████║       ██║   ███████╗██║ ╚═╝ ██║██║     ███████╗██║  ██║   ██║   ███████╗███████║
#    ╚════╝ ╚══════╝       ╚═╝   ╚══════╝╚═╝     ╚═╝╚═╝     ╚══════╝╚═╝  ╚═╝   ╚═╝   ╚══════╝╚══════╝
?>
<div markdown="1" id="templatePopupConfig" style="display:none">
_(Config Type)_:
: <select name="Type" onchange="toggleMode(this,false)">
  <option value="Path">_(Path)_</option>
  <option value="Port">_(Port)_</option>
  <option value="Variable">_(Variable)_</option>
  <option value="Label">_(Label)_</option>
  <option value="Device">_(Device)_</option>
  </select>

_(Name)_:
: <input type="text" name="Name" autocomplete="off" spellcheck="false">

<div markdown="1" id="Target" class="w-full">
<span id="dt1">_(Target)_</span>:
: <input type="text" name="Target" autocomplete="off" spellcheck="false">
</div>

<div markdown="1" id="Value" class="w-full">
<span id="dt2">_(Value)_</span>:
: <input type="text" name="Value" autocomplete="off" spellcheck="false">
</div>

<div markdown="1" id="Default" class="w-full">
_(Default Value)_:
: <input type="text" name="Default" autocomplete="off" spellcheck="false">
</div>

<div id="Mode" class="w-full"></div>

_(Description)_:
: <textarea name="Description" spellcheck="false" cols="80" rows="3"></textarea>

<div markdown="1" class="advanced" class="w-full">
_(Display)_:
: <select name="Display">
  <option value="always" selected>_(Always)_</option>
  <option value="always-hide">_(Always)_ - _(Hide Buttons)_</option>
  <option value="advanced">_(Advanced)_</option>
  <option value="advanced-hide">_(Advanced)_ - _(Hide Buttons)_</option>
  </select>

_(Required)_:
: <select name="Required">
  <option value="false" selected>_(No)_</option>
  <option value="true">_(Yes)_</option>
  </select>

_(Password Mask)_:
: <select name="Mask">
  <option value="false" selected>_(No)_</option>
  <option value="true">_(Yes)_</option>
  </select>
</div>
</div>

<div markdown="1" id="templateDisplayConfig" style="display:none">
<input type="hidden" name="confName[]" value="{0}">
<input type="hidden" name="confTarget[]" value="{1}">
<input type="hidden" name="confDefault[]" value="{2}">
<input type="hidden" name="confMode[]" value="{3}">
<input type="hidden" name="confDescription[]" value="{4}">
<input type="hidden" name="confType[]" value="{5}">
<input type="hidden" name="confDisplay[]" value="{6}">
<input type="hidden" name="confRequired[]" value="{7}">
<input type="hidden" name="confMask[]" value="{8}">

<span class="{11}"><i class="fa fa-fw fa-{13}"></i>&nbsp;&nbsp;{0}:</span>
: <span class="flex flex-col gap-4">
    <span class="flex flex-row flex-wrap items-center gap-4 buttons-no-margin">
      <input type="text" class="setting_input" name="confValue[]" default="{2}" value="{9}" autocomplete="off" spellcheck="false" {11}>
      {10}
    </span>
    <span class="boxed">
      <span class='orange-text'>{12}: {1}</span>
      <span class="orange-text">{4}</span>
    </span>
  </span>
</div>

<div markdown="1" id="templateAllocations" style="display:none">
<span class="docker-allocation-dt">&nbsp;</span>
: <span class="docker-allocation-row"><span>{1}</span>{2}</span>
</div>

<script>
var subnet = {};
<?foreach ($subnet as $network => $value):?>
subnet['<?=$network?>'] = '<?=$value?>';
<?endforeach;?>

function showSubnet(bridge) {
  if (bridge.match(/^(bridge|host|none)$/i) !== null) {
    $('.myIP').hide();
    $('input[name="contMyIP"]').val('');
    $('.netCONT').hide();
    $('#netCONT').val('');
  } else if (bridge.match(/^(container)$/i) !== null) {
    $('.netCONT').show();
    $('#netCONT').val('<?php echo (isset($xml) && isset($xml['Network'][1])) ? $xml['Network'][1] : ''; ?>');
    $('.myIP').hide();
    $('input[name="contMyIP"]').val('');
  } else {
    $('.myIP').show();
    $('#myIP').html('<?=_('Subnet')?>: '+subnet[bridge]);
    $('.netCONT').hide();
    $('#netCONT').val('');
  }
  // make sure to re-trigger Tailscale check when network is changed
  if (bridge.match(/^(host|container)$/i) !== null) {
    $('#contTailscale').siblings('.switch-button-background').click();
    $(".TSNetworkAllowed").hide();
    $(".TSNetworkNotAllowed").show();
  } else {
    $(".TSNetworkAllowed").show();
    $(".TSNetworkNotAllowed").hide();
  }
}

function processExitNodeoptions(value) {
  val = null;
  if (value.tagName.toLowerCase() === "input") {
    val = value.value.trim();
  } else if (value.tagName.toLowerCase() === "select") {
    val = value.value;
  }
  if (val) {
    $('.TSallowlanaccess').show();
  } else {
    $('#TSallowlanaccess').val('false');
    $('.TSallowlanaccess').hide();
  }
  setUserspaceNetworkOptions();
  setIsExitNodeoptions();
}

function setUserspaceNetworkOptions() {
  optTrueDisabled = false;
  optFalseDisabled = false;
  optMessage = "";
  value = null;

  var network = $('select[name="contNetwork"]')[0].value;
  var isExitnode = $('#TSisexitnode').val();
  if (network == 'host' || isExitnode == 'true') {
    // in host mode or if this container is an Exit Node
    // then Userspace Networking MUST be enabled ('true')
    value = 'true';
    optTrueDisabled = false;
    optFalseDisabled = true;
    optMessage = (isExitnode == 'true') ? "Enabled because this is an Exit Node" : "Enabled due to Docker "+network+" mode";
  } else {
    if (document.querySelector('input[name="TSexitnodeip"], select[name="TSexitnodeip"]').value) {
      // If an Exit Node IP is set, Userspace Networking MUST be disabled ('false')
      value = 'false';
      optTrueDisabled = true;
      optFalseDisabled = false;
      optMessage = "Disabled due to use of an Exit Node";
    } else {
      // Exit Node IP is not set, user can decide whether to enable/disable Userspace Networking
      optTrueDisabled = false;
      optFalseDisabled = false;
      optMessage = "";
    }
  }

  $("#TSuserspacenetworking option[value='true']").prop("disabled", optTrueDisabled);
  $("#TSuserspacenetworking option[value='false']").prop("disabled", optFalseDisabled);
  if (value != null) $('#TSuserspacenetworking').val(value);
  $('#TSuserspacenetworking_msg').text(optMessage);
  setExitNodeoptions();
}

function setIsExitNodeoptions() {
  optTrueDisabled = false;
  optFalseDisabled = false;
  optMessage = "";
  value = null;

  var network = $('select[name="contNetwork"]')[0].value;
  if (network == 'host') {
    // in host mode then this cannot be an Exit Node
    value = 'false';
    optTrueDisabled = true;
    optFalseDisabled = false;
    optMessage = "Disabled due to Docker "+network+" mode";
  } else {
    if (document.querySelector('input[name="TSexitnodeip"], select[name="TSexitnodeip"]').value) {
      // If an Exit Node IP is set, this cannot be an Exit Node
      value = 'false';
      optTrueDisabled = true;
      optFalseDisabled = false;
      optMessage = "Disabled due to use of an Exit Node";
    } else {
      optTrueDisabled = false;
      optFalseDisabled = false;
    }
  }
  $("#TSisexitnode option[value='true']").prop("disabled", optTrueDisabled);
  $("#TSisexitnode option[value='false']").prop("disabled", optFalseDisabled);
  if (value != null) $('#TSisexitnode').val(value);
  $('#TSisexitnode_msg').text(optMessage);
}

function setExitNodeoptions() {
  optMessage = "";
  var $exitNodeInput = $('input[name="TSexitnodeip"]');
  var $exitNodeSelect = $('#TSexitnodeip');
  // In host mode, TSuserspacenetworking is true
  if ($('#TSuserspacenetworking').val() == 'true') {
    // if TSuserspacenetworking is true, then TSexitnodeip must be "" and all options are disabled
    optMessage = "Disabled because Userspace Networking is Enabled.";
    $exitNodeInput.val('').prop('disabled', true);  // Disable the input field
    $exitNodeSelect.val('').prop('disabled', true).find('option').each(function() {
      if ($(this).val() === "") {
        $(this).prop('disabled', false);  // Enable the option with value=""
      } else {
        $(this).prop('disabled', true);   // Disable all other options
      }
    });
  } else {
    // if TSuserspacenetworking is false, then all TSexitnodeip options can be enabled
    $exitNodeInput.prop('disabled', false);  // Enable the input field
    $exitNodeSelect.prop('disabled', false).find('option').each(function() {
      $(this).prop('disabled', false);   // Enable all options
    });
  }
  $('#TSexitnodeip_msg').text(optMessage);
}

function showTSAdvanced(checked) {
  if (!checked) {
    <?if (!empty($TSwebuiport)):?>
      $('.TSserveport').hide();
    <?elseif (empty($contTailscale) || $contTailscale == 'false'):?>
      $('.TSserveport').hide();
    <?else:?>
      $('.TSserveport').show();
    <?endif;?>
    $('.TSdaemonparams').hide();
    $('.TSextraparams').hide();
    $('.TSstatedir').hide();
    $('.TSservepath').hide();
    $('.TSserveprotocol').hide();
    $('.TSserveprotocolport').hide();
    $('.TSservetarget').hide();
    $('.TSservelocalpath').hide();
    $('.TSwebui').hide();
    $('.TStroubleshooting').hide();
    $('.TSroutes').hide();
    $('.TSacceptroutes').hide();
  } else {
    $('.TSdaemonparams').show();
    $('.TSextraparams').show();
    $('.TSstatedir').show();
    $('.TSserveport').show();
    $('.TSservepath').show();
    $('.TSserveprotocol').show();
    $('.TSserveprotocolport').show();
    $('.TSservetarget').show();
    $('.TSservelocalpath').show();
    $('.TSwebui').show();
    $('.TStroubleshooting').show();
    $('.TSroutes').show();
    $('.TSacceptroutes').show();
  }
}

function showTailscale(source) {
  var bridge = $('select[name="contNetwork"]').val();
  if (bridge.match(/^(host|container)$/i) !== null) {
    $('#contTailscale').prop('checked',false);
    $(".TSNetworkAllowed").hide();
    $(".TSNetworkNotAllowed").show();
  }
  if (!$.trim($('#TSallowlanaccess').val())) {
    $('#TSallowlanaccess').val('false');
  }
  if (!$.trim($('#TSserve').val())) {
    $('#TSserve').val('no');
  }
  checked = $('#contTailscale').prop('checked');
  if (!checked) {
    $('.TSdivider').hide();
    $('.TSwarning').hide();
    $('.TSdeploy').hide();
    $('.TSisexitnode').hide();
    $('.TShostname').hide();
    $('.TSexitnodeip').hide();
    $('.TSssh').hide();
    $('.TSallowlanaccess').hide();
    $('.TSdaemonparams').hide();
    $('.TSextraparams').hide();
    $('.TSstatedir').hide();
    $('.TSserve').hide();
    $('.TSuserspacenetworking').hide();
    $('.TSservepath').hide();
    $('.TSserveprotocol').hide();
    $('.TSserveprotocolport').hide();
    $('.TSservelocalpath').hide();
    $('.TSwebui').hide();
    $('.TSserveport').hide();
    $('.TSadvanced').hide();
    $('.TSroutes').hide();
    $('.TSacceptroutes').hide();
    $('.TStroubleshooting').hide();
  } else {
    // reset these vals back to what they were in the XML
    $('#TSssh').val('<?php echo (!empty($xml) && !empty($xml['TailscaleSSH'])) ? $xml['TailscaleSSH'] : 'false'; ?>');
    $('#TSallowlanaccess').val('<?php echo (!empty($xml) && !empty($xml['TailscaleLANAccess'])) ? $xml['TailscaleLANAccess'] : 'false'; ?>');
    $('#TSserve').val('<?php echo (!empty($xml) && !empty($xml['TailscaleServe'])) ? $xml['TailscaleServe'] : 'false'; ?>');
    $('#TSexitnodeip').val('<?php echo (!empty($xml) && !empty($xml['TailscaleExitNodeIP'])) ? $xml['TailscaleExitNodeIP'] : ''; ?>');
    $('#TSuserspacenetworking').val('<?php echo (!empty($xml) && !empty($xml['TailscaleUserspaceNetworking'])) ? $xml['TailscaleUserspaceNetworking'] : 'false'; ?>');
    $('#TSacceptroutes').val('<?php echo (!empty($xml) && !empty($xml['TailscaleAcceptRoutes'])) ? $xml['TailscaleAcceptRoutes'] : 'false'; ?>');
    <?if (empty($xml['TailscaleServe']) && !empty($TSwebuiport) && empty($xml['TailscaleServePort'])):?>
      $('#TSserve').val('serve');
    <?elseif (empty($xml['TailscaleServe']) && empty($TSwebuiport) && empty($xml['TailscaleServePort'])):?>
      $('#TSserve').val('no');
    <?endif;?>
    // don't reset this field if caller was the onchange event for this field
    if (source.id != 'TSisexitnode') $('#TSisexitnode').val('<?php echo !empty($xml['TailscaleIsExitNode']) ? $xml['TailscaleIsExitNode'] : 'false'; ?>');
    $('.TSisexitnode').show();
    $('.TShostname').show();
    $('.TSssh').show();
    $('.TSexitnodeip').show();
    $('.TSallowlanaccess').hide();
    $('.TSserve').show();
    $('.TSuserspacenetworking').show();
    processExitNodeoptions(document.querySelector('input[name="TSexitnodeip"], select[name="TSexitnodeip"]'));
    $('.TSdivider').show();
    $('.TSwarning').show();
    $('.TSdeploy').show();
    $('.TSadvanced').show();
  }
}

function reloadTriggers() {
  $(".basic").toggle(!$(".advancedview").is(":checked"));
  $(".advanced").toggle($(".advancedview").is(":checked"));
  $(".numbersOnly").keypress(function(e){if(e.which != 45 && e.which != 8 && e.which != 0 && (e.which < 48 || e.which > 57)){return false;}});
}

function toggleReadmore() {
  var readm = $('#readmore_toggle');
  if (readm.hasClass('readmore_collapsed')) {
    readm.removeClass('readmore_collapsed').addClass('readmore_expanded');
    $('#configLocationAdvanced').slideDown('fast');
    readm.find('a').html('<i class="fa fa-fw fa-chevron-up"></i> _(Hide more settings)_ ...');
  } else {
    $('#configLocationAdvanced').slideUp('fast');
    readm.removeClass('readmore_expanded').addClass('readmore_collapsed');
    readm.find('a').html('<i class="fa fa-fw fa-chevron-down"></i> _(Show more settings)_ ...');
  }
}

function toggleAllocations() {
  var readm = $('#allocations_toggle');
  if (readm.hasClass('readmore_collapsed')) {
    readm.removeClass('readmore_collapsed').addClass('readmore_expanded');
    $('#dockerAllocations').slideDown('fast');
    readm.find('a').html('<i class="fa fa-fw fa-chevron-up"></i> _(Hide docker allocations)_ ...');
  } else {
    $('#dockerAllocations').slideUp('fast');
    readm.removeClass('readmore_expanded').addClass('readmore_collapsed');
    readm.find('a').html('<i class="fa fa-fw fa-chevron-down"></i> _(Show docker allocations)_ ...');
  }
}

function load_contOverview() {
  var new_overview = $("textarea[name='contOverview']").val();
  new_overview = new_overview.replaceAll("[","<").replaceAll("]",">");
  // Handle code block being created by authors indenting (manually editing the xml and spacing)
  new_overview = new_overview.replaceAll("    ","&nbsp;&nbsp;&nbsp;&nbsp;");
  new_overview = marked(new_overview);
  new_overview = new_overview.replaceAll("\n","<br>"); // has to be after marked
  $("#contDescription").html(new_overview);

  var new_requires = $("textarea[name='contRequires']").val();
  new_requires = new_requires.replaceAll("[","<").replaceAll("]",">");
  // Handle code block being created by authors indenting (manually editing the xml and spacing)
  new_requires = new_requires.replaceAll("    ","&nbsp;&nbsp;&nbsp;&nbsp;");
  new_requires = marked(new_requires);
  new_requires = new_requires.replaceAll("\n","<br>"); // has to be after marked
  new_requires = new_requires ? new_requires : "<em>_(None Listed)_</em>";
  $("#contRequires").html(new_requires);
}

$(function() {
  // Load container info on page load
  if (typeof Settings != 'undefined') {
    for (var key in Settings) {
      if (Settings.hasOwnProperty(key)) {
        var target = $('#canvas').find('*[name=cont'+key+']:first');
        if (target.length) {
          var value = Settings[key];
          if (target.attr("type") == 'checkbox') {
            target.prop('checked', (value == 'true'));
          } else if ($(target).prop('nodeName') == 'DIV') {
            target.html(value);
          } else {
            target.val(value);
          }
        }
      }
    }
    load_contOverview();
    // Load the confCategory input into the s1 select
    categories=$("input[name='contCategory']").val().split(" ");
    for (var i = 0; i < categories.length; i++) {
      $("#catSelect option[value='"+categories[i]+"']").prop("selected", true);
    }
    // Remove empty description
    if (!Settings.Description.length) {
      $('#canvas').find('#Overview:first').hide();
    }
    // Load config info
    var network = $('select[name="contNetwork"]')[0].selectedIndex;
    for (var i = 0; i < Settings.Config.length; i++) {
      confNum += 1;
      Opts = Settings.Config[i];
      if (Opts.Display == "always-hide" || Opts.Display == "advanced-hide") {
        Opts.Buttons = "<span class='flex flex-row items-center gap-4'>";
        Opts.Buttons += "<span class='advanced'><button type='button' onclick='editConfigPopup("+confNum+",<?=$disableEdit?>)'>_(Edit)_</button>";
        Opts.Buttons += "<button type='button' onclick='removeConfig("+confNum+")'>_(Remove)_</button></span>";
        Opts.Buttons += "</span>";
      } else {
        Opts.Buttons = "<span class='flex flex-row items-center gap-4'>";
        Opts.Buttons += "<button type='button' onclick='editConfigPopup("+confNum+",<?=$disableEdit?>)'>_(Edit)_</button>";
        Opts.Buttons += "<button type='button' onclick='removeConfig("+confNum+")'>_(Remove)_</button>";
        Opts.Buttons += "</span>";
      }
      Opts.Number = confNum;
      if (Opts.Type == "Device") {
        Opts.Target = Opts.Value;
      }
      newConf = makeConfig(Opts);
      if (Opts.Display == 'advanced' || Opts.Display == 'advanced-hide') {
        $("#configLocationAdvanced").append(newConf);
      } else {
        $("#configLocation").append(newConf);
      }
    }
  } else {
    $('#canvas').find('#Overview:first').hide();
  }
  // Show associated subnet with fixed IP (if existing)
  showSubnet($('select[name="contNetwork"]').val());
  // Add list of docker allocations
  $("#dockerAllocations").html(makeAllocations(Allocations,$('input[name="contName"]').val()));
  // Add switchButton
  $('.switch-on-off').switchButton({labels_placement:'right',on_label:"_(On)_",off_label:"_(Off)_"});
  // Add dropdownchecklist to Select Categories
  $("#catSelect").dropdownchecklist({emptyText:"_(Select categories)_...", maxDropHeight:200, width:300, explicitClose:"..._(close)_"});
  <?if ($authoringMode){
    echo "$('.advancedview').prop('checked','true'); $('.advancedview').change();";
    echo "$('.advancedview').siblings('.switch-button-background').click();";
  }?>
});

if (window.location.href.indexOf("/Apps/") > 0  && <? if (is_file($xmlTemplate)) echo "true"; else echo "false"; ?> ) {
  $(".TemplateDropDown").hide();
}
</script>
<?END:?>

