/* eslint no-bitwise: ["error", { "allow": [">>"] }] */
import { nextTick, bindModuleMethods } from '../../utils/utils';

const Controller = {
  LinearSpline: function LinearSpline(x, y) {
    const binarySearch = (function search() {
      let maxIndex;
      let minIndex;
      let guess;
      return (array, val) => {
        minIndex = -1;
        maxIndex = array.length;
        while (maxIndex - minIndex > 1) {
          guess = (maxIndex + minIndex) >> 1;
          if (array[guess] <= val) {
            minIndex = guess;
          } else {
            maxIndex = guess;
          }
        }
        return maxIndex;
      };
    })();
    this.x = x;
    this.y = y;
    this.lastIndex = x.length - 1;
    // Given an x value (x2), return the expected y2 value:
    // (x1,y1) is the known point before given value,
    // (x3,y3) is the known point after given value.
    let i1;
    let i3;

    this.interpolate = function interpolate(x2) {
      if (!x2) return 0;

      // Get the indexes of x1 and x3 (the array indexes before and after given x2):
      i3 = binarySearch(this.x, x2);
      i1 = i3 - 1;

      // We have our indexes i1 & i3, so we can calculate already:
      // y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1
      return (
        ((x2 - this.x[i1]) * (this.y[i3] - this.y[i1])) / (this.x[i3] - this.x[i1]) + this.y[i1]
      );
    };
    return this;
  },
  // xxx: for now i will just save one spline function to to
  getInterpolateFunction(c) {
    const swiper = this;
    if (!swiper.controller.spline) {
      swiper.controller.spline = swiper.params.loop
        ? new Controller.LinearSpline(swiper.slidesGrid, c.slidesGrid)
        : new Controller.LinearSpline(swiper.snapGrid, c.snapGrid);
    }
  },
  setTranslate(setTranslate, byController) {
    const swiper = this;
    const controlled = swiper.controller.control;
    let multiplier;
    let controlledTranslate;
    const Swiper = swiper.constructor;
    function setControlledTranslate(c) {
      // this will create an Interpolate function based on the snapGrids
      // x is the Grid of the scrolled scroller and y will be the controlled scroller
      // it makes sense to create this only once and recall it for the interpolation
      // the function does a lot of value caching for performance
      const translate = swiper.rtlTranslate ? -swiper.translate : swiper.translate;
      if (swiper.params.controller.by === 'slide') {
        swiper.controller.getInterpolateFunction(c);
        // i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid
        // but it did not work out
        controlledTranslate = -swiper.controller.spline.interpolate(-translate);
      }

      if (!controlledTranslate || swiper.params.controller.by === 'container') {
        multiplier =
          (c.maxTranslate() - c.minTranslate()) / (swiper.maxTranslate() - swiper.minTranslate());
        controlledTranslate = (translate - swiper.minTranslate()) * multiplier + c.minTranslate();
      }

      if (swiper.params.controller.inverse) {
        controlledTranslate = c.maxTranslate() - controlledTranslate;
      }
      c.updateProgress(controlledTranslate);
      c.setTranslate(controlledTranslate, swiper);
      c.updateActiveIndex();
      c.updateSlidesClasses();
    }
    if (Array.isArray(controlled)) {
      for (let i = 0; i < controlled.length; i += 1) {
        if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
          setControlledTranslate(controlled[i]);
        }
      }
    } else if (controlled instanceof Swiper && byController !== controlled) {
      setControlledTranslate(controlled);
    }
  },
  setTransition(duration, byController) {
    const swiper = this;
    const Swiper = swiper.constructor;
    const controlled = swiper.controller.control;
    let i;
    function setControlledTransition(c) {
      c.setTransition(duration, swiper);
      if (duration !== 0) {
        c.transitionStart();
        if (c.params.autoHeight) {
          nextTick(() => {
            c.updateAutoHeight();
          });
        }
        c.$wrapperEl.transitionEnd(() => {
          if (!controlled) return;
          if (c.params.loop && swiper.params.controller.by === 'slide') {
            c.loopFix();
          }
          c.transitionEnd();
        });
      }
    }
    if (Array.isArray(controlled)) {
      for (i = 0; i < controlled.length; i += 1) {
        if (controlled[i] !== byController && controlled[i] instanceof Swiper) {
          setControlledTransition(controlled[i]);
        }
      }
    } else if (controlled instanceof Swiper && byController !== controlled) {
      setControlledTransition(controlled);
    }
  },
};
export default {
  name: 'controller',
  params: {
    controller: {
      control: undefined,
      inverse: false,
      by: 'slide', // or 'container'
    },
  },
  create() {
    const swiper = this;
    bindModuleMethods(swiper, {
      controller: {
        control: swiper.params.controller.control,
        ...Controller,
      },
    });
  },
  on: {
    update(swiper) {
      if (!swiper.controller.control) return;
      if (swiper.controller.spline) {
        swiper.controller.spline = undefined;
        delete swiper.controller.spline;
      }
    },
    resize(swiper) {
      if (!swiper.controller.control) return;
      if (swiper.controller.spline) {
        swiper.controller.spline = undefined;
        delete swiper.controller.spline;
      }
    },
    observerUpdate(swiper) {
      if (!swiper.controller.control) return;
      if (swiper.controller.spline) {
        swiper.controller.spline = undefined;
        delete swiper.controller.spline;
      }
    },
    setTranslate(swiper, translate, byController) {
      if (!swiper.controller.control) return;
      swiper.controller.setTranslate(translate, byController);
    },
    setTransition(swiper, duration, byController) {
      if (!swiper.controller.control) return;
      swiper.controller.setTransition(duration, byController);
    },
  },
};


keep keep keep replace keep keep keep keep

let modules = process.env.MODULES || false;
if (modules === 'esm' || modules === 'false') modules = false;

module.exports = {
  presets: [['@babel/preset-env', { modules, loose: true }]],
};


keep keep keep keep replace keep keep keep

import { getWindow, getDocument } from 'ssr-window';
import $ from '../../../utils/dom';
import { extend, now } from '../../../utils/utils';

export default function onTouchStart(event) {
  const swiper = this;
  const document = getDocument();
  const window = getWindow();

  const data = swiper.touchEventsData;
  const { params, touches, enabled } = swiper;
  if (!enabled) return;

  if (swiper.animating && params.preventInteractionOnTransition) {
    return;
  }
  let e = event;
  if (e.originalEvent) e = e.originalEvent;
  let $targetEl = $(e.target);

  if (params.touchEventsTarget === 'wrapper') {
    if (!$targetEl.closest(swiper.wrapperEl).length) return;
  }
  data.isTouchEvent = e.type === 'touchstart';
  if (!data.isTouchEvent && 'which' in e && e.which === 3) return;
  if (!data.isTouchEvent && 'button' in e && e.button > 0) return;
  if (data.isTouched && data.isMoved) return;

  // change target el for shadow root componenet
  const swipingClassHasValue = !!params.noSwipingClass && params.noSwipingClass !== '';
  if (swipingClassHasValue && e.target && e.target.shadowRoot && event.path && event.path[0]) {
    $targetEl = $(event.path[0]);
  }

  if (
    params.noSwiping &&
    $targetEl.closest(
      params.noSwipingSelector ? params.noSwipingSelector : `.${params.noSwipingClass}`,
    )[0]
  ) {
    swiper.allowClick = true;
    return;
  }

  if (params.swipeHandler) {
    if (!$targetEl.closest(params.swipeHandler)[0]) return;
  }

  touches.currentX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  touches.currentY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  const startX = touches.currentX;
  const startY = touches.currentY;

  // Do NOT start if iOS edge swipe is detected. Otherwise iOS app cannot swipe-to-go-back anymore

  const edgeSwipeDetection = params.edgeSwipeDetection || params.iOSEdgeSwipeDetection;
  const edgeSwipeThreshold = params.edgeSwipeThreshold || params.iOSEdgeSwipeThreshold;
  if (
    edgeSwipeDetection &&
    (startX <= edgeSwipeThreshold || startX >= window.innerWidth - edgeSwipeThreshold)
  ) {
    if (edgeSwipeDetection === 'prevent') {
      event.preventDefault();
    } else {
      return;
    }
  }

  extend(data, {
    isTouched: true,
    isMoved: false,
    allowTouchCallbacks: true,
    isScrolling: undefined,
    startMoving: undefined,
  });

  touches.startX = startX;
  touches.startY = startY;
  data.touchStartTime = now();
  swiper.allowClick = true;
  swiper.updateSize();
  swiper.swipeDirection = undefined;
  if (params.threshold > 0) data.allowThresholdMove = false;
  if (e.type !== 'touchstart') {
    let preventDefault = true;
    if ($targetEl.is(data.focusableElements)) preventDefault = false;
    if (
      document.activeElement &&
      $(document.activeElement).is(data.focusableElements) &&
      document.activeElement !== $targetEl[0]
    ) {
      document.activeElement.blur();
    }

    const shouldPreventDefault =
      preventDefault && swiper.allowTouchMove && params.touchStartPreventDefault;
    if (
      (params.touchStartForcePreventDefault || shouldPreventDefault) &&
      !$targetEl[0].isContentEditable
    ) {
      e.preventDefault();
    }
  }
  swiper.emit('touchStart', e);
}


import setBreakpoint from './setBreakpoint';
import getBreakpoint from './getBreakpoint';

export default { setBreakpoint, getBreakpoint };


import { h, ref, onMounted, onUpdated, onBeforeUpdate, computed, onBeforeUnmount } from 'vue';
import { uniqueClasses } from './utils';

const SwiperSlide = {
  name: 'SwiperSlide',
  props: {
    tag: {
      type: String,
      default: 'div',
    },
    swiperRef: Object,
    zoom: { type: Boolean, default: undefined },
    virtualIndex: {
      type: [String, Number],
      default: undefined,
    },
  },
  setup(props, { slots }) {
    let eventAttached = false;
    const { swiperRef } = props;
    const slideElRef = ref(null);
    const slideClasses = ref('swiper-slide');

    function updateClasses(swiper, el, classNames) {
      if (el === slideElRef.value) {
        slideClasses.value = classNames;
      }
    }

    onMounted(() => {
      if (!swiperRef.value) return;
      swiperRef.value.on('_slideClass', updateClasses);
      eventAttached = true;
    });

    onBeforeUpdate(() => {
      if (eventAttached || !swiperRef || !swiperRef.value) return;
      swiperRef.value.on('_slideClass', updateClasses);
      eventAttached = true;
    });

    onUpdated(() => {
      if (!slideElRef.value || !swiperRef || !swiperRef.value) return;
      if (swiperRef.value.destroyed) {
        if (slideClasses.value !== 'swiper-slide') {
          slideClasses.value = 'swiper-slide';
        }
      }
    });

    onBeforeUnmount(() => {
      if (!swiperRef || !swiperRef.value) return;
      swiperRef.value.off('_slideClass', updateClasses);
    });

    const slideData = computed(() => ({
      isActive:
        slideClasses.value.indexOf('swiper-slide-active') >= 0 ||
        slideClasses.value.indexOf('swiper-slide-duplicate-active') >= 0,
      isVisible: slideClasses.value.indexOf('swiper-slide-visible') >= 0,
      isDuplicate: slideClasses.value.indexOf('swiper-slide-duplicate') >= 0,
      isPrev:
        slideClasses.value.indexOf('swiper-slide-prev') >= 0 ||
        slideClasses.value.indexOf('swiper-slide-duplicate-prev') >= 0,
      isNext:
        slideClasses.value.indexOf('swiper-slide-next') >= 0 ||
        slideClasses.value.indexOf('swiper-slide-duplicate-next') >= 0,
    }));
    return () => {
      return h(
        props.tag,
        {
          class: uniqueClasses(`${slideClasses.value}`),
          ref: slideElRef,
          'data-swiper-slide-index': props.virtualIndex,
        },
        props.zoom
          ? h(
              'div',
              {
                class: 'swiper-zoom-container',
                'data-swiper-zoom': typeof props.zoom === 'number' ? props.zoom : undefined,
              },
              slots.default && slots.default(slideData.value),
            )
          : slots.default && slots.default(slideData.value),
      );
    };
  },
};

export { SwiperSlide };


// Prevent losing hardware keyboard focus on the terminal when using touch keyboard

document.getElementById("shell_tab0").innerText = "MAIN - "+p;


# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

settings.json
dist
prebuild-src


div.modal_popup {
    position: fixed;
    min-width: 30vw;
    background: var(--color_light_black);
    border-color: rgb(var(--color_r), var(--color_g), var(--color_b));;
    border-radius: 0.278vh;
    padding: 2vh;
    padding-bottom: 0vh;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    cursor: default;
}

div.modal_popup.error {border-width: 0.7vh; border-style: double;}
div.modal_popup.warning {border-width: 0.5vh; border-style: solid;}
div.modal_popup.info {border-width: 0.2vh; border-style: solid;}

div.modal_popup.blink {
    border: none;
    animation-name: blink;
    animation-duration: .1s;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
}

@keyframes blink {
    0% {background: rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);}
    100% {background: rgba(var(--color_r), var(--color_g), var(--color_b), 1);}
}

div.modal_popup > h1 {
    font-size: 4vh;
    margin: 0vh;
    margin-bottom: 2vh;
}

div.modal_popup > h5 {
    font-size: 2vh;
    font-weight: normal;
    margin: 0vh;
    margin-bottom: 2vh;
}

div.modal_popup > h5 > a {
    color: inherit;
}

div.modal_popup > h5 > a:hover {
    font-weight: bold;
}

div.modal_popup > div:last-child {
    width: 100%;
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
    position: relative;
    top: 0.2vh;
}

div.modal_popup button {
    background: transparent;
    border: 0.19vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.7);
    border-radius: 0.278vh;
    color: rgb(var(--color_r), var(--color_g), var(--color_b));
    font-size: 1.85vh;
    font-weight: bold;
    padding: 0.5vh 0.9vh;
    margin-left: 1.5vh;
    border-bottom-left-radius: 0vh;
    border-bottom-right-radius: 0vh;
    padding-bottom: 0.7vh;
    cursor: pointer;
}


Welcome to eDEX-UI!
eDEX Kernel Version 1.0.0: Tue Oct 11 20:56:35 PDT 2011; root:xnu-1699.22.73~1/RELEASE_X86_64
vm_page_bootstrap: 987323 free pages and 53061 wired pages
kext submap [0xffffff7f8072e000 - 0xffffff8000000000], kernel text [0xffffff8000200000 - 0xffffff800072e000]
zone leak detection enabled
standard timeslicing quantum is 10000 us
mig_table_max_displ = 72
TSC Deadline Timer supported and enabled
eDEXACPICPU: ProcessorId=1 LocalApicId=0 Enabled
eDEXACPICPU: ProcessorId=2 LocalApicId=2 Enabled
eDEXACPICPU: ProcessorId=3 LocalApicId=1 Enabled
eDEXACPICPU: ProcessorId=4 LocalApicId=3 Enabled
eDEXACPICPU: ProcessorId=5 LocalApicId=255 Disabled
eDEXACPICPU: ProcessorId=6 LocalApicId=255 Disabled
eDEXACPICPU: ProcessorId=7 LocalApicId=255 Disabled
eDEXACPICPU: ProcessorId=8 LocalApicId=255 Disabled
calling mpo_policy_init for TMSafetyNet
Security policy loaded: Safety net for Rollback (TMSafetyNet)
calling mpo_policy_init for Sandbox
Security policy loaded: Seatbelt sandbox policy (Sandbox)
calling mpo_policy_init for Quarantine
Security policy loaded: Quarantine policy (Quarantine)
Copyright (c) 1982, 1986, 1989, 1991, 1993, 2015
The Regents of the University of Adelaide. All rights reserved.

HN_ Framework successfully initialized
using 16384 buffer headers and 10240 cluster IO buffer headers
IOAPIC: Version 0x20 Vectors 64:87
ACPI: System State [S0 S3 S4 S5] (S3)
PFM64 0xf10000000, 0xf0000000
[ PCI configuration begin ]
eDEXIntelCPUPowerManagement: Turbo Ratios 0046
eDEXIntelCPUPowerManagement: (built 13:08:12 Jun 18 2011) initialization complete
console relocated to 0xf10000000
PCI configuration changed (bridge=16 device=4 cardbus=0)
[ PCI configuration end, bridges 12 devices 16 ]
mbinit: done [64 MB total pool size, (42/21) split]
Pthread support ABORTS when sync kernel primitives misused
com.eDEX.eDEXFSCompressionTypeZlib kmod start
com.eDEX.eDEXTrololoBootScreen kmod start
com.eDEX.eDEXFSCompressionTypeZlib load succeeded
com.eDEX.eDEXFSCompressionTypeDataless load succeeded

eDEXIntelCPUPowerManagementClient: ready
BTCOEXIST off
wl0: Broadcom BCM4331 802.11 Wireless Controller
5.100.98.75

FireWire (OHCI) Lucent ID 5901 built-in now active, GUID c82a14fffee4a086; max speed s800.
rooting via boot-uuid from /chosen: F5670083-AC74-33D3-8361-AC1977EE4AA2
Waiting on <dict ID="0"><key>IOProviderClass</key><string ID="1">
IOResources</string><key>IOResourceMatch</key><string ID="2">boot-uuid-media</string></dict>
Got boot device = IOService:/eDEXACPIPlatformExpert/PCI0@0/eDEXACPIPCI/SATA@1F,2/
eDEXIntelPchSeriesAHCI/PRT0@0/IOAHCIDevice@0/eDEXAHCIDiskDriver/SarahI@sTheBestDriverIOAHCIBlockStorageDevice/IOBlockStorageDriver/
eDEX SSD TS128C Media/IOGUIDPartitionScheme/Customer@2
BSD root: disk0s2, major 14, minor 2
Kernel is LP64
IOThunderboltSwitch::i2cWriteDWord - status = 0xe00002ed
IOThunderboltSwitch::i2cWriteDWord - status = 0x00000000
IOThunderboltSwitch::i2cWriteDWord - status = 0xe00002ed
IOThunderboltSwitch::i2cWriteDWord - status = 0xe00002ed
eDEXUSBMultitouchDriver::checkStatus - received Status Packet, Payload 2: device was reinitialized
MottIsAScrub::checkstatus - true, Mott::Scrub
[IOBluetoothHCIController::setConfigState] calling registerService
AirPort_Brcm4331: Ethernet address e4:ce:8f:46:18:d2
IO80211Controller::dataLinkLayerAttachComplete():  adding eDEXEFINVRAM notification
IO80211Interface::efiNVRAMPublished():
Created virtif 0xffffff800c32ee00 p2p0
BCM5701Enet: Ethernet address c8:2a:14:57:a4:7a
Previous Shutdown Cause: 3
NTFS driver 3.8 [Flags: R/W].
NTFS volume name BOOTCAMP, version 3.1.
DSMOS has arrived
en1: 802.11d country code set to 'US'.
en1: Supported channels 1 2 3 4 5 6 7 8 9 10 11 36 40 44 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 149 153 157 161 165
m_thebest
MacAuthEvent en1   Auth result for: 00:60:64:1e:e9:e4  MAC AUTH succeeded
MacAuthEvent en1   Auth result for: 00:60:64:1e:e9:e4 Unsolicited  Auth
wlEvent: en1 en1 Link UP
AirPort: Link Up on en1
en1: BSSID changed to 00:60:64:1e:e9:e4
virtual bool IOHIDEventSystemUserClient::initWithTask(task*, void*, UInt32):
Client task not privileged to open IOHIDSystem for mapping memory (e00002c1)

Boot Complete


let src = path.join(themesDir, theme+".json" || settings.theme+".json");

if (typeof window.term[i] !== undefined) {

class Terminal {
    constructor(opts) {
        if (opts.role === "client") {
            if (!opts.parentId) throw "Missing options";

            this.xTerm = require("xterm").Terminal;
            this.Ipc = require("electron").ipcRenderer;

            this.port = opts.port || 3000;
            this.cwd = "";
            this.oncwdchange = () => {};

            let attachAddon = require("./node_modules/xterm/lib/addons/attach/attach.js");
            let fitAddon = require("./node_modules/xterm/lib/addons/fit/fit.js");
            this.xTerm.applyAddon(attachAddon);
            this.xTerm.applyAddon(fitAddon);

            this._sendSizeToServer = () => {
                let cols = this.term.cols.toString();
                let rows = this.term.rows.toString();
                while (cols.length < 3) {
                    cols = "0"+cols;
                }
                while (rows.length < 3) {
                    rows = "0"+rows;
                }
                this.Ipc.send("terminal_channel-"+this.port, "Resize", cols, rows);
            };

            let color = require("color");
            let colorify = (base, target) => {
                return color(base).grayscale().mix(color(target), 0.3).hex();
            };
            let themeColor = `rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b})`;

            this.term = new this.xTerm({
                cols: 80,
                rows: 24,
                cursorBlink: window.theme.terminal.cursorBlink || true,
                cursorStyle: window.theme.terminal.cursorStyle || "block",
                allowTransparency: window.theme.terminal.allowTransparency || false,
                fontFamily: window.theme.terminal.fontFamily || "Fira Mono",
                fontSize: window.theme.terminal.fontSize || 15,
                fontWeight: window.theme.terminal.fontWeight || "normal",
                fontWeightBold: window.theme.terminal.fontWeightBold || "bold",
                letterSpacing: window.theme.terminal.letterSpacing || 0,
                lineHeight: window.theme.terminal.lineHeight || 1,
                scrollback: 1500,
                bellStyle: "none",
                theme: {
                    foreground: window.theme.terminal.foreground,
                    background: window.theme.terminal.background,
                    cursor: window.theme.terminal.cursor,
                    cursorAccent: window.theme.terminal.cursorAccent,
                    selection: window.theme.terminal.selection,
                    black: window.theme.colors.black || colorify("#2e3436", themeColor),
                    red: window.theme.colors.red || colorify("#cc0000", themeColor),
                    green: window.theme.colors.green || colorify("#4e9a06", themeColor),
                    yellow: window.theme.colors.yellow || colorify("#c4a000", themeColor),
                    blue: window.theme.colors.blue || colorify("#3465a4", themeColor),
                    magenta: window.theme.colors.magenta || colorify("#75507b", themeColor),
                    cyan: window.theme.colors.cyan || colorify("#06989a", themeColor),
                    white: window.theme.colors.white || colorify("#d3d7cf", themeColor),
                    brightBlack: window.theme.colors.brightBlack || colorify("#555753", themeColor),
                    brightRed: window.theme.colors.brightRed || colorify("#ef2929", themeColor),
                    brightGreen: window.theme.colors.brightGreen || colorify("#8ae234", themeColor),
                    brightYellow: window.theme.colors.brightYellow || colorify("#fce94f", themeColor),
                    brightBlue: window.theme.colors.brightBlue || colorify("#729fcf", themeColor),
                    brightMagenta: window.theme.colors.brightMagenta || colorify("#ad7fa8", themeColor),
                    brightCyan: window.theme.colors.brightCyan || colorify("#34e2e2", themeColor),
                    brightWhite: window.theme.colors.brightWhite || colorify("#eeeeec", themeColor)
                }
            });
            this.term.open(document.getElementById(opts.parentId));
            this.term.focus();

            this.Ipc.send("terminal_channel-"+this.port, "Renderer startup");
            this.Ipc.on("terminal_channel-"+this.port, (e, ...args) => {
                switch(args[0]) {
                    case "New cwd":
                        this.cwd = args[1];
                        this.oncwdchange(this.cwd);
                        break;
                    case "Fallback cwd":
                        this.cwd = "FALLBACK |-- "+args[1];
                        this.oncwdchange(this.cwd);
                        break;
                    default:
                        return;
                }
            });
            this.resendCWD = () => {
                this.oncwdchange(this.cwd || null);
            };

            let sockHost = opts.host || "127.0.0.1";
            let sockPort = this.port;

            this.socket = new WebSocket("ws://"+sockHost+":"+sockPort);
            this.socket.onopen = () => {
                this.term.attach(this.socket);
                this.fit();
                setTimeout(() => {
                    this.fit();
                    if (process.platform === "win32") {
                        // Force Powershell to print a prompt
                        setTimeout(() => {
                            this.write("n");
                            this.write("\b");
                        }, 1000);
                    }
                }, 200);
            };
            this.socket.onerror = e => {throw JSON.stringify(e)};
            this.socket.onclose = e => {
                if (this.onclose) {
                    this.onclose(e);
                }
            };

            let parent = document.getElementById(opts.parentId);
            parent.addEventListener("wheel", e => {
                this.term.scrollLines(Math.round(e.deltaY/10));
            });
            this._lastTouchY = null;
            parent.addEventListener("touchstart", e => {
                this._lastTouchY = e.targetTouches[0].screenY;
            });
            parent.addEventListener("touchmove", e => {
                if (this._lastTouchY) {
                    let y = e.changedTouches[0].screenY;
                    let deltaY = y - this._lastTouchY;
                    this._lastTouchY = y;
                    this.term.scrollLines(-Math.round(deltaY/10));
                }
            });
            parent.addEventListener("touchend", e => {
                this._lastTouch = null;
            });
            parent.addEventListener("touchcancel", e => {
                this._lastTouch = null;
            });

            document.querySelector(".xterm-helper-textarea").addEventListener("keydown", e => {
                if (e.key === "F11" && window.settings.allowWindowed) {
                    e.preventDefault();
                    let win = require("electron").remote.BrowserWindow.getFocusedWindow();
                    let bool = (win.isFullScreen() ? false : true);
                    win.setFullScreen(bool);

                    setTimeout(() => {
                        this.fit();
                    }, 700);
                }
            });

            this.fit = () => {
                this.term.fit();
                setTimeout(() => {
                    this.resize(this.term.cols+1, this.term.rows-1);
                }, 50);
            };

            this.resize = (cols, rows) => {
                this.term.resize(cols, rows);
                this._sendSizeToServer();
            };

            this.write = cmd => {
                this.socket.send(cmd);
            };

            this.writelr = cmd => {
                this.socket.send(cmd+"\r");
            };

            this.clipboard = {
                copy: () => {
                    if (!this.term.hasSelection()) return false;
                    document.execCommand("copy");
                    this.term.clearSelection();
                    this.clipboard.didCopy = true;
                },
                paste: () => {
                    this.Ipc.once("clipboard-reply", (e, txt) => {
                        this.write(txt);
                        this.clipboard.didCopy = false;
                    });
                    this.Ipc.send("clipboard", "read");
                },
                didCopy: false
            };

        } else if (opts.role === "server") {

            this.Pty = require("node-pty");
            this.Websocket = require("ws").Server;
            this.Ipc = require("electron").ipcMain;

            this.renderer = null;
            this.port = opts.port || 3000;

            this._closed = false;
            this.onclosed = () => {};
            this.onopened = () => {};
            this.onresize = () => {};
            this.ondisconnected = () => {};

            this._disableCWDtracking = false;
            this._getTtyCWD = (tty) => {
                return new Promise((resolve, reject) => {
                    let pid = tty._pid;
                    switch(require("os").type()) {
                        case "Linux":
                            require("fs").readlink(`/proc/${pid}/cwd`, (e, cwd) => {
                                if (e !== null) {
                                    reject(e);
                                } else {
                                    resolve(cwd);
                                }
                            });
                            break;
                        case "Darwin":
                            // OK, the following is quite of a hacky solution
                            // Each $XX after the $9 in the awk commands provide support for one more space
                            // character in the path (otherwise it just gets cut)
                            // There's probably a better way to do this, PRs welcome
                            require("child_process").exec(`lsof -a -d cwd -p ${pid} | tail -1 | awk '{print $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20}'`, (e, cwd) => {
                                if (e !== null) {
                                    reject(e);
                                } else {
                                    resolve(cwd.trim());
                                }
                            });
                            break;
                        default:
                            reject("Unsupported OS");
                    }
                });
            };
            this._nextTickUpdateTtyCWD = false;
            this._tick = setInterval(() => {
                if (this._nextTickUpdateTtyCWD && this._disableCWDtracking === false) {
                    this._nextTickUpdateTtyCWD = false;
                    this._getTtyCWD(this.tty).then(cwd => {
                        if (this.tty._cwd === cwd) return;
                        this.tty._cwd = cwd;
                        if (this.renderer) {
                            this.renderer.send("terminal_channel-"+this.port, "New cwd", cwd);
                        }
                    }).catch(e => {
                        if (!this._closed) {
                            console.log("Error while tracking TTY working directory: ", e);
                            this._disableCWDtracking = true;
                            if (this.renderer) {
                                this.renderer.send("terminal_channel-"+this.port, "Fallback cwd", opts.cwd || process.env.PWD);
                            }
                        }
                    });
                }
            }, 1000);

            this.tty = this.Pty.spawn(opts.shell || "bash", opts.params || [], {
                name: "xterm-color",
                cols: 80,
                rows: 24,
                cwd: opts.cwd || process.env.PWD,
                env: process.env
            });

            this.tty.on("exit", (code, signal) => {
                this._closed = true;
                this.onclosed(code, signal);
            });

            this.wss = new this.Websocket({
                port: this.port,
                clientTracking: true,
                verifyClient: (info) => {
                    if (this.wss.clients.length >= 1) {
                        return false;
                    } else {
                        return true;
                    }
                }
            });
            this.Ipc.on("terminal_channel-"+this.port, (e, ...args) => {
                switch(args[0]) {
                    case "Renderer startup":
                        this.renderer = e.sender;
                        if (!this._disableCWDtracking && this.tty._cwd) {
                            this.renderer.send("terminal_channel-"+this.port, "New cwd", this.tty._cwd);
                        }
                        if (this._disableCWDtracking) {
                            this.renderer.send("terminal_channel-"+this.port, "Fallback cwd", opts.cwd || process.env.PWD);
                        }
                        break;
                    case "Resize":
                        let cols = args[1];
                        let rows = args[2];
                        this.tty.resize(Number(cols), Number(rows));
                        this.onresized(cols, rows);
                        break;
                    default:
                        return;
                }
            });
            this.wss.on("connection", (ws) => {
                this.onopened();
                ws.on("close", (code, reason) => {
                    this.ondisconnected(code, reason);
                });
                ws.on("message", (msg) => {
                    this.tty.write(msg);
                });
                this.tty.on("data", (data) => {
                    this._nextTickUpdateTtyCWD = true;
                    try {
                        ws.send(data);
                    } catch (e) {
                        // Websocket closed
                    }
                });
            });

            this.close = () => {
                this.tty.kill();
                this._closed = true;
            };
        } else {
            throw "Unknown purpose";
        }
    }
}

module.exports = {
    Terminal
};


# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.12.0
ignore: {}
# patches apply the minimum changes required to fix a vulnerability
patch:
  'npm:hoek:20180212':
    - electron-rebuild > node-gyp > request > hawk > hoek:
        patched: '2018-06-19T01:45:58.543Z'
    - electron-rebuild > node-gyp > request > hawk > boom > hoek:
        patched: '2018-06-19T01:45:58.543Z'
    - electron-rebuild > node-gyp > request > hawk > sntp > hoek:
        patched: '2018-06-19T01:45:58.543Z'
    - electron-rebuild > node-gyp > request > hawk > cryptiles > boom > hoek:
        patched: '2018-06-19T01:45:58.543Z'


div#mod_toplist {
    border-top: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    font-family: var(--font_main_light);
    letter-spacing: 0.092vh;
    padding: 0.645vh 0vh;
    display: flex;
    flex-wrap: wrap;
}

div#mod_toplist::before {
    content: "";
    border-left: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    align-self: flex-start;
    position: relative;
    left: -0.092vh;
    top: -1.111vh;
    height: 0.833vh;
}

div#mod_toplist::after {
    content: "";
    border-right: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    position: relative;
    right: -15.4vw;
    top: -12.7vh;
    height: 0.833vh;
}

div#mod_toplist h1 {
    font-size: 1.48vh;
    margin: 0vh;
    margin-bottom: -1vh;
    width: 97%;
    padding-left: 0.5vh;
}

div#mod_toplist i {
    font-style: normal;
    font-size: 1.20vh;
    opacity: 0.5;
    text-align: right;
    display: inline-block;
    position: relative;
    bottom: 1.6vh;
    width: 100%;
}

table#mod_toplist_table {
    margin: 0px;
    margin-top: 0.2vh;
    width: 99%;
    padding-left: 0.5vh;
}

table#mod_toplist_table td {
    font-size: 1.50vh;
}

table#mod_toplist_table td:nth-child(2) {
    max-width: 7vw;
    min-width: 7vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

table#mod_toplist_table td:nth-child(3), table#mod_toplist_table td:nth-child(4) {
    text-align: right;
        max-width: 2.4vw;
    min-width: 2.4vw;
}


edex-ui

A science fiction desktop running everywhere. Awesome.

desktop sci-fi gui portable tty terminal

npm install && cd src && npm install && ./../node_modules/.bin/electron-rebuild -f -w node-pty && cd ..

npm install --global --production windows-build-tools && npm install --global node-gyp && setx PYTHON "%USERPROFILE%\.windows-build-tools\python27\python.exe"

npm install && cd src && npm install && ..\node_modules\.bin\electron-rebuild -f -w node-pty && cd ..

mkdir prebuild-src && cp -Rv src/* prebuild-src/ && node prebuild-minify.js && cd prebuild-src && npm install

mkdir prebuild-src && xcopy src\* prebuild-src\ /E /C /Q /Y && node prebuild-minify.js && cd prebuild-src && npm install

./node_modules/.bin/electron-builder build -l -p never

./node_modules/.bin/electron-builder build -m -p never

node_modules\.bin\electron-builder -w -p never

rsync -a --info=progress2 src prebuild-src --exclude node_modules && node prebuild-minify.js && rm -R prebuild-src

Copyright © 2017-2018 GitSquared <squared@codebrew.fr>

Output: dist


// Disable eval()
window.eval = global.eval = function () {
    throw new Error("eval() is disabled for security reasons.");
};
// Security helper :)
window._escapeHtml = (text) => {
    let map = {
        '&': '&',
        '<': '<',
        '>': '>',
        '"': '"',
        "'": '''
    };
    return text.replace(/[&<>"']/g, m => {return map[m];});
};
window._purifyCSS = (str) => {
    return str.replace(/[<]/g, "");
};

// Initiate basic error handling
window.onerror = (msg, path, line, col, error) => {
    document.getElementById("boot_screen").innerHTML += `${error} :  ${msg}<br/>==> at ${path}  ${line}:${col}`;
};

const path = require("path");
const fs = require("fs");
const electron = require("electron");
const ipc = electron.ipcRenderer;

const settingsDir = electron.remote.app.getPath("userData");
const themesDir = path.join(settingsDir, "themes");
const keyboardsDir = path.join(settingsDir, "keyboards");
const fontsDir = path.join(settingsDir, "fonts");
const settingsFile = path.join(settingsDir, "settings.json");

// Load config
window.settings = require(settingsFile);

// Load UI theme
window._loadTheme = (theme) => {

    if (document.querySelector("style.theming")) {
        document.querySelector("style.theming").remove();
    }

    document.querySelector("head").innerHTML += ``;

    window.theme = theme;
    window.theme.r = theme.colors.r;
    window.theme.g = theme.colors.g;
    window.theme.b = theme.colors.b;
};

_loadTheme(require(path.join(themesDir, settings.theme+".json")));

// Startup boot log
let resumeInit, initUI, initMods, initGreeter;
let bootScreen = document.getElementById("boot_screen");
let log = fs.readFileSync(path.join(__dirname, 'assets/misc/boot_log.txt')).toString().split('\n');
let i = 0;
displayLine();

function displayLine() {
    if (log[i] === undefined) {
        setTimeout(resumeInit, 300);
        return;
    }
    bootScreen.innerHTML += log[i]+"
";
    i++;

    switch(true) {
        case i === 4:
            setTimeout(displayLine, 500);
            break;
        case i > 4 && i < 25:
            setTimeout(displayLine, 30);
            break;
        case i === 25:
            setTimeout(displayLine, 400);
            break;
        case i === 42:
            setTimeout(displayLine, 300);
            break;
        case i > 42 && i < 82:
            setTimeout(displayLine, 25);
            break;
        case i >= log.length-2 && i < log.length:
            setTimeout(displayLine, 300);
            break;
        default:
            setTimeout(displayLine, Math.pow(1 - (i/1000), 3)*25);
    }
}

// Show "logo" and background grid
resumeInit = () => {
    bootScreen.innerHTML = "";
    setTimeout(() => {
        document.body.setAttribute("class", "");
        setTimeout(() => {
            document.body.setAttribute("class", "solidBackground");
            setTimeout(() => {
                document.body.setAttribute("class", "");
            }, 400);
        }, 200);

        bootScreen.setAttribute("class", "center");
        bootScreen.innerHTML = "
eDEX-UI
";
        let title = document.querySelector("section > h1");

        setTimeout(() => {
            title.setAttribute("style", `background-color: rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});border-bottom: 5px solid rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});`);
            setTimeout(() => {
                title.setAttribute("style", `border: 5px solid rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b});`);
                setTimeout(() => {
                    // Initiate graphical error display
                    window.edexErrorsModals = [];
                    window.onerror = (msg, path, line, col, error) => {
                        let errorModal = new Modal({
                            type: "error",
                            title: error,
                            message: `${msg}<br/>        at ${path}  ${line}:${col}`
                        });
                        window.edexErrorsModals.push(errorModal);

                        ipc.send("log", "error", `${error}: ${msg}`);
                        ipc.send("log", "debug", `at ${path} ${line}:${col}`);
                    };
                    document.getElementById("boot_screen").remove();
                    initUI();
                }, 1200);
            }, 600);
        }, 300);
    }, 400);
};

// Create the UI's html structure and initialize the terminal client and the keyboard
initUI = () => {
    document.body.innerHTML += `

PANEL

SYSTEM

TERMINAL

MAIN SHELL

PANEL

NETWORK

`;

    setTimeout(() => {
        document.getElementById("main_shell").setAttribute("style", "height:0%;margin-bottom:30vh;");
        setTimeout(() => {
            document.getElementById("main_shell").setAttribute("style", "margin-bottom: 30vh;");
            document.querySelector("#main_shell > h3.title").setAttribute("style", "");
            setTimeout(() => {
                document.getElementById("main_shell").setAttribute("style", "opacity: 0;");
                document.body.innerHTML += `

`;
                window.keyboard = new Keyboard({
                    layout: path.join(keyboardsDir, settings.keyboard+".json"),
                    container: "keyboard"
                });
                setTimeout(() => {
                    document.getElementById("main_shell").setAttribute("style", "");
                    setTimeout(() => {
                        initGreeter();
                        document.getElementById("filesystem").setAttribute("style", "");
                        document.getElementById("keyboard").setAttribute("style", "");
                        document.getElementById("keyboard").setAttribute("class", "animation_state_1");
                        setTimeout(() => {
                            document.getElementById("keyboard").setAttribute("class", "animation_state_1 animation_state_2");
                            setTimeout(() => {
                                document.getElementById("keyboard").setAttribute("class", "");
                                initMods();
                            }, 1100);
                        }, 100);
                    }, 270);
                }, 10);
            }, 700);
        }, 500);
    }, 10);
};

// Create the "mods" in each column
initMods = () => {
    window.mods = {};

    // Left column
    window.mods.clock = new Clock("mod_column_left");
    window.mods.sysinfo = new Sysinfo("mod_column_left");
    window.mods.cpuinfo = new Cpuinfo("mod_column_left");
    window.mods.ramwatcher = new RAMwatcher("mod_column_left");
    window.mods.toplist = new Toplist("mod_column_left");
    window.mods.clipboardButtons = new ClipboardButtons("mod_column_left");

    // Right column
    window.mods.netstat = new Netstat("mod_column_right");
    window.mods.globe = new LocationGlobe("mod_column_right");
    window.mods.conninfo = new Conninfo("mod_column_right");

    // Fade-in animations
    document.querySelectorAll(".mod_column").forEach((e) => {
        e.setAttribute("class", "mod_column activated");
    });

    let i = 0;
    let left = document.querySelectorAll("#mod_column_left > div");
    let right = document.querySelectorAll("#mod_column_right > div");
    let x = setInterval(() => {
        if (!left[i] && !right[i]) {
            clearInterval(x);
        } else {
            if (left[i]) {
                left[i].setAttribute("style", "animation-play-state: running;");
            }
            if (right[i]) {
                right[i].setAttribute("style", "animation-play-state: running;");
            }
            i++;
        }
    }, 500);
};

initGreeter = () => {
    let shellContainer = document.getElementById("main_shell");
    let greeter = document.getElementById("main_shell_greeting");

    require("systeminformation").users()
        .then((userlist) => {
            greeter.innerHTML += `Welcome back, <em>${userlist[0].user}</em>`;
        })
        .catch(() => {
            greeter.innerHTML += "Welcome back";
        })
    .then(() => {
        greeter.setAttribute("style", "opacity: 1;");
        setTimeout(() => {
            greeter.setAttribute("style", "opacity: 0;");
            setTimeout(() => {
                greeter.remove();
                setTimeout(() => {
                    shellContainer.innerHTML += `

MAIN SHELL

EMPTY

EMPTY

EMPTY

EMPTY

`;
                    window.term = {
                        0: new Terminal({
                            role: "client",
                            parentId: "terminal0",
                            port: window.settings.port || 3000
                        })
                    };
                    window.currentTerm = 0;
                    // Prevent losing hardware keyboard focus on the terminal when using touch keyboard
                    window.onmouseup = (e) => {
                        window.term[window.currentTerm].term.focus();
                    };

                    window.fsDisp = new FilesystemDisplay({
                        parentId: "filesystem"
                    });

                    setTimeout(() => {
                        document.getElementById("filesystem").setAttribute("style", "opacity: 1;");
                        window.updateCheck = new UpdateChecker();
                    }, 300);
                }, 100);
            }, 500);
        }, 1100);
    });
};

window.themeChanger = (theme) => {
    window.focusShellTab(0);

    let src = path.join(themesDir, theme+".json" || settings.theme+".json");
    // Always get fresh theme files
    delete require.cache[src];

    window.mods.globe.globe.destroy();
    window.removeEventListener("resize", window.mods.globe.resizeHandler);

    window._loadTheme(require(src));
    for (let i; i < 99999; i++) {
        clearInterval(i);
    }
    window.term[window.currentTerm].socket.close();
    delete window.term[window.currentTerm];
    delete window.mods;
    delete window.fsDisp;

    document.getElementById("terminal0").innerHTML = "";
    document.querySelectorAll(".mod_column").forEach((e) => {
        e.setAttribute("class", "mod_column");
    });
    document.querySelectorAll(".mod_column > div").forEach(e => {e.remove()});
    document.querySelectorAll("div.smoothie-chart-tooltip").forEach(e => {e.remove()});

    window.term = {
        0: new Terminal({
            role: "client",
            parentId: "terminal0",
            port: window.settings.port || 3000
        })
    };
    window.currentTerm = 0;
    initMods();
    window.fsDisp = new FilesystemDisplay({
        parentId: "filesystem"
    });

    setTimeout(() => {
        window.term[window.currentTerm].fit();
    }, 2700);
};

window.remakeKeyboard = (layout) => {
    document.getElementById("keyboard").innerHTML = "";
    window.keyboard = new Keyboard({
        layout: path.join(keyboardsDir, layout+".json" || settings.keyboard+".json"),
        container: "keyboard"
    });
};

window.focusShellTab = (number) => {
    if (number !== window.currentTerm && window.term[number]) {
        window.currentTerm = number;

        document.querySelectorAll(`ul#main_shell_tabs > li:not(:nth-child(${number+1}))`).forEach(e => {
            e.setAttribute("class", "");
        });
        document.getElementById("shell_tab"+number).setAttribute("class", "active");

        document.querySelectorAll(`div#main_shell_innercontainer > pre:not(:nth-child(${number+1}))`).forEach(e => {
            e.setAttribute("class", "");
        });
        document.getElementById("terminal"+number).setAttribute("class", "active");

        window.term[number].fit();
        window.term[number].term.focus();
        window.term[number].resendCWD();
    } else if (number > 0 && number <= 4 && window.term[number] !== null) {
        window.term[number] = null;

        document.getElementById("shell_tab"+number).innerText = "LOADING...";
        ipc.send("ttyspawn", "true");
        ipc.once("ttyspawn-reply", (e, r) => {
            if (r.startsWith("ERROR")) {
                document.getElementById("shell_tab"+number).innerText = "ERROR";
            } else if (r.startsWith("SUCCESS")) {
                let port = Number(r.substr(9));

                window.term[number] = new Terminal({
                    role: "client",
                    parentId: "terminal"+number,
                    port
                });

                window.term[number].onclose = e => {
                    document.getElementById("shell_tab"+number).innerText = "EMPTY";
                    document.getElementById("terminal"+number).innerHTML = "";
                    delete window.term[number];
                    window.focusShellTab(0);
                };

                document.getElementById("shell_tab"+number).innerText = "::"+port;
                setTimeout(() => {
                    window.focusShellTab(number);
                }, 500);
            }
        });
    }
};

// Global keyboard shortcuts
const globalShortcut = electron.remote.globalShortcut;
globalShortcut.register("CommandOrControl+Tab", () => {
    if (window.currentTerm <= 3) {
        window.focusShellTab(window.currentTerm+1);
    } else {
        window.focusShellTab(0);
    }
});

// Prevent showing menu, exiting fullscreen or app with keyboard shortcuts
window.onkeydown = e => {
    if (e.key === "Alt") {
        e.preventDefault();
    }
    if (e.key === "F11" && !settings.allowWindowed) {
        e.preventDefault();
    }
    if (e.code === "KeyD" && e.ctrlKey) {
        e.preventDefault();
    }
    if (e.code === "KeyA" && e.ctrlKey) {
        e.preventDefault();
    }
};

// Fix double-tap zoom on touchscreens
require('electron').webFrame.setVisualZoomLevelLimits(1, 1);

// Resize terminal with window
window.onresize = () => {
    window.term[window.currentTerm].fit();
}


section#filesystem{left: 0;width:100vw} section#filesystem>h3.title, section#filesystem>div{width:100vw} section#keyboard{display:none;}


section#keyboard {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 56vw;
    position: relative;
    top: -0.925vh;
}

div.keyboard_row {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    flex-wrap: nowrap;
    margin: 0.46vh 0vh;
    height: 5.28vh;
}

section#keyboard.animation_state_2 > div.keyboard_row:nth-child(1) {
    transition: width .7s cubic-bezier(0.4, 0, 1, 1) .3s, filter .1s linear .8s;
}
section#keyboard.animation_state_2 > div.keyboard_row:nth-child(2) {
    transition: width .7s cubic-bezier(0.4, 0, 1, 1) .2s, filter .1s linear .6s;
}
section#keyboard.animation_state_2 > div.keyboard_row:nth-child(3) {
    transition: width .7s cubic-bezier(0.4, 0, 1, 1), filter .1s linear .5s;
}
section#keyboard.animation_state_2 > div.keyboard_row:nth-child(4) {
    transition: width .7s cubic-bezier(0.4, 0, 1, 1) .25s, filter .1s linear .8s;
}
section#keyboard.animation_state_2 > div.keyboard_row:nth-child(5) {
    transition: width .6s cubic-bezier(0.4, 0, 1, 1) .2s, filter .1s linear .6s;
}

section#keyboard.animation_state_1 > * {
    filter: brightness(170%);
    width: 0vh;
    overflow: hidden;
}

section#keyboard.animation_state_2 > * {
    filter: brightness(100%);
    width: 100vw;
    overflow: hidden;
}

div.keyboard_row:last-child {
    position: relative;
    left: 2.4vh;
}

div.keyboard_key {
    height: 2.7vw;
    min-width: 2.7vw;
    overflow: hidden;
    border-radius: 0.46vh;
    background-color: rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);
    border: 0.18vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);
    margin: 0vh 0.46vh;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    cursor: pointer;
}

div.keyboard_key:active, div.keyboard_key.active {
    border: 0.18vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);
    background-color: rgba(var(--color_r), var(--color_g), var(--color_b), 1);
}

div.keyboard_key.blink {
    animation-name: blink;
    animation-duration: .5s;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
}

@keyframes blink {
    0% {background-color: rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);}
    50% {background-color: rgba(var(--color_r), var(--color_g), var(--color_b), 1);}
    100% {background-color: rgba(var(--color_r), var(--color_g), var(--color_b), 0.0);}
}

div.keyboard_key > * {
    margin: 0vh 0.46vh;
    padding: 0vh;
    -webkit-margin-after: 0vh;
    -webkit-margin-before: 0vh;
}

div.keyboard_key > h1, div.keyboard_key > h4, div.keyboard_row > .keyboard_key:last-child > *, div.keyboard_row > .keyboard_key:first-child > * {
    position: relative;
    top: 0.278vh;
    font-size: 2.4vh;
}

div.keyboard_row:not(:nth-child(4)) > .keyboard_key:last-child > *, div.keyboard_row > .keyboard_key:first-child > *, div.keyboard_row:last-child > .keyboard_key > *, div.keyboard_row:nth-child(4) > .keyboard_key:nth-last-child(2) > * {
    font-size: 1.85vh;
}

div.keyboard_row:last-child > .keyboard_key:nth-last-child(-n+3) > * {
    font-size: 2.4vh;
}

div.keyboard_key > *:not(h1) {
    margin: 0vh;
    font-size: 1.67vh;
    position: absolute;
}

div.keyboard_key > h2 {
    top: 0.278vh;
    left: 0.278vh;
}

div.keyboard_key > h3 {
    bottom: 0.278vh;
    right: 0.278vh;
}

div.keyboard_key > h5 {
    top: 0.278vh;
    right: 0.278vh;
}

div.keyboard_key > h4, div.keyboard_row > .keyboard_key:last-child > *:not(h1), div.keyboard_row > .keyboard_key:first-child > *:not(h1) {
    opacity: 0;
    position: absolute;
}

div.keyboard_row:not(:nth-child(4)):not(:last-child) > .keyboard_key:last-child, div.keyboard_row > .keyboard_key:first-child {
    width: 8.33vh;
    min-width: 8.33vh;
}

div.keyboard_key#keyboard_spacebar {
    border: 0.19vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.5);
}

div.keyboard_row#row_1 > div.keyboard_enter {
    border: 0.19vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.5);
    border-bottom-right-radius: 0vh;
    width: 9.72vh;
    min-width: 9.72vh;
}

div.keyboard_row#row_2 > div.keyboard_enter {
    border: 0.19vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.5);
    border-top-left-radius: 0vh;
    border-top: 0.3vh solid var(--color_light_black);
    width: 7.78vh;
    min-width: 7.78vh;
    margin-top: 0vh;
    height: 6.389vh;
    top: -0.37vh;
    right: -1vh;
}

div.keyboard_row#row_2 > div.keyboard_enter:active, div.keyboard_row#row_2 > div.keyboard_enter.active {
    border-top: 0.3vh solid rgb(var(--color_r), var(--color_g), var(--color_b));
}

div.keyboard_key#keyboard_spacebar {
    width: 47.68vh;
    min-width: 47.68vh;
    height: 3.52vh;
}

div.keyboard_row:nth-child(4) > div.keyboard_key:last-child {
    position: relative;
    top: 0.278vh;
}

div.keyboard_row:last-child > div.keyboard_key:nth-last-child(-n+3) {
    margin: 0vh;
    position: relative;
    bottom: 0.278vh;
}

section#keyboard[data-is-caps-lck-on="true"] div.keyboard_key[data-cmd="ESCAPED|-- CAPSLCK: ON"] {
    background-color: rgb(var(--color_r), var(--color_g), var(--color_b));
    color: var(--color_black);
}

section#keyboard[data-is-shift-on="true"] > div.keyboard_row > div.keyboard_key > h2:not(:empty), section#keyboard[data-is-caps-lck-on="true"] > div.keyboard_row > div.keyboard_key > h2:not(:empty) {
    position: relative;
    top: 0.278vh;
    font-size: 2.4vh;
    margin: 0vh 0.46vh;
}

section#keyboard[data-is-shift-on="true"] > div.keyboard_row:first-child > div.keyboard_key:last-child > h2, section#keyboard[data-is-caps-lck-on="true"] > div.keyboard_row:first-child > div.keyboard_key:last-child > h2 {
    font-size: 1.85vh;
    opacity: 1;
}

section#keyboard[data-is-shift-on="true"] > div.keyboard_row > div.keyboard_key > h2:not(:empty) + h1, section#keyboard[data-is-caps-lck-on="true"] > div.keyboard_row > div.keyboard_key > h2:not(:empty) + h1 {
    margin: 0vh;
    font-size: 1.67vh;
    position: absolute;
    top: 0.278vh;
    left: 0.278vh;
}

section#keyboard[data-is-shift-on="true"] > div.keyboard_row:first-child > div.keyboard_key:last-child > h2 + h1, section#keyboard[data-is-caps-lck-on="true"] > div.keyboard_row:first-child > div.keyboard_key:last-child > h2 + h1 {
    opacity: 0;
}

section#keyboard[data-is-fn-on="true"] div.keyboard_key[data-cmd="ESCAPED|-- FN: ON"] {
    background-color: rgb(var(--color_r), var(--color_g), var(--color_b));
    color: var(--color_black);
}

section#keyboard[data-is-fn-on="true"] > div.keyboard_row > div.keyboard_key > h4:not(:empty) {
    position: relative;
    top: 0.278vh;
    font-size: 2.4vh;
    margin: 0vh 0.46vh;
    opacity: 1;
}

section#keyboard[data-is-fn-on="true"] > div.keyboard_row > div.keyboard_key > h4:not(:empty) + h3, section#keyboard[data-is-fn-on="true"] > div.keyboard_row > div.keyboard_key > h4:not(:empty) + h3 + h2, section#keyboard[data-is-fn-on="true"] > div.keyboard_row > div.keyboard_key > h4:not(:empty) + h3 + h2 + h1 {
    position: absolute;
    opacity: 0;
}


section.mod_column {
    width: 17%;
    top: 2.5vh;
    min-height: 10%;
    max-height: 96%;
    padding: 1.39vh;
    padding-bottom: 0vh;
    box-sizing: border-box;
    position: absolute;
    display: flex;
    flex-direction: column;
    justify-content: space-between;

    opacity: 0;
    transition: opacity .5s cubic-bezier(0.4, 0, 1, 1);
}

section.mod_column.activated {
    opacity: 1;
}

section.mod_column > h3.title {
    top: 0.74vh;
    width: 14.8%;
}

section.mod_column > div {
    width: 100%;
    opacity: 0;
    animation-name: fadeIn;
    animation-duration: .5s;
    animation-timing-function: cubic-bezier(0.4, 0, 1, 1);
    animation-delay: 0s;
    animation-iteration-count: 1;
    animation-fill-mode: forwards;
    animation-play-state: paused;
}

@keyframes fadeIn {
    from {opacity: 0;}
    to {opacity: 1;}
}

section#mod_column_left {
    left: -0.555vh;
    align-items: flex-end;
}

section#mod_column_left > h3.title {
    left: 0.555vh;
}

section#mod_column_right {
    right: -0.555vh;
    align-items: flex-start;
}

section#mod_column_right > h3.title {
    right: 0.555vh;
}


keep keep keep keep add keep keep keep keep keep


class Toplist {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        this.si = require("systeminformation");

        // Create DOM
        this.parent = document.getElementById(parentId);
        this._element = document.createElement("div");
        this._element.setAttribute("id", "mod_toplist");
        this._element.innerHTML = `
TOP PROCESSESPID | NAME | CPU | MEM

`;

        this.parent.append(this._element);

        this.updateList();
        this.listUpdater = setInterval(() => {
            this.updateList();
        }, 5000);
    }
    updateList() {
        this.si.processes().then(data => {
            let list = data.list.sort((a, b) => {
                return ((b.pcpu-a.pcpu)*100 + b.pmem-a.pmem);
            }).splice(0, 5);

            document.querySelectorAll("#mod_toplist_table > tr").forEach(el => {
                el.remove();
            });
            list.forEach(proc => {
                let el = document.createElement("tr");
                el.innerHTML = `${proc.pid}
                                ${proc.name}
                                ${Math.round(proc.pcpu*10)/10}%
                                ${Math.round(proc.pmem*10)/10}%	`;
                document.getElementById("mod_toplist_table").append(el);
            });
        });
    }
}

module.exports = {
    Toplist
};


window.modals = [];

class Modal {
    constructor(options) {
        if (!options || !options.type) throw "Missing parameters";

        this.type = options.type;
        this.id = window.modals.length;
        this.title = options.title || options.type || "Modal window";
        this.message = options.message || "Lorem ipsum dolor sit amet.";
        let classes = "modal_popup";
        let buttons = [];
        let zindex = 0;

        // Reserve a slot in window.modals
        window.modals[this.id] = {};

        switch(this.type) {
            case "error":
                classes += " error";
                zindex = 1500;
                buttons.push({label:"PANIC", action:"window.modals["+this.id+"].close();"}, {label:"RELOAD", action:"window.location.reload(true);"});
                break;
            case "warning":
                classes += " warning";
                zindex = 1000;
                buttons.push({label:"OK", action:"window.modals["+this.id+"].close();"});
                break;
            default:
                classes += " info";
                zindex = 500;
                buttons.push({label:"OK", action:"window.modals["+this.id+"].close();"});
                break;
        }

        let DOMstring = `

${this.title}

${this.message}

`;
            buttons.forEach((b) => {
                DOMstring += `<button onclick="${b.action}">${b.label}</button>`;
            });
        DOMstring += `

`;

        this.close = () => {
            let modalElement = document.getElementById("modal_"+this.id);
            modalElement.setAttribute("class", "modal_popup "+this.type+" blink");
            setTimeout(() => {
                modalElement.remove();
            }, 100);
        };

        let tmp = document.createElement("div");
        tmp.innerHTML = DOMstring;
        let element = tmp.firstChild;

        window.modals[this.id] = this;
        document.body.appendChild(element);
        return this.id;
    }
}

module.exports = {
    Modal
};


window.term[number].onprocesschange = p => {

document.getElementById("shell_tab"+number).innerText = `#${number} - ${p}`;


class Conninfo {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        this.si = require("systeminformation");

        // Create DOM
        this.parent = document.getElementById(parentId);
        this.parent.innerHTML += `

NETWORK TRAFFICUP / DOWN, MB/S

TOTAL0B OUT, 0B IN

                <canvas id="mod_conninfo_canvas_top"></canvas>
                <canvas id="mod_conninfo_canvas_bottom"></canvas>

OFFLINE

`;

        this.total = document.querySelector("#mod_conninfo_innercontainer > h2 > i");
        this._pb = require("pretty-bytes");

        // Init Smoothie
        let TimeSeries = require("smoothie").TimeSeries;
        let SmoothieChart = require("smoothie").SmoothieChart;

        // Set chart options
        let chartOptions = [{
            limitFPS: 40,
            responsive: true,
            millisPerPixel: 70,
            interpolation: 'linear',
            grid:{
                millisPerLine: 5000,
                fillStyle:'transparent',
                strokeStyle:`rgba(${window.theme.r},${window.theme.g},${window.theme.b},0.4)`,
                verticalSections:3,
                borderVisible:false
            },
            labels:{
                fontSize: 10,
                fillStyle: `rgb(${window.theme.r},${window.theme.g},${window.theme.b})`,
                precision: 2
            }
        }];
        chartOptions.push(Object.assign({}, chartOptions[0]));  // Deep copy object, see http://jsben.ch/bWfk9
        chartOptions[0].minValue = 0;
        chartOptions[1].maxValue = 0;

        // Create chart
        this.series = [new TimeSeries(), new TimeSeries()];
        this.charts = [new SmoothieChart(chartOptions[0]), new SmoothieChart(chartOptions[1])];

        this.charts[0].addTimeSeries(this.series[0], {lineWidth:1.7,strokeStyle:`rgb(${window.theme.r},${window.theme.g},${window.theme.b})`});
        this.charts[1].addTimeSeries(this.series[1], {lineWidth:1.7,strokeStyle:`rgb(${window.theme.r},${window.theme.g},${window.theme.b})`});

        this.charts[0].streamTo(document.getElementById("mod_conninfo_canvas_top"), 1000);
        this.charts[1].streamTo(document.getElementById("mod_conninfo_canvas_bottom"), 1000);

        // Init updater
        this.updateInfo();
        this.infoUpdater = setInterval(() => {
            this.updateInfo();
        }, 1000);
    }
    updateInfo() {
        this.si.networkInterfaces((data) => {
            let net = data[0];
            let netID = 0;
            while (net.internal === true) {
                netID++;
                if (data[netID] !== undefined) {
                    net = data[netID];
                } else {
                    break;
                }
            }

            let time = new Date().getTime();

            if (window.mods.netstat.offline) {
                this.series[0].append(time, 0);
                this.series[1].append(time, 0);
                document.querySelector("div#mod_conninfo").setAttribute("class", "offline");
                return;
            } else {
                document.querySelector("div#mod_conninfo").setAttribute("class", "");
                this.si.networkStats(net.iface, (data) => {

                    let max0 = this.series[0].maxValue;
                    let max1 = -this.series[1].minValue;
                    if (max0 > max1) {
                        this.series[1].minValue = -max0;
                    } else if (max1 > max0) {
                        this.series[0].maxValue = max1;
                    }

                    this.series[0].append(time, data.tx_sec/125000);
                    this.series[1].append(time, -data.rx_sec/125000);

                    this.total.innerText = `${this._pb(data.tx)} OUT, ${this._pb(data.rx)} IN`.toUpperCase();
                });
            }
        });
    }
}

module.exports = {
    Conninfo
};


class Clock {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        // Create DOM
        this.parent = document.getElementById(parentId);
        this.parent.innerHTML += `

??:??:??

`;

        this.updateClock();
        this.updater = setInterval(() => {
            this.updateClock();
        }, 1000);
    }
    updateClock() {
        let time = new Date();
        let array = [time.getHours(), time.getMinutes(), time.getSeconds()];
        array.forEach((e, i) => {
            if (e.toString().length !== 2) {
                array[i] = "0"+e;
            }
        });
        let clockString = `${array[0]}:${array[1]}:${array[2]}`;
        array = clockString.match(/.{1}/g);
        clockString = "";
        array.forEach((e) => {
            if (e === ":") clockString += ""+e+"";
            else clockString += ""+e+"";
        });
        document.getElementById("mod_clock_text").innerHTML = clockString;
    }
}

module.exports = {
    Clock
};


class ClipboardButtons {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        // Create DOM
        this.parent = document.getElementById(parentId);
        this._element = document.createElement("div");
        this._element.setAttribute("id", "mod_clipboardButtons");
        this._element.innerHTML = `

CLIPBOARD ACCESS

COPY

PASTE

`;

        this.parent.append(this._element);

        document.querySelector("div#mod_clipboardButtons_inner > div:nth-child(2)").addEventListener("click", e => {
            window.term.clipboard.copy();
        });
        document.querySelector("div#mod_clipboardButtons_inner > div:last-child").addEventListener("click", e => {
            window.term.clipboard.paste();
        });
    }
}

module.exports = {
    ClipboardButtons
};


CANNOT ACCESS CURRENT WORKING DIRECTORY
`;

window.term[window.currentTerm].oncwdchange = (cwd) => {

keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace keep

window.term[num].oncwdchange = (cwd) => {

if (cwd && window.currentTerm === num) {


<svg xmlns="http://www.w3.org/2000/svg" height="459.04mm" width="459.04mm" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 459.03571 459.03571"><defs><linearGradient id="linearGradient4535" y2="-13.833" gradientUnits="userSpaceOnUse" x2="-30.735" y1="267.26" x1="249.6"><stop stop-color="#373737" offset="0"/><stop stop-color="#1f1f1f" offset="1"/></linearGradient></defs><g transform="translate(126.89 109.98)"><circle cx="102.63" stroke="url(#linearGradient4535)" cy="119.53" r="224.52" stroke-width="10" fill="#1f1f1f"/><path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;font-variant-numeric:normal;shape-rendering:auto;font-variant-ligatures:normal;text-decoration-line:none;font-variant-position:normal;mix-blend-mode:normal;solid-color:#000000;font-feature-settings:normal;shape-padding:0;font-variant-alternates:normal;text-indent:0;dominant-baseline:auto;font-variant-caps:normal;image-rendering:auto;white-space:normal;text-decoration-style:solid;text-orientation:mixed;isolation:auto;text-transform:none" d="m-30.735-13.833v266.73h266.74v-266.73zm15.997 15.997h234.74v234.74h-234.74z" fill="#eaeaeb"/><g aria-label="e" transform="matrix(3.7504 0 0 3.7504 -344.72 -397.96)" fill="#eaeaeb"><path d="m108.83 159.42-7.4083-7.4083v-28.046l7.4083-7.4083h20.902l7.4083 7.4083v16.922h-26.576v7.6387l2.4995 2.5815h13.328l1.7691-2.1032v-4.7438h8.9792v8.6327l-6.4382 6.5264zm1.8168-26.434h17.432v-6.1964l-2.2474-2.4113h-12.931l-2.2536 2.9032z" stroke-width=".26458" fill="#eaeaeb"/></g></g></svg>


section#boot_screen {
    position: fixed;
    top: 0vh;
    left: 0vh;
    width: 100%;
    height: 100%;
    padding: 0vh;
    margin: 0vh;
    overflow: hidden;

    font-family: monospace;
    font-size: 1.4vh;
    text-align: left;
    display: flex;
    align-items: flex-end;
    justify-content: flex-start;
}

section#boot_screen.center {
    align-items: center;
    justify-content: center;
}

section#boot_screen h1 {
    font-family: var(--font_main);
    font-size: 10vh;
    text-align: center;
    border-bottom: 0.46vh solid rgb(var(--color_r), var(--color_g), var(--color_b));
    padding-top: 2vh;
    padding-right: 2vh;
    padding-left: 1.5vh;
    background-color: transparent;
}


};
this.watchFS = (dir) => {
if (this._fsWatcher) {


class Cpuinfo {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        this.si = require("systeminformation");

        // Create initial DOM
        this.parent = document.getElementById(parentId);
        this.parent.innerHTML += `

`;
        this.container = document.getElementById("mod_cpuinfo");

        // Init Smoothie
        let TimeSeries = require("smoothie").TimeSeries;
        let SmoothieChart = require("smoothie").SmoothieChart;

        this.series = [];
        this.charts = [];
        this.si.cpu((data) => {
            let divide = Math.floor(data.cores/2);
            this.divide = divide;

            let innercontainer = document.createElement("div");
            innercontainer.setAttribute("id", "mod_cpuinfo_innercontainer");
            innercontainer.innerHTML = `
CPU USAGE${data.manufacturer} ${data.brand}

# 1 - ${divide}

                    Avg. --%

                    <canvas id="mod_cpuinfo_canvas_0" height="60"></canvas>

# ${divide+1} - ${data.cores}

                    Avg. --%

                    <canvas id="mod_cpuinfo_canvas_1" height="60"></canvas>

TEMP

                        --°C

MIN

                        --GHz

MAX

                        --GHz

TASKS

                        ---

`;
            this.container.append(innercontainer);

            for (var i = 0; i < 2; i++) {
                this.charts.push(new SmoothieChart({
                    limitFPS: 30,
                    responsive: true,
                    millisPerPixel: 50,
                    grid:{
                        fillStyle:'transparent',
                        strokeStyle:'transparent',
                        verticalSections:0,
                        borderVisible:false
                    },
                    labels:{
                        disabled: true
                    },
                    yRangeFunction: () => {
                        return {min:0,max:100};
                    }
                }));
            }

            for (var i = 0; i < data.cores; i++) {
                // Create TimeSeries
                this.series.push(new TimeSeries());

                let serie = this.series[i];
                let options = {
                    lineWidth: 1.7,
                    strokeStyle: `rgb(${window.theme.r},${window.theme.g},${window.theme.b})`
                };

                if (i < divide) {
                    this.charts[0].addTimeSeries(serie, options);
                } else {
                    this.charts[1].addTimeSeries(serie, options);
                }
            }

            for (var i = 0; i < 2; i++) {
                this.charts[i].streamTo(document.getElementById(`mod_cpuinfo_canvas_${i}`), 500);
            }

            // Init updater
            this.updateCPUload();
            this.updateCPUtemp();
            this.updateCPUspeed();
            this.updateCPUtasks();
            this.loadUpdater = setInterval(() => {
                this.updateCPUload();
            }, 500);
            this.tempUpdater = setInterval(() => {
                this.updateCPUtemp();
            }, 2000);
            this.speedUpdater = setInterval(() => {
                this.updateCPUspeed();
            }, 1000);
            this.tasksUpdater = setInterval(() => {
                this.updateCPUtasks();
            }, 5000);
        });
    }
    updateCPUload() {
        this.si.currentLoad((data) => {
            let average = [[], []];

            if (!data.cpus) return; // Prevent memleak in rare case where systeminformation takes extra time to retrieve CPU info (see github issue #216)

            data.cpus.forEach((e, i) => {
                this.series[i].append(new Date().getTime(), e.load);

                if (i < this.divide) {
                    average[0].push(e.load);
                } else {
                    average[1].push(e.load);
                }
            });
            average.forEach((stats, i) => {
                average[i] = Math.round(stats.reduce((a, b) => a + b, 0)/stats.length);

                try {
                    document.getElementById(`mod_cpuinfo_usagecounter${i}`).innerText = `Avg. ${average[i]}%`;
                } catch(e) {
                    // Fail silently, DOM element is probably getting refreshed (new theme, etc)
                }
            });
        });
    }
    updateCPUtemp() {
        this.si.cpuTemperature((data) => {
            try {
                document.getElementById("mod_cpuinfo_temp").innerText = `${data.main}°C`;
            } catch(e) {
                // See above notice
            }
        });
    }
    updateCPUspeed() {
        this.si.cpuCurrentspeed((data) => {
            try {
                document.getElementById("mod_cpuinfo_speed_min").innerText = `${data.min}GHz`;
                document.getElementById("mod_cpuinfo_speed_max").innerText = `${data.max}GHz`;
            } catch(e) {
                // See above notice
            }
        });
    }
    updateCPUtasks() {
        this.si.processes((data) => {
            try {
                document.getElementById("mod_cpuinfo_tasks").innerText = `${data.all}`;
            } catch(e) {
                // See above notice
            }
        });
    }
}

module.exports = {
    Cpuinfo
};


class Netstat {
    constructor(parentId) {
        if (!parentId) throw "Missing parameters";

        this.si = require("systeminformation");

        // Create DOM
        this.parent = document.getElementById(parentId);
        this.parent.innerHTML += `

NETWORK STATUS

STATE

UNKNOWN

IPv4

--.--.--.--

PING

--ms

`;

        this.offline = false;
        this.lastconn = {_ended: true};

        this._httpsAgent = new require("https").Agent({
            keepAlive: false,
            maxSockets: 10
        });

        // Init updaters
        this.updateInfo();
        this.infoUpdater = setInterval(() => {
            this.updateInfo();
        }, 1000);
    }
    updateInfo() {
        this.si.networkInterfaces((data) => {
            let offline = false;

            // Find the first external network networkInterface
            let net = data[0];
            let netID = 0;
            while (net.internal === true) {
                netID++;
                if (data[netID]) {
                    net = data[netID];
                } else {
                    // No external connection!
                    break;
                }
            }

            if (net.ip4 === "127.0.0.1") {
                offline = true;
            } else {
                if (this.lastconn._ended) {
                    this.lastconn = require("https").get({host: "ipinfo.now.sh", port: 443, path: "/", agent: this._httpsAgent}, (res) => {
                        let rawData = "";
                        res.on("data", (chunk) => {
                            rawData += chunk;
                        });
                        res.on("end", () => {
                            try {
                                this.ipinfo = JSON.parse(rawData);

                                if (this.ipinfo.api_version !== "2.0.0") console.warn("Warning: ipinfo API version might not be compatible");

                                delete this.ipinfo.api_version;
                                delete this.ipinfo.time;
                                let ip = this.ipinfo.ip;
                                document.querySelector("#mod_netstat_innercontainer > div:nth-child(2) > h2").innerHTML = ip;
                            } catch(e) {
                                console.warn(e);
                                console.info(rawData.toString());
                                let electron = require("electron");
                                electron.ipcRenderer.send("log", "note", "NetStat: Error parsing data from ipinfo.now.sh");
                                electron.ipcRenderer.send("log", "debug", `Error: ${e}`);
                            }
                        });
                    }).on("error", (e) => {
                        // Drop it
                    });
                }

                this.si.inetLatency("1.1.1.1", (data) => {
                    let ping;
                    if (data === -1) {
                        ping = "--ms";
                        offline = true;
                    } else {
                        ping = Math.round(data)+"ms";
                    }

                    this.offline = offline;
                    if (offline) {
                        document.querySelector("#mod_netstat_innercontainer > div:first-child > h2").innerHTML = "OFFLINE";
                        document.querySelector("#mod_netstat_innercontainer > div:nth-child(2) > h2").innerHTML = "--.--.--.--";
                        document.querySelector("#mod_netstat_innercontainer > div:nth-child(3) > h2").innerHTML = "--ms";
                    } else {
                        document.querySelector("#mod_netstat_innercontainer > div:first-child > h2").innerHTML = "ONLINE";
                        document.querySelector("#mod_netstat_innercontainer > div:nth-child(3) > h2").innerHTML = ping;
                    }
                });
            }
        });
    }
}

module.exports = {
    Netstat
};


section#filesystem{left: 55vw;} section#keyboard{right: 44vw;}


if (this._nextTickUpdateTtyCWD && this._disableCWDtracking === false) {


Download | Linux & macOS | Windows | Dependencies | Build system | Source
-------- | ------------- | ------- | ------------ | ------------ | ------
[![Release Badge](https://badgen.net/github/release/GitSquared/edex-ui)](https://github.com/GitSquared/edex-ui/releases) | [![Build Status](https://travis-ci.org/GitSquared/edex-ui.svg?branch=master)](https://travis-ci.org/GitSquared/edex-ui) | [![Build status](https://ci.appveyor.com/api/projects/status/leb069bro8gwocu7/branch/master?svg=true)](https://ci.appveyor.com/project/GitSquared/edex-ui/branch/master) | [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=GitSquared/edex-ui)](https://dependabot.com) | [![Known Vulnerabilities](https://snyk.io/test/github/GitSquared/edex-ui/badge.svg?targetFile=package.json)](https://snyk.io/test/github/GitSquared/edex-ui?targetFile=package.json) | [![Known Vulnerabilities](https://snyk.io/test/github/GitSquared/edex-ui/badge.svg?targetFile=src%2Fpackage.json)](https://snyk.io/test/github/GitSquared/edex-ui?targetFile=src%2Fpackage.json)

Download 1.0.1: [for Windows](https://github.com/GitSquared/edex-ui/releases/download/v1.0.1/eDEX-UI.Windows.Installer.exe) | [for macOS](https://github.com/GitSquared/edex-ui/releases/download/v1.0.1/eDEX-UI.MacOS.Image.dmg) | [for Linux x32](https://github.com/GitSquared/edex-ui/releases/download/v1.0.1/eDEX-UI.Linux.i386.AppImage) | [for Linux x64](https://github.com/GitSquared/edex-ui/releases/download/v1.0.1/eDEX-UI.Linux.x86_64.AppImage)

eDEX-UI is a fullscreen desktop application resembling a sci-fi computer interface, heavily inspired from [DEX-UI](https://github.com/seenaburns/dex-ui) and the [TRON Legacy movie effects](https://web.archive.org/web/20170511000410/http://jtnimoy.com/blogs/projects/14881671). It runs the shell of your choice in a real terminal, and displays live information about your system. It was made to be used on large touchscreens but will work nicely on a regular desktop computer or perhaps a tablet PC or one of those funky 360° laptops with touchscreens.

I had no ideas for a name so i took DEX-UI and added a "e" for Electron. Deal with it.

([Official ArchLinux AUR package](https://aur.archlinux.org/packages/edex-ui/) maintained by [@JesusCrie](https://github.com/JesusCrie))

## Screenshots
![Default screenshot](https://github.com/GitSquared/edex-ui/raw/master/media/screenshot_default.png)

_([neofetch](https://github.com/dylanaraps/neofetch) on eDEX-UI 1.0 with the default "tron" theme & QWERTY keyboard)_

![Blade screenshot](https://github.com/GitSquared/edex-ui/raw/master/media/screenshot_blade.png)

_([browsh](https://github.com/browsh-org/browsh) showing the results of a DuckDuckGo image search on eDEX-UI 1.0 with the "blade" theme & AZERTY keyboard)_

![Disrupted screenshot](https://github.com/GitSquared/edex-ui/raw/master/media/screenshot_disrupted.png)

_([fx](https://github.com/antonmedv/fx) showing the results of a [ipapi](https://github.com/GitSquared/ipapi) query on eDEX-UI 1.0 with the experimental "tron-disrupted" theme, and the French AZERTY keyboard)_

## Q&A
#### What OS can this thing run on?
Currently Windows, the latest macOS and any Linux distro that can run Chromium (AppImage package).
#### Is this a real terminal?
Yes. By default, eDEX runs bash on Linux and Powershell on Windows, but you can change that to any command in the settings.json file.
#### I don't like the colors/the keyboard layout is not right for me!
We got you covered! Check the [repo's Wiki](https://github.com/GitSquared/edex-ui/wiki) for in-depth information about customizing eDEX.
#### Why is there a keyboard?
eDEX-UI is meant to be used on touchscreens, even if it works well on regular displays! If you have a physical keyboard wired to your computer, pressing keys IRL will illuminate the virtual keyboard: *please remember to not type any passwords if you are recording your screen!*
#### What's the difference between this and the original DEX-UI?
Seenaburns' DEX-UI was created _"as an experiment or an art piece, not distributable software"_. The goal of this project is to push Seena's vision forward by making such an interface usable in real-life scenarios.
#### Will using this make me insanely badass?
Yes.

## Featured in...
- [Linux Uprising Blog](https://www.linuxuprising.com/2018/11/edex-ui-fully-functioning-sci-fi.html)
- [My post on r/unixporn](https://www.reddit.com/r/unixporn/comments/9ysbx7/oc_a_little_project_that_ive_been_working_on/)
- [Korben article (in french)](https://korben.info/une-interface-futuriste-pour-vos-ecrans-tactiles.html)
- [Hacker News](https://news.ycombinator.com/item?id=18509828)
- [This tweet that made me smile](https://twitter.com/mikemaccana/status/1065615451940667396)
- [BoingBoing article](https://boingboing.net/2018/11/23/simulacrum-sf.html) - Apparently i'm a "French hacker" lol
- [OReilly 4 short links](https://www.oreilly.com/ideas/four-short-links-23-november-2018)

## Useful commands for the nerds
#### Starting from source:

**IMPORTANT NOTE:** the following instructions are meant **for DEVELOPERS ONLY**. If you're just a casual user please download the precompiled binaries available on the [Releases tab](https://github.com/GitSquared/edex-ui/releases).

on *nix systems (You'll need the Xcode command line tools on macOS):
- clone the repository
- `npm run install-linux`
- `npm start`

on Windows:
- start cmd or powershell **as administrator**
- clone the repository
- `npm run install-windows`
- `npm start`
#### Building
Note: Due to native modules, you can only compile binaries for the OS you are compiling from.
Note²: It is recommended to start building from a fresh clone to prevent the code minifier script from parsing an entire `node_modules` folder.

- make a fresh clone (recommended)
- `npm install` (NOT `install-linux` or `install-windows`)
- `npm run build-linux` or `build-windows` or `build-darwin`

Resulting binaries and assets will be in the `dist` folder.

## Credits
eDEX-UI's source code was primarily written by me, [Squared](https://github.com/GitSquared). If you want to get in touch with me or find other projects I'm involved in, check out [my website](https://squared.codebrew.fr).

[PixelyIon](https://github.com/PixelyIon) helped me get started with Windows compatibility and offered some precious advice when I started to work on this project seriously.

## Thanks
Of course, eDEX would never have existed if I hadn't stumbled upon the amazing work of [Seena](https://github.com/seenaburns) on [r/unixporn](https://reddit.com/r/unixporn).

This project uses a bunch of open-source libraries, frameworks and tools, see [the full dependency graph](https://github.com/GitSquared/edex-ui/network/dependencies).

I want to namely thank the developers behind [xterm.js](https://github.com/xtermjs/xterm.js), [systeminformation](https://github.com/sebhildebrandt/systeminformation) and [SmoothieCharts](https://github.com/joewalnes/smoothie).

Huge thanks to [Rob "Arscan" Scanlon](https://github.com/arscan) for making the fantastic [ENCOM Globe](https://github.com/arscan/encom-globe), also inspired by the TRON: Legacy movie, and distributing it freely. His work really puts the icing on the cake.


div#mod_netstat {
    border-top: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    font-family: var(--font_main_light);
    letter-spacing: 0.092vh;
    font-size: 1.111vh;
    padding: 0vh;
    padding-top: 0.645vh;
    display: flex;
}

div#mod_netstat::before {
    content: "";
    border-left: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    align-self: flex-start;
    position: relative;
    left: -0.092vh;
    top: -1.111vh;
    height: 0.833vh;
}

div#mod_netstat::after {
    content: "";
    border-right: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    position: relative;
    right: -0.092vh;
    top: -1.111vh;
    height: 0.833vh;
}

div#mod_netstat_inner > h1:first-child {
    font-size: 1.48vh;
    margin: 0vh;
    width: 98%;
    padding-left: 2%;
}

div#mod_netstat_innercontainer {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: 1fr;
    grid-gap: 0.46vh;
}

div#mod_netstat_innercontainer div {
    padding: 0.925vh 0.46vh;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: space-around;
}

div#mod_netstat_innercontainer div:nth-child(2) {
    grid-column: 2 / 4;
}

div#mod_netstat_innercontainer h1 {
    margin: 0vh;
    opacity: 0.5;
}

div#mod_netstat_innercontainer h2 {
    margin: 0vh;
}


this.tty = this.Pty.spawn(opts.shell || "bash", opts.params || [], {

if (this.renderer && this._nextTickUpdateProcess) {

this.renderer.send("terminal_channel-"+this.port, "New process", this.tty._file);


div#mod_cpuinfo {
    border-top: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    font-family: var(--font_main_light);
    letter-spacing: 0.092vh;
    padding: 0.645vh 0vh;
    display: flex;
}

div#mod_cpuinfo::before {
    content: "";
    border-left: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    align-self: flex-start;
    position: relative;
    left: -0.092vh;
    top: -1.111vh;
    height: 0.833vh;
}

div#mod_cpuinfo::after {
    content: "";
    border-right: 0.092vh solid rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    position: relative;
    right: -0.092vh;
    top: -1.111vh;
    height: 0.833vh;
}

div#mod_cpuinfo > div#mod_cpuinfo_innercontainer {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    width: 100%;
}

div#mod_cpuinfo_innercontainer > h1:first-child {
    font-size: 1.48vh;
    margin: 0vh;
    width: 98%;
    padding-left: 2%;
    margin-bottom: -1.5vh;
}

div#mod_cpuinfo_innercontainer > h1:first-child > i {
    font-style: normal;
    font-size: 1.20vh;
    opacity: 0.5;
    text-align: right;
    display: inline-block;
    position: relative;
    bottom: 1.9vh;
    width: 100%;
}

div#mod_cpuinfo > div > div {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    margin: 0.278vh 0vh;
}

div#mod_cpuinfo > div > div:last-child {
    width: 95%;
    border-top: 0.092vh dashed rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    padding-top: 0.838vh;
}
div#mod_cpuinfo > div > div:last-child > div {
    width: 20%;
    text-align: center;
}

div#mod_cpuinfo h1 {
    font-size: 1.3vh;
    line-height: 1.5vh;
    margin: 0vh;
}

div#mod_cpuinfo em {
    font-family: var(--font_main);
    font-style: normal;
}

div#mod_cpuinfo span {
    width: 1em;
    position: relative;
    top: 0.37vh;
    right: 0.278vh;
    text-align: center;
    overflow: hidden;
    display: inline-block;
}

div#mod_cpuinfo i {
    font-style: normal;
    font-size: 1.3vh;
    opacity: 0.5;
    margin-top: 0.5vh;
}

div#mod_cpuinfo canvas {
    width: 76%;
    height: 4.167vh;
    border-top: 0.092vh dashed rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    border-bottom: 0.092vh dashed rgba(var(--color_r), var(--color_g), var(--color_b), 0.3);
    margin: 0.46vh 0vh;
}