Raw Source
balthazar / xa_designer_overlay

// ==UserScript==
// @name xa_designer_overlay
// @description Overlay with quality of life improvements
// @version  1.0
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
// @require https://raw.githubusercontent.com/JDMcKinstry/jQuery.winFocus/master/jquery.winFocus.js
// @require https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.min.js
// @include /^https://.*-des\.f.v..d\.com//
// @license MIT
// @updateURL https://openuserjs.org/meta/ford.lambert/xa_designer_overlay.meta.js
// @downloadURL https://openuserjs.org/install/ford.lambert/xa_designer_overlay.user.js
// ==/UserScript==
// ==OpenUserJS==
// @author ford.lambert
// ==/OpenUserJS==
// jshint esversion: 6
// jshint jquery: true
// globals $, jQuery

(function (jQuery) {
  "use strict";
  jQuery.noConflict(); // do not use $ because it's also used by Prototype
  console.log(`%c Loading XA overlay`, "color: #38C4CC");
  console.log(
    `%c Enable or disable XA overlay modules in it's menu on the top of the screen`,
    "color: #38C4CC"
  );

  let tabIsActive = true;
  const modules = {
    regenMonitor: {
      active: getCookie("xa_regen_monitor").length
        ? getCookie("xa_regen_monitor") == "true" ||
          getCookie("xa_regen_monitor") == true
          ? true
          : false
        : true,
      cookie: "xa_regen_monitor",
      label: "Regen monitor",
      title:
        "IceBlue Style required - Show production monitor infos with a colored logo on top left corner, green: done, yellow: running. Go inactive/active when tab loose/gain focus",
    },
    shortcuts: {
      active: getCookie("xa_shortcuts").length
        ? getCookie("xa_shortcuts") == "true" ||
          getCookie("xa_shortcuts") == true
          ? true
          : false
        : true,
      cookie: "xa_shortcuts",
      label: "Keyboard shortcuts",
      title:
        "Enable some keyboard shortcuts: ctrl + shift + f -> global fv search, ctrl + shift + s -> save and quit, ctrl + s -> save and continue",
    },
    editorQol: {
      active: getCookie("xa_editor_qol").length
        ? getCookie("xa_editor_qol") == "true" ||
          getCookie("xa_editor_qol") == true
          ? true
          : false
        : true,
      cookie: "xa_editor_qol",
      label: "Editor QOL",
      title:
        "Add some quality of life display when editing something, status, spec reference and level of sharing",
    },
    generalLisibility: {
      active: getCookie("xa_lisibility").length
        ? getCookie("xa_lisibility") == "true" ||
          getCookie("xa_lisibility") == true
          ? true
          : false
        : true,
      cookie: "xa_lisibility",
      label: "General lisibility",
      title:
        "Add some little things to boost lisibility: partial names are colored, always show lightened lightbulbs, show scss var colors",
    },
    flash: {
      active: getCookie("xa_flash").length
        ? getCookie("xa_flash") == "true" || getCookie("xa_flash") == true
          ? true
          : false
        : true,
      cookie: "xa_flash",
      label: "Flash expire",
      title: "Remove flash messages after 5sec of display",
    },
    fieldPresence: {
      active: getCookie("xa_field_presence").length
        ? getCookie("xa_field_presence") == "true" ||
          getCookie("xa_field_presence") == true
          ? true
          : false
        : true,
      cookie: "xa_field_presence",
      label: "Field presence alert",
      title:
        "Display a warning if a model does not have a created_at and updated_at",
    },
    userStory: {
      active: getCookie("xa_user_story").length
        ? getCookie("xa_user_story") == "true" ||
          getCookie("xa_user_story") == true
          ? true
          : false
        : false,
      cookie: "xa_user_story",
      label: "User story warning",
      title:
        "Experimental, display a warning if you have no user story assigned",
    },
    editorSize: {
      active: getCookie("xa_editor_size").length
        ? getCookie("xa_editor_size") == "true" ||
          getCookie("xa_editor_size") == true
          ? true
          : false
        : false,
      cookie: "xa_editor_size",
      label: "Ace editor default size",
      title: "Save your last editor height and set it on load",
    },
    regenButton: {
      active: getCookie("xa_regen_button").length
        ? getCookie("xa_regen_button") == "true" ||
          getCookie("xa_regen_button") == true
          ? true
          : false
        : true,
      cookie: "xa_regen_button",
      label: "Add a regen button",
      title:
        "Add a regen button on top of models pages next to the Maveoc name, does exactly like the drag and drop of fields without the risk of missclick.",
    },
    dragDropLocker: {
      active: getCookie("xa_drag_drop_locker").length
        ? getCookie("xa_drag_drop_locker") == "true" ||
          getCookie("xa_drag_drop_locker") == true
          ? true
          : false
        : true,
      cookie: "xa_drag_drop_locker",
      label: "Add a drag and drop locking button",
      title:
        "Add a button to 'lock' model and graphical views therefore avoiding missdrop. Locked by default. Also move the reminders away from this area to avoid their removing",
    },
    qualityFramework: {
      active: getCookie("xa_quality_framework").length
        ? getCookie("xa_quality_framework") == "true" ||
          getCookie("xa_quality_framework") == true
          ? true
          : false
        : false,
      cookie: "xa_quality_framework",
      label: "Experimental - Quality framework",
      title:
        "Experimental, add functionnalities to force the use of quality tools around the app (status / description...)",
    },
    experimentalInterface: {
      active: getCookie("xa_experimental_interface").length
        ? getCookie("xa_experimental_interface") == "true" ||
          getCookie("xa_experimental_interface") == true
          ? true
          : false
        : false,
      cookie: "xa_experimental_interface",
      label: "Experimental - Apply UI improvements",
      title:
        "The functionnality in this section will be merged elsewhere after user review. Currently: change certain select into chosens",
    },
  };

  console.log({ modules });

  const initOverlay = () => {
    setXaMenu();
    addKeybordShortcuts();
    limitFlashDuration();
    displayRequiredFieldNotice();
    displayUserStoryWarning();
    displayEditorInfos();
    displayVisibilityBoost();
    resizeEditor();
    addRegenButton();
    addDragDropLocker();
    initQualityFramework();
    initExperimentalUi();
    delay(1000).then(() => addRegenWatcher());
  };

  // XA Menu ------>
  const setXaMenu = () => {
    const $xaMenuButton = jQuery("<button></button>")
      .attr("id", "xa-menu-button")
      .text("XA - Menu")
      .css("margin", "0 20px");

    const $xaMenuBox = jQuery("<div></div>")
      .attr("id", "xa-menu")
      .css("position", "fixed")
      .css("top", "20vh")
      .css("left", "42vw")
      .css("padding", "10px")
      .css("box-shadow", "0px 0px 900px 50px black")
      .css("z-index", "9000")
      .css("background-color", "white")
      .css("color", "black")
      .css("visibility", "hidden")
      .css("max-height", "60vh")
      .css("overflow", "auto");

    const $xaMenu = jQuery("<form></form>").attr("method", "dialog");

    const $info = jQuery("<p></p>")
      .css("width", "350px")
      .css("font-size", "1.2em")
      .css("font-style", "italic")
      .text(
        "Changing the value a checkbox immediatly update a related cookie for enabling/disabling the module. Reload needed to apply effect (consider your background work before doing so...)."
      );
    $xaMenuBox.append($info);

    for (const [key, module] of Object.entries(modules)) {
      const $xaMenuEntrie = jQuery("<div></div>")
        .css("display", "flex")
        .css("justify-content", "center")
        .css("align-items", "center")
        .css("margin-bottom", "10px")
        .css("border-bottom", "1px solid grey");

      const $label = jQuery("<label></label>")
        .text(module.label)
        .css("width", "150px")
        .css("font-size", "1.2em")
        .attr("for", `xa-${module.cookie}-input`);

      const $input = jQuery("<input />")
        .attr("type", "checkbox")
        .prop("checked", module.active)
        .css("margin", "5px 10px")
        .attr("id", `xa-${module.cookie}-input`);

      $input.on("change", function () {
        setLocalCookie(module.cookie, this.checked);
      });

      const $info = jQuery("<img />")
        .attr("src", "/images/std/help.png?1199643545")
        .attr("alt", "Help")
        .attr("title", module.title);

      $xaMenuEntrie.append($label);
      $xaMenuEntrie.append($input);
      $xaMenuEntrie.append($info);
      $xaMenu.append($xaMenuEntrie);
      $xaMenuBox.append($xaMenu);
    }

    const $menu = jQuery("<menu></menu>")
      .css("padding", "0")
      .css("display", "flex")
      .css("justify-content", "center");

    const $closeButton = jQuery("<button></button>")
      .attr("value", "cancel")
      .text("Cancel")
      .css("margin", "10px");

    const $reloadButton = jQuery("<button></button>")
      .text("Reload to apply")
      .css("margin", "10px")
      .attr("onClick", "location.reload()");

    $menu.append($closeButton);
    $menu.append($reloadButton);
    $xaMenu.append($menu);

    jQuery("#header-elts").append($xaMenuBox);
    jQuery("#header-elts").prepend($xaMenuButton);

    $xaMenuButton.on("click", () => {
      toggleModal(jQuery("#xa-menu"));
    });

    $closeButton.on("click", () => {
      toggleModal(jQuery("#xa-menu"));
    });
  };

  const toggleModal = ($modal) => {
    if ($modal.css("visibility") == "visible") {
      $modal.css("visibility", "hidden");
    } else {
      $modal.css("visibility", "visible");
    }
  };
  // XA Menu <------

  // Regen watcher ------>
  const addRegenWatcher = () => {
    if (
      modules.regenMonitor.active &&
      !window.location.href.includes("/tasks")
    ) {
      const hasCustomStyle =
        jQuery("#title").find("h1").css("display") == "none";
      displayLogo(hasCustomStyle);
      pollRegen();

      let monitor_pu = new Ajax.PeriodicalUpdater(
        "production_monitor",
        "/changes/current_tasks",
        { method: "post", frequency: 3, decay: 2 }
      );

      jQuery.winFocus({
        //  will only fire when current tab loses focus
        blur: () => {
          disableRegenWatcher(monitor_pu);
          tabIsActive = false;
        },
        //  will only fire when current tab gains focus
        focus: () => {
          tabIsActive = true;
          monitor_pu = new Ajax.PeriodicalUpdater(
            "production_monitor",
            "/changes/current_tasks",
            { method: "post", frequency: 3, decay: 2 }
          );
          pollRegen();
        },
      });
    }
  };

  const disableRegenWatcher = (monitor_pu) => {
    monitor_pu.stop();
    monitor_pu = undefined;
  };

  const pollRegen = () => {
    const inactiveLogo = "https://i.imgur.com/qU5tlox.png";
    const doneLogo = "https://i.imgur.com/2ZV6gAJ.png";
    const processingLogo = "https://i.imgur.com/35esLp6.png";

    let text = "";
    jQuery("#production_monitor")
      .find("td")
      .each(function () {
        text += jQuery(this).text().trim();
      });
    const isProcessing = text.length > 0;

    if (isProcessing) {
      changeLogo(processingLogo);
    } else {
      changeLogo(doneLogo);
    }

    setTimeout(function () {
      if (tabIsActive) {
        pollRegen();
      } else {
        changeLogo(inactiveLogo);
      }
    }, 2000);
  };

  const changeLogo = (logoPath) => {
    jQuery("#header")
      .find("#title")
      .css("background-image", "url(" + logoPath + ")");
  };

  const displayLogo = (hasCustomStyle) => {
    const logoPath = hasCustomStyle
      ? "https://storage.gra.cloud.ovh.net/v1/AUTH_0b4eb0b702894162b8bcdc31088fb7dd/hexa-public/designer-script/default.png"
      : "/images/logo/logo.png";
    const $header = jQuery("#header");
    const $title = $header.find("#title");
    $title.css("background-image", "url(" + logoPath + ")");
    const $titleInfos = jQuery("<span></span>")
      .addClass("sub-title")
      .text($title.text());
    $header.append($titleInfos);
  };

  // Regen watcher <------

  // KEYBOARD SHORTCUTS ------>
  const addKeybordShortcuts = () => {
    if (modules.shortcuts.active) {
      jQuery(document).on("keydown", (e) => {
        const isMac = navigator.platform.match("Mac");
        const ctrlLikeKey = isMac ? e.metaKey : e.ctrlKey;
        const saveKeyCode = 83;
        const searchKeyCode = 70;

        if (isMac) {
          if (ctrlLikeKey && e.shiftKey && e.keyCode == saveKeyCode) {
            e.preventDefault(); // prevent "save html page" default event
            submitEditor();
          }
        } else {
          if (ctrlLikeKey && e.keyCode == saveKeyCode) {
            e.preventDefault(); // prevent "save html page" default event
            submitEditor();
          }
        }

        if (ctrlLikeKey && e.shiftKey && e.keyCode == searchKeyCode) {
          jQuery("#global_search_field").focus();
        }
      });
    }
  };

  const submitEditor = () => {
    // const $saveContinue = jQuery('#new_create_form_submit').length ? jQuery('#new_create_form_submit') : jQuery('#continue_editing_form_submit');
    // stayOnPage ? $saveContinue.click() : jQuery('#update_form_submit').click();
    jQuery("#continue_editing_form_submit").click();
  };
  // <------ KEYBOARD SHORTCUTS

  // Info display ------>
  const limitFlashDuration = () => {
    if (modules.flash.active) {
      // After reload on save, a sticky flash stay on the top left, remove it after some time.
      setTimeout(function () {
        jQuery("#flash").hide();
      }, 5000);
    }
  };
  // <------ Info display

  // ------> Alert for basic fields presence (created_at, updated_at)
  // This script is brought to you by Aloïs, thanks to him and his amazing cat :)
  const displayRequiredFieldNotice = () => {
    if (modules.fieldPresence.active) {
      const created_at = document.querySelector("[title^='created_at']");
      const updated_at = document.querySelector("[title^='updated_at']");
      const sidebarNotes = document.querySelector("#field_types span");
      const maveocName = document.querySelector("#content h2").innerText;
      const existingStorage = JSON.parse(
        JSON.parse(window.localStorage.getItem("disabled_notices"))
      );
      const disableButtons = document.querySelectorAll(".disable-notice");

      if (created_at && updated_at) return;
      if (sidebarNotes) {
        if (
          (!created_at && existingStorage == null) ||
          (!created_at &&
            existingStorage !== null &&
            !existingStorage.includes(maveocName))
        ) {
          sidebarNotes.innerHTML +=
            '<p class="date_fields_warning" style="color:red">You do not have a created_at field! <img alt="Del" class="disable-notice" style="cursor:pointer;" title="Disable ALL notice messages!" src="/images/faveod/del.png?1596181992"></p>';
        }

        if (
          (!updated_at && existingStorage == null) ||
          (!updated_at &&
            existingStorage !== null &&
            !existingStorage.includes(maveocName))
        ) {
          sidebarNotes.innerHTML +=
            '<p class="date_fields_warning" style="color:red">You do not have an updated_at field! <img alt="Del" class="disable-notice" style="cursor:pointer;" title="Disable ALL notice messages!" src="/images/faveod/del.png?1596181992"></p>';
        }
      }

      disableButtons.forEach((button) =>
        button.addEventListener("click", removeNotices)
      );
    }
  };

  const removeRequiredFieldNotice = () => {
    const notices = document.querySelectorAll(".date_fields_warning");
    notices.forEach((notice) => notice.hide());
    let value = "";
    if (existingStorage !== null) {
      existingStorage.push(maveocName);
      value = JSON.stringify(existingStorage);
    } else {
      value = JSON.stringify([maveocName]);
    }
    window.localStorage.setItem(`disabled_notices`, value);
  };
  // <------ Alert for basic fields presence

  // ------> User Story warning
  const displayUserStoryWarning = () => {
    // Initially started this one to encourage always working with a user story
    // Enable or not depending of the project, disabled by default

    if (modules.userStory.active) {
      const $userStoryIndicator = jQuery("#working_on");

      if (!$userStoryIndicator.length) {
        const $userStoryWarning = jQuery("<span></span>")
          .attr("id", "user-story-warning")
          .text("WARNING ! You are currently working without any User story !")
          .css("color", "#D04158")
          .css("font-weight", "bold")
          .css("font-size", "1.1em");

        const $warningLogo = jQuery("<span></span>")
          .attr("id", "user-story-warning-logo")
          .css("background-image", "url(https://i.imgur.com/JW2wKDs.png)")
          .css("height", "20px")
          .css("width", "20px")
          .css("display", "inline-block")
          .css("background-position", "center")
          .css("background-size", "contain")
          .css("margin-bottom", "-4px")
          .css("margin-right", "4px");

        jQuery("#header-elts").prepend($userStoryWarning);
        $userStoryWarning.prepend($warningLogo);

        let counter = 1;
        const isOdd = (x) => {
          return x & 1;
        };

        setInterval(function () {
          if (isOdd(counter) == 1) {
            $warningLogo.css(
              "background-image",
              "url(https://i.imgur.com/JW2wKDs.png)"
            );
            counter += 1;
          } else {
            $warningLogo.css(
              "background-image",
              "url(https://i.imgur.com/iqfdJGd.png)"
            );
            counter += 1;
          }
        }, 1000);
      }
    }
  };
  // <------ User stroy warning

  // ------> Editor QOL
  const displayEditorInfos = () => {
    if (modules.editorQol.active) {
      displayStatus();
      displaySpecReference();
      displaySharing();
    }
  };

  const displayStatus = () => {
    const $originalStatus = jQuery("select[id$='_status']");

    if (!$originalStatus.length) {
      return;
    }

    const $newStatus = $originalStatus.clone();
    $newStatus
      .attr("id", "new-select")
      .val($originalStatus.val())
      .css("margin-right", "10px")
      .css("border-radius", "5px");

    $newStatus.on("change", function () {
      $originalStatus.val(jQuery(this).val());
    });

    $originalStatus.on("change", function () {
      $newStatus.val(jQuery(this).val());
    });

    jQuery("#content h2.title").prepend($newStatus);
  };

  const displaySpecReference = () => {
    const $originalSpecRef = jQuery("input[id$='_spec_ref']");

    if (!$originalSpecRef.length) {
      return;
    }

    const $newSpecRef = $originalSpecRef.clone();
    $newSpecRef
      .attr("id", "new-specref")
      .attr("placeholder", "US Reference")
      .css("text-align", "center")
      .css("border-radius", "5px")
      .css("margin-left", "10px")
      .css("display", "inline-block");

    $newSpecRef.on("change", function () {
      $originalSpecRef.val(jQuery(this).val());
    });

    $originalSpecRef.on("change", function () {
      $newSpecRef.val(jQuery(this).val());
    });

    jQuery("#content h2.title").append($newSpecRef);
  };

  const displaySharing = () => {
    const currentAvailability = jQuery("select[id$='_shared_for']").val();
    const label = jQuery("select[id$='_shared_for'] option:selected").text();

    if (!jQuery("select[id$='_shared_for']").length) {
      return;
    }

    const $wrapper = jQuery("<p></p>");
    const $availability = jQuery("<span></span>")
      .text(`Available for ${label}`)
      .css("border", "1px solid black")
      .css("background-color", "grey")
      .css("color", "white")
      .css("padding", "3px")
      .css("border-radius", "5px")
      .css("margin-left", "10px");

    if (currentAvailability == 3) {
      $wrapper.text("This code need manual regeration to be applied");
      const $warningSign = jQuery("<i />")
        .addClass("muted glyphicon glyphicon-exclamation-sign")
        .css("color", "orange")
        .css("margin-right", "5px");
      $wrapper.prepend($warningSign);
    }

    $wrapper.append($availability);
    jQuery("#content h2.title").append($wrapper);
  };
  // <------ Editor QOL

  // ------> General lisibility
  const displayVisibilityBoost = () => {
    if (modules.generalLisibility.active) {
      /* ----- Always show lightened lightbulbs ----- */
      const wantedSrc = "/images/page_icons/lightbulb.png?1199643502";
      jQuery("td")
        .find(`img[src="${wantedSrc}"]`)
        .parents("td")
        .css("opacity", "1");

      /*------- Add a color if a view is a partial ------*/
      jQuery(".partial").find("a").css("color", "rgba(233, 96, 42, 0.7)");

      /* show color for scss variables */
      const $variableTable = jQuery("#style_variables_table");

      if ($variableTable.length) {
        $variableTable
          .find("tbody")
          .find("tr")
          .each(function () {
            jQuery(this)
              .find("td:nth-child(2)")
              .css(
                "background-color",
                jQuery(this).find("td:nth-child(4)").text()
              );
          });
      }

      /* Add separator in search */
      document
        .getElementById("global_search_field")
        .addEventListener("input", () => {
          setTimeout(function () {
            const separatorStyle = "1px dashed black";
            const resultBox = document.getElementById("gs_results");
            const entries = [];

            entries.push(resultBox.querySelector(".config_initializers"));
            entries.push(resultBox.querySelector(".cfg_dependencies"));
            entries.push(resultBox.querySelector(".use_cases"));
            entries.push(resultBox.querySelector(".style_variables"));
            entries.push(resultBox.querySelector(".javascripts"));
            entries.push(resultBox.querySelector(".js_frameworks"));
            entries.push(resultBox.querySelector(".js_functions"));
            entries.push(resultBox.querySelector(".styles"));
            entries.push(resultBox.querySelector(".layouts"));
            entries.push(resultBox.querySelector(".notifications"));
            entries.push(resultBox.querySelector(".aktions"));
            entries.push(resultBox.querySelector(".field_actions"));
            entries.push(
              resultBox.querySelector(".action_attribute_definitions")
            );
            entries.push(resultBox.querySelector(".field_formatters"));
            entries.push(resultBox.querySelector(".field_behaviours"));
            entries.push(resultBox.querySelector(".field_styles"));
            entries.push(resultBox.querySelector(".field_views"));
            entries.push(resultBox.querySelector(".view_helpers"));
            entries.push(resultBox.querySelector(".views"));
            entries.push(resultBox.querySelector(".model_tests"));
            entries.push(resultBox.querySelector(".model_logics"));
            entries.push(resultBox.querySelector(".fields"));

            entries.map((entry) => {
              if (entry != null) {
                entry.parentNode.style.borderTop = separatorStyle;
              }
            });
          }, 1000);
        });
    }
  };

  // <------ General lisibility

  // ------> Editor Size
  const resizeEditor = () => {
    if (modules.editorSize.active) {
      let height = getCookie("text_editor_height").length
        ? getCookie("text_editor_height")
        : "454px";
      const $editor = jQuery(".ace_editor");
      $editor.css("height", height);

      window.addEventListener("beforeunload", function () {
        setLocalCookie("text_editor_height", $editor.css("height"));
      });
    }
  };
  // <------ Editor Size

  // ------> Regen Button
  const addRegenButton = () => {
    // This script is brought to you by Thibault, thanks to him :)
    if (modules.regenButton.active && location.href.includes("/model/")) {
      const titles = document.querySelectorAll("h2.title");
      console.log(titles);
      const title = titles[titles.length - 1];
      console.log(title);
      title.insertAdjacentHTML(
        "beforeend",
        `<span id="regen">${String.fromCodePoint(0x1f504)}</span>`
      );
      const regen = document.querySelector("#regen");
      console.log(regen);
      regen.style.cursor = "pointer";
      let divs = document.querySelectorAll("fieldset div.moved_field_objects");
      console.log(divs);
      let maveocId = document.querySelector("#content").dataset["in_use"];
      console.log(maveocId);
      let fields = [];

      divs.forEach((mfo, i) => {
        if (divs[i + 1] == mfo.nextElementSibling && fields.length < 2) {
          fields.push(mfo.id.split("_")[1]);
          fields.push(divs[i + 1].id.split("_")[1]);
        }
        return fields;
      });

      regen.addEventListener("click", () => {
        if (!confirm("Do you want to regen this Maveoc ?")) return;

        fetch("https://datx-bsolutions-des.faveod.com/fields/move_dropped", {
          credentials: "include",
          headers: {
            "User-Agent":
              "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0",
            Accept:
              "text/javascript, text/html, application/xml, text/xml, */*",
            "Accept-Language": "en-GB,en;q=0.5",
            "X-Requested-With": "XMLHttpRequest",
            "X-Prototype-Version": "1.6.1",
            "Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-CSRF-Token": "XkQQ715zzX7MXgiqZQMzkFCOox4TiI3VtJ3Vvcsfq5M=",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
            Priority: "u=1",
            Pragma: "no-cache",
            "Cache-Control": "no-cache",
          },
          referrer:
            "https://datx-bsolutions-des.faveod.com/maveocs/model/502?tab=model",
          body: `model=${maveocId}&receiver=${fields[0]}&id=${fields[1]}`,
          method: "POST",
          mode: "cors",
        }).then(console.log(`%c Starting Maveoc regen...`, "color: #38C4CC"));
      });
    }
  };
  // <------ Regen Button

  // ------> DragDrop Locker
  const addDragDropLocker = () => {
    if (modules.dragDropLocker.active) {
      const addLocker = () => {
        return new Promise((resolve, reject) => {
          // add html button
          const titles = document.querySelectorAll("h2.title");
          const title = titles[titles.length - 1];

          const lockerBox = document.createElement("div");
          lockerBox.classList.add("locker-box");
          lockerBox.style.display = "flex";
          lockerBox.style.alignItems = "center";
          lockerBox.style.justifyContent = "center";
          const lockerInput = document.createElement("input");
          lockerInput.id = "js-dragdrop-input";
          lockerInput.setAttribute("type", "checkbox");
          lockerInput.style.display = "none";
          const lockerButton = document.createElement("label");
          lockerButton.classList.add("btn");
          lockerButton.id = "js-dragdrop-label";
          lockerButton.innerHTML = "DragDrop Lock";
          lockerButton.setAttribute("for", "js-dragdrop-input");
          lockerButton.style.border = "1px solid darkblue";
          lockerButton.style.cursor = "pointer";
          lockerButton.style.padding = "5px";

          lockerBox.append(lockerInput);
          lockerBox.append(lockerButton);
          title.append(lockerBox);

          // If we set it manually or by default anyway
          const isLocked =
            getCookie("dragDropLock") == "true" ||
            getCookie("dragDropLock") == "";

          lockerInput.checked = isLocked;

          lockerInput.addEventListener("change", (e) => {
            const label = document.getElementById("js-dragdrop-label");
            e.target.checked ? lock(label) : unlock(label, true);
          });

          resolve(isLocked);
        });
      };

      const lock = (label) => {
        label.innerHTML = "Drag&Drop locked, click to unlock";
        setLocalCookie("dragDropLock", "true", 365, location.pathname);
        const fields = document.querySelectorAll(".moved_field_objects");
        const dragDropPanel = document.querySelector("td.drag_objects");
        dragDropPanel.remove();
        // const rows = document.querySelectorAll('#view_parts_table fieldset')

        Array.from(fields).map((field) => {
          const newElement = field.cloneNode(true);
          field.parentNode.replaceChild(newElement, field);
        });

        const actions = document.querySelectorAll(".field_actions");
        Array.from(actions).map((action) => (action.style.display = "block"));
        Array.from(
          document.querySelectorAll('[data-indicator="model_indicator"]')
        ).map((arrow) => (arrow.style.display = "none"));
      };

      const unlock = (label, reload = false) => {
        if (reload && !confirm("This will reload current page, proceed ?"))
          return;
        label.innerHTML = "Drag&Drop unlocked, click to lock";
        setLocalCookie("dragDropLock", "false", 365, location.pathname);
        if (reload) window.location.reload();
      };

      const moveReminders = () => {
        const tabBox = document.getElementById("mvc_tabs");
        const helpButton = document.querySelector(
          '#field_types img[alt="Help"]'
        ).parentNode;
        const infoButton = document.querySelector(
          '#field_types img[alt="Information"]'
        ).parentNode;

        helpButton.querySelector("img").style.marginRight = "5px";
        infoButton.querySelector("img").style.marginRight = "5px";

        const helpTab = document.createElement("li");
        helpTab.classList.add("help-tab");
        helpTab.style.float = "right";

        const helpLabel = document.createElement("span");
        helpLabel.innerText = "Help";

        const infoTab = document.createElement("li");
        infoTab.classList.add("info-tab");
        infoTab.style.float = "right";

        const infoLabel = document.createElement("span");
        infoLabel.innerText = "Infos";

        helpButton.appendChild(helpLabel);
        infoButton.appendChild(infoLabel);
        helpTab.appendChild(helpButton);
        infoTab.appendChild(infoButton);
        tabBox.appendChild(infoTab);
        tabBox.appendChild(helpTab);
      };

      if (location.href.includes("/model/")) {
        moveReminders();
        addLocker().then((isLocked) => {
          const label = document.getElementById("js-dragdrop-label");
          isLocked ? lock(label) : unlock(label);
        });
      }
    }
  };
  // <------ DragDrop Locker

  // ------> Quality Framework
  const initQualityFramework = () => {
    if (
      modules.qualityFramework.active &&
      !window.location.pathname.includes("/use_cases/")
    ) {
      const initDevalidationWarning = () => {
        const submitButtons = [
          ...document.querySelectorAll('input[type="submit"]'),
        ];
        const originalStatus = document.querySelector("select[id$='_status']");

        if (!submitButtons || !originalStatus) return;

        const isValidated = originalStatus.value == 8;
        const isDraft = originalStatus.value == 1;

        if (isDraft) {
          setTimeout(function () {
            initDevalidationWarning();
            return;
          }, 800);
        }
        if (!isValidated) {
          return;
        }

        submitButtons.map((button) => {
          button.addEventListener("click", function (e) {
            if (!button.classList.contains("confirmed")) {
              e.preventDefault();
              if (
                !confirm(
                  'Saving a "validated" code will make it regress to "waiting for validation". Continue ?'
                )
              )
                return;
              originalStatus.value = 7;
              button.classList.add("confirmed");
              button.click();
            }
          });
        });
      };

      const initFieldsRequired = () => {
        const description = document.querySelector(
          "textarea[id$='_description']"
        );
        // const commit = document.querySelector("textarea[id$='_commit_message']")
        const submitButtons = [
          ...document.querySelectorAll('input[type="submit"]'),
        ];

        if (!submitButtons || !description) return;

        if (description.value.trim().length < 10) {
          submitButtons.map((button) => {
            disableElement(
              button,
              "A description of at least 10 characters is required"
            );
          });
        }

        description.addEventListener("input", () => {
          handleRequiredFields(submitButtons, description);
        });

        // commit.addEventListener('input', () => {
        //     handleRequiredFields(submitButtons, description, commit)
        // })
      };

      const handleRequiredFields = (buttons, description) => {
        if (description.value.trim().length < 10) {
          buttons.map((button) => {
            disableElement(
              button,
              "A description and a commit message of at least 10 characters is required"
            );
          });
        } else {
          buttons.map((button) => {
            button.removeAttribute("disabled");
            button.removeAttribute("title");
            button.removeAttribute("style");
          });
        }
      };

      initDevalidationWarning();
      initFieldsRequired();
    }
  };
  // <------ Quality Framework

  // ------> Experimental UI
  const initExperimentalUi = () => {
    if (modules.experimentalInterface.active) {
      console.log("%c Loading experimental interface", "color: #38C4CC");
      const link = document.createElement("link");
      link.type = "text/css";
      link.rel = "stylesheet";

      document.head.appendChild(link);
      link.href =
        "https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.css";

      jQuery('select[multiple="multiple"]').chosen();
      // jQuery("select[id*='field_format_views_matching_']").chosen()

      jQuery(".chosen-container-multi").css("width", "100%");

      jQuery(".chosen-choices")
        .css("overflow", "auto")
        .css("max-height", "35vh");

      console.log("test", jQuery("#review_filters").find("select"));
      jQuery("#review_filters").find("select").chosen();
    }
  };
  // <----- Experimental UI

  jQuery(document).ready(function () {
    setTimeout(function () {
      initOverlay();
    }, 800);
  });
})(jQuery);

// ------> HELPERS (overlay  global)
function setLocalCookie(name, value, days, path = "/") {
  let expires = "";
  if (days) {
    const date = new Date();
    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
    expires = "; expires=" + date.toUTCString();
  }
  document.cookie = name + "=" + (value || "") + expires + `; path=${path}`;
}

function getCookie(cname) {
  const decodedCookie = decodeURIComponent(document.cookie);
  const ca = decodedCookie.split(";");
  const match = ca.filter((cookie) => {
    return cookie.includes(cname);
  });
  return match[0] ? match[0].replace(`${cname}=`, "").trim() : "";
}

function disableElement(element, title = "") {
  element.setAttribute("disabled", "disabled");
  element.setAttribute("title", title);
  element.style.color = "grey";
  element.style.borderColor = "grey";
  element.style.cursor = "not-allowed";
}

function delay(time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}
// <------ HELPERS