NovaOS Development Documentation

source

core.js

// WebDesk 0.0.9
// Rebuild 7 (wtf)
/**
 * A toolkit for generating random numbers
 * @namespace
 */
var randTk = {
  /**
   * Generates a number, synchronously, with the specified number of digits.
   * @param {Number} length - The number of digits the generated number should have.
   * @returns {Number} The generated number.
   */
  gen: function (length) {
    if (length <= 0) {
      console.error("Length should be greater than 0");
      return null;
    }

    const min = Math.pow(10, length - 1);
    const max = Math.pow(10, length) - 1;
    return Math.floor(Math.random() * (max - min + 1)) + min;
  },
  /**
   * Generates a number, asynchronously, with the specified number of digits, outputted as a string.
   * @param {Number} length - The number of digits the generated number should have.
   * @returns {Promise<String>} A promise that resolves with the generated number, formatted as a string.
   */
  gens: async function (length) {
    if (length <= 0) {
      console.error("Length should be greater than 0");
      return null;
    }

    const array = new Uint32Array(Math.ceil(length / 4));
    window.crypto.getRandomValues(array);

    let result = "";
    for (let i = 0; i < array.length; i++) {
      result += array[i].toString(16).padStart(8, "0");
    }

    return result.slice(0, length);
  },
};
var gen = randTk.gen;
var gens = randTk.gens;
/**
 * NovaOS's main namespace, containing the core functions and utilities.
 * named "wd" due to NovaOS being based on WebDesk.
 * @namespace
 */
var wd = {
  loadapps: async function (inapp, onlineApps, apps) {
    const onlineApp = onlineApps.find((app) => app.name === inapp.name);

    if (onlineApp.ver === inapp.ver && sys.fucker === false) {
      console.log(
        `<i> ${inapp.name} is up to date (${inapp.ver} = ${onlineApp.ver})`,
      );
      const fucker = await fs.read(inapp.exec);
      if (fucker) {
        eval(fucker);
      } else {
        fs.del("/system/apps.json");
        fs.delfold("/system/apps");
        wm.notif(
          "App Issues",
          "All apps were uninstalled due to corruption or an update. Your data is safe, you can reinstall them anytime.",
          () => app.appmark.init(),
          "App Market",
        );
        sys.fucker = true;
        return;
      }
    } else {
      const remove = apps.filter((item) => item.id !== inapp.id);
      const removed = JSON.stringify(remove);
      fs.write("/system/apps.json", removed);
      app.appmark.create(onlineApp.path, onlineApp, true);
      console.log(
        `<!> ${inapp.name} was updated (${inapp.ver} --> ${onlineApp.ver})`,
      );
    }
  },
  win: function () {
    $(".d")
      .not(".dragged")
      .on("mousedown touchstart", function (event) {
        var $window = $(this).closest(".window");
        if (!$window.hasClass("max")) {
          var offsetX, offsetY;
          var windows = $(".window");
          highestZIndex = Math.max.apply(
            null,
            windows
              .map(function () {
                var zIndex = parseInt($(this).css("z-index")) || 0;
                return zIndex;
              })
              .get(),
          );
          $window.css("z-index", highestZIndex + 1);
          

          if (event.type === "mousedown") {
            offsetX = event.clientX - $window.offset().left;
            offsetY = event.clientY - $window.offset().top;
          } else if (event.type === "touchstart") {
            var touch = event.originalEvent.touches[0];
            offsetX = touch.clientX - $window.offset().left;
            offsetY = touch.clientY - $window.offset().top;
          }

          $(document).on("mousemove touchmove", function (event) {
            var newX, newY;
            if (event.type === "mousemove") {
              newX = event.clientX - offsetX;
              newY = event.clientY - offsetY;
              $window.addClass("dragging");
            } else if (event.type === "touchmove") {
              var touch = event.originalEvent.touches[0];
              newX = touch.clientX - offsetX;
              newY = touch.clientY - offsetY;
              $window.addClass("dragging");
            }

            $window.offset({ top: newY, left: newX });
          });

          $(document).on("mouseup touchend", function () {
            $(document).off("mousemove touchmove");
            $window.removeClass("dragging");
          });

          document.body.addEventListener(
            "touchmove",
            function (event) {
              event.preventDefault();
            },
            { passive: false },
          );
        }
      });
  },
  makeDragzone: function (el) {
    $(el)
      .not(".dragged")
      .on("mousedown touchstart", function (event) {
        var $window = $(this).closest(".window");
        if (!$window.hasClass("max")) {
          var offsetX, offsetY;
          var windows = $(".window");
          highestZIndex = Math.max.apply(
            null,
            windows
              .map(function () {
                var zIndex = parseInt($(this).css("z-index")) || 0;
                return zIndex;
              })
              .get(),
          );
          $window.css("z-index", highestZIndex + 1);
          

          if (event.type === "mousedown") {
            offsetX = event.clientX - $window.offset().left;
            offsetY = event.clientY - $window.offset().top;
          } else if (event.type === "touchstart") {
            var touch = event.originalEvent.touches[0];
            offsetX = touch.clientX - $window.offset().left;
            offsetY = touch.clientY - $window.offset().top;
          }

          $(document).on("mousemove touchmove", function (event) {
            var newX, newY;
            if (event.type === "mousemove") {
              newX = event.clientX - offsetX;
              newY = event.clientY - offsetY;
              $window.addClass("dragging");
            } else if (event.type === "touchmove") {
              var touch = event.originalEvent.touches[0];
              newX = touch.clientX - offsetX;
              newY = touch.clientY - offsetY;
              $window.addClass("dragging");
            }

            $window.offset({ top: newY, left: newX });
          });

          $(document).on("mouseup touchend", function () {
            $(document).off("mousemove touchmove");
            $window.removeClass("dragging");
          });

          document.body.addEventListener(
            "touchmove",
            function (event) {
              event.preventDefault();
            },
            { passive: false },
          );
        }
      });
  },
  /**
   * The main function, responsible for the shell.
   * @param {String} name - the user's name
   * @param {String} deskid - the novanet id for the current install.
   * @param {Boolean} waitopt - whether to wait for a certain amount of time before starting the desktop
   * @param {Number} waitFor - the amount of time to wait before starting the desktop
   * @returns {void} This function does not return anything.
   */
  desktop: function (name, deskid, waitopt = false, waitFor = 400) {
    ui.dest(tk.g("setuparea"));
    function smApps(apps = app) {
      document.querySelectorAll(".appItem").forEach(function (e) {
        e.remove();
      });
      for (var key in apps) {
        if (apps.hasOwnProperty(key)) {
          if (apps[key].hasOwnProperty("runs") && apps[key].runs === true) {
            console.log(`<i> ${apps[key].name} is launchable!`);
            const btn = tk.cb("b1", apps[key].name, function () {}, el.sm);
            btn.innerHTML = `
                        <img src='${apps[key].icon}' class='appIcon'/>
                        <span class='appName'>${apps[key].name}</span>`;
            btn.classList.add("appItem");
            var $thisapp = app[key];
            btn.onclick = $thisapp.init;
            btn.addEventListener("click", function () {
              if (document.querySelector(".tbmenu")) {
                ui.dest(document.querySelector(".tbmenu"), 150);
              }
            });
          } else {
            console.log(`<i> ${apps[key].name} is not launchable! :(`);
          }
        }
      }
    }
    function startmenu() {
      if (el.sm == undefined) {
        if (document.querySelector(".contcent")) {
          $(".contcent").fadeOut(150, function () {});
        }
        el.sm = tk.c("div", document.body, "tbmenu");
        el.sm.addEventListener("mouseleave", function () {
          ui.dest(el.sm, 150);
          el.sm = undefined;
        });
        const btm = el.taskbar.getBoundingClientRect();
        el.sm.style.bottom = btm.height + btm.x + 4 + "px";
        tk.p(`Hello, ${name}!`, "h2", el.sm);
        console.log(el.sm);
        var searchbar = tk.mkel("input", ["i1"], "", el.sm);
        searchbar.placeholder = "Search for anything...";
        searchbar.addEventListener("input", async function (event) {
          var results = {};
          var search = event.target.value.trim();
          var searchAsWords = search.split(" ");
          var appIds = Object.keys(app);
          appIds.forEach(function (appId, index) {
            appIds[index] = appId.toLowerCase();
          });
          searchAsWords.forEach((srch) => {
            if (appIds.includes(srch.toLowerCase())) {
              results[srch] = app[srch];
            }
          });
          // search by app names
          searchAsWords.forEach((srch) => {
            for (var key in app) {
              console.log(app[key]);
              if (app.hasOwnProperty(key) && app[key].hasOwnProperty("name")) {
                console.log(app[key].name);
                if (app[key].name.toLowerCase().includes(srch.toLowerCase())) {
                  results[key] = app[key];
                }
              }
            }
          });
          console.log(results);
          smApps(results);
          if (document.querySelector(".aiResponse") == null) {
            var aiResponse = tk.c("p", el.sm, "aiResponse");
          }
          document.querySelector(".aiResponse").innerText = "";

          // const  = {
          //     queries: [search],
          //     responses: [
          //         `Your system colour is set to ${await fs.read('/user/info/color')}.`,
          //         `Your system colour scheme is set to ${await fs.read('/user/info/lightdark') || "default"}.`,
          //         `Your name is ${await fs.read('/user/info/name') || "unset, somehow (how did you get here without triggering setup lmaoo)"}.`,
          //     ]
          // };
          if (
            (search.includes("what") &&
              search.includes("your") &&
              search.includes("name")) ||
            (search.includes("who") && search.includes("you")) ||
            (search.includes("ai") && search.includes("you"))
          ) {
            document.querySelector(".aiResponse").innerText =
              "I'm NovaAI! (not really ai lmao im just some if loops uwu)";
          } else if (
            search.includes("what") &&
            search.includes("my") &&
            search.includes("name")
          ) {
            document.querySelector(".aiResponse").innerText =
              `Your name is ${(await fs.read("/user/info/name")) || "unset, somehow (how did you get here without triggering setup lmaoo)"}!`;
          } else if (
            search.includes("what") &&
            (search.includes("color") || search.includes("colour")) &&
            (search.includes("theme") || search.includes("accent"))
          ) {
            document.querySelector(".aiResponse").innerText =
              `Your system colour is set to ${(await fs.read("/user/info/color")) || "unknown"}.`;
          } else if (
            search.includes("what") &&
            (search.includes("color") || search.includes("colour")) &&
            (search.includes("scheme") || search.includes("mode"))
          ) {
            document.querySelector(".aiResponse").innerText =
              `Your system colour mode is set to ${(await fs.read("/user/info/lightdark")) || "default"}.`;
          } else if (
            search.includes("what") &&
            (search.includes("time") ||
              search.includes("clock") ||
              search.includes("hour") ||
              search.includes("minute"))
          ) {
            document.querySelector(".aiResponse").innerHTML =
              `It is currently <div class="time">${wd.clock() || "Unknown"}</div>`;
          } else if (app.hasOwnProperty("docai")) {
            document.querySelector(".aiResponse").innerHTML =
              `No results found.<br> Would you like to <button class="b1" onclick="app.docai.init('${search}')">Search for it with DocAI?</button>`;
          } else {
            document.querySelector(".aiResponse").innerText =
              "No results found, and DocAI is not found on your system!";
          }
        });
        smApps(app);
      } else {
        ui.dest(el.sm, 150);
        el.sm = undefined;
      }
    }
    function desktopgo() {
      el.taskbar = tk.c("div", document.body, "taskbar");
      const lefttb = tk.c("div", el.taskbar, "tnav");
      const titletb = tk.c("div", el.taskbar, "title");
      const start = tk.cb("b1 start", "", () => startmenu(), el.taskbar);
      const smtxt = tk.c("span", start, "smtxt");
      smtxt.innerText = "Apps";
      const smico = tk.c("span", start, "smico");
      smico.innerText = "🏠";
      el.tr = tk.c("div", lefttb);
      tk.cb("b1 time", "--:--", () => wd.controls.toggle(), titletb);
    }
    if (waitopt) {
      setTimeout(function () {
        desktopgo();
      }, waitFor);
    } else {
      desktopgo();
    }
  },
  /**
   * Forces the clock to tick, and returns the current time in HH:MM:SS format.
   * @returns {String} The current time in HH:MM:SS format.
   */
  clock: function () {
    const currentTime = new Date();
    let hours = currentTime.getHours();
    const minutes = currentTime.getMinutes();
    const seconds = currentTime.getSeconds();
    const formattedHours = hours < 10 ? `0${hours}` : hours;
    const formattedMinutes = minutes < 10 ? `0${minutes}` : minutes;
    const formattedSeconds = seconds < 10 ? `0${seconds}` : seconds;
    const formattedTime = `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
    const elements = document.getElementsByClassName("time");
    for (let i = 0; i < elements.length; i++) {
      elements[i].innerText = formattedTime;
    }
    return formattedTime;
  },
  /**
   * Stores the user's name and sets parts of the filesystem
   */
  finishsetup: async function (name, div1, div2) {
    if (sys.isIOT) {
      ui.sw2(div1, div2);
      ui.masschange("name", "iotuser");
      await fs.mkdir("/user");
      await fs.mkdir("/user/info");
      await fs.mkdir("/user/Documents");
      await fs.write("/user/info/name", "iotuser");
    } else {
      ui.sw2(div1, div2);
      ui.masschange("name", name);
      await fs.mkdir("/user");
      await fs.mkdir("/user/info");
      await fs.mkdir("/user/Documents");
      await fs.write("/user/info/name", name);
    }
  },
  /**
   * Reboot NovaOS
   */
  reboot: function () {
    window.location.reload();
  },
  /**
   * Switch to dark mode
   */
  dark: function () {
    ui.cv("ui1", "rgb(40, 40, 40, 0.6)");
    ui.cv("ui2", "#1b1b1b");
    ui.cv("ui3", "#2b2b2b");
    ui.cv("font", "#fff");
    fs.write("/user/info/lightdark", "dark");
  },
  /**
   * Switch to light mode
   */
  light: function () {
    ui.cv("ui1", "rgb(255, 255, 255, 0.6)");
    ui.cv("ui2", "#ffffff");
    ui.cv("ui3", "#dddddd");
    ui.cv("font", "#000");
    fs.del("/user/info/lightdark");
  },
  clearm: function () {
    ui.cv("ui1", "rgb(255, 255, 255, 0)");
    ui.cv("ui2", "rgba(var(--accent), 0.1)");
    ui.cv("ui3", "rgba(var(--accent) 0.2)");
    ui.cv("font", "#000");
    fs.write("/user/info/lightdark", "clear");
  },
  clearm2: function () {
    ui.cv("ui1", "rgb(255, 255, 255, 0)");
    ui.cv("ui2", "rgba(var(--accent), 0.1)");
    ui.cv("ui3", "rgba(var(--accent) 0.2)");
    ui.cv("font", "#fff");
    fs.write("/user/info/lightdark", "clear2");
  },
  /**
   * The control center namespace.
   * @namespace
   */
  controls: {
    /**
     * Toggles the control center.
     */
    toggle: function () {
      // document.querySelector(".taskbar > .tnav > .b1").onclick = startmenu
      var cc = document.querySelector("#contcent");
      // if(cc.computedStyleMap().get("display").value === "none" || cc.style.display === "none"){
      if (cc.style.display === "none") {
        cc.style.display = "block";
        if (document.querySelector(".tbmenu")) {
          ui.dest(document.querySelector(".tbmenu"), 150);
        }
      } else {
        cc.style.display = "none";
      }
    },
  },
};

setInterval(wd.clock, 1000);