Dreadcast Script Manager

13/11/2023 02:55:01

Version au 07/09/2024. Voir la dernière version.

// ==UserScript==
// @name        Dreadcast Script Manager
// @namespace   Violentmonkey Scripts
// @match       https://www.dreadcast.net/Main
// @match       https://www.dreadcast.net/Forum
// @match       https://www.dreadcast.net/Forum/*
// @match       https://www.dreadcast.net/EDC
// @match       https://www.dreadcast.net/EDC/*
// @version     1.0.1
// @author      Pelagia/Isilin
// @description 13/11/2023 02:55:01
// @license     http://creativecommons.org/licenses/by-nc-nd/4.0/
// @require     https://update.greasyfork.org/scripts/507382/Dreadcast%20Development%20Kit.user.js
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_deleteValue
// @grant       GM_listValues
// @grant       GM_xmlhttpRequest
// @grant       GM_addStyle
// @connect     docs.google.com
// @connect     googleusercontent.com
// @connect     sheets.googleapis.com
// @connect     raw.githubusercontent.com
// ==/UserScript==

// TODO ajouter dans la liste tous les scripts (en utilisant le lien greasemonkey) et remplacer petit à petit par les versions locales nettoyées.
// TODO add function to export, import settings, and to reset all settings.
// TODO add text to say that disabling a script does not remove settings.

const LIST_TAG = 'dcm_list';
const ALL_DISABLED_TAG = 'dcm_all_disabled';

let newSettings, newAllDisabled;

const initPersistence = () => {
  // Init persistent memory if needed.
  DC.LocalMemory.init(LIST_TAG, {});
  DC.LocalMemory.init(ALL_DISABLED_TAG, false);

  // Load the current settings.
  let settings = DC.LocalMemory.get(LIST_TAG);
  let allDisabled = DC.LocalMemory.get(ALL_DISABLED_TAG);

  return { settings, allDisabled };
};

const synchronizeSettings = (settings, scripts) => {
  let tmp = settings;

  scripts.forEach((script) => {
    if (!Object.hasOwn(tmp, script.id)) {
      // Update the settings, if there is new scripts.
      tmp[script.id] = false;
    }
  });

  // Remove in settings, scripts that doesn't exist anymore.
  tmp = Object.keys(tmp)
    .filter((key) => scripts.find((script) => script.id === key) !== undefined)
    .reduce((obj, key) => {
      obj[key] = tmp[key];
      return obj;
    }, {});

  // Save the new settings in persistent memory.
  DC.LocalMemory.set(LIST_TAG, tmp);

  return tmp;
};

const createScriptLine = (script, index) => {
  const line = $(`
    <tr style="border-top: 1px solid white; border-left: 1px solid white; border-right: 1px solid white;">
      <td style="padding: 5px 0 0 5px" rowspan="2">${index}</td>
      ${
        script.icon && script.icon !== ''
          ? `<td style="padding: 5px" rowspan="2"><img src="${script.icon}" width="48" height="48" /></td>`
          : '<td class="short" style="width: 58px;" rowspan="2" />'
      }
      <td style="padding: 5px 0; min-width: 120px; text-align: left;">${
        script.name || ''
      }</td>
      <td style="padding: 5px 0; min-width: 120px; text-align: left;"><small>${
        script.authors || ''
      }</small></td>
      <td class="enabled_cell" style="padding: 5px 0; display: flex; justify-content: center;"></td>
      <td class="setting_cell" style="padding: 5px 5px 0 0;"></td>
      <td class="doc_cell" style="padding: 5px 5px 0 0;"></td>
      <td class="rp_cell" style="padding: 5px 5px 0 0;"></td>
      <td class="contact_cell" style="padding: 5px 5px 0 0;"></td>
    </tr>
    <tr style="border-bottom: 1px solid white; border-left: 1px solid white; border-right: 1px solid white;">
      <td colspan="7" style="padding: 0 5px 5px 5px; text-align: left;"><small><em class="couleur5">${
        script.description || ''
      }</em></small></td>
    </tr>
  `);
  $('.enabled_cell', line).append(
    DC.UI.Checkbox(
      `${script.id}_check`,
      newSettings[script.id],
      () => (newSettings[script.id] = !newSettings[script.id]),
    ),
  );
  if (script.settings) {
    $('.setting_cell', line).append(
      DC.UI.Tooltip(
        'Settings',
        DC.UI.Button(
          `${script.id}_setting`,
          '<i class="fas fa-cog"></i>',
          () => {},
        ),
      ),
    );
  }
  if (script.doc && script.doc !== '') {
    $('.doc_cell', line).append(
      DC.UI.Tooltip(
        'Documentation',
        DC.UI.Button(`${script.id}_doc`, '<i class="fas fa-book"></i>', () =>
          window.open(script.doc, '_blank'),
        ),
      ),
    );
  }
  if (script.rp && script.rp !== '') {
    $('.rp_cell', line).append(
      DC.UI.Tooltip(
        'Topic RP',
        DC.UI.Button(
          `${script.id}_rp`,
          '<div class=""gridCenter>RP</div>',
          () => window.open(script.doc, '_blank'),
        ),
      ),
    );
  }
  if (script.contact && script.contact !== '') {
    $('.contact_cell', line).append(
      DC.UI.Tooltip(
        'Contact',
        DC.UI.Button(`${script.id}_rp`, '<i class="fas fa-envelope"></i>', () =>
          nav.getMessagerie().newMessage(script.contact),
        ),
      ),
    );
  }

  return line;
};

$(() => {
  let { settings, allDisabled } = initPersistence();

  const createUI = (scripts, settings) => {
    DC.UI.addSubMenuTo(
      'Paramètres ▾',
      DC.UI.SubMenu(
        'Scripts & Skins',
        () => {
          // On récupère une config temporaire qu'on appliquera uniquement si sauvegardée.
          newSettings = settings;
          newAllDisabled = allDisabled;

          const sections = [
            { id: 'all', label: 'Tous' },
            { id: 'game', label: 'Jeu' },
            { id: 'forum', label: 'Forum' },
            { id: 'edc', label: 'EDC' },
          ];

          const categories = [
            { id: 'all', label: 'Tous' },
            { id: 'mailing', label: 'Messagerie' },
            { id: 'chat', label: 'Chat' },
            { id: 'silhouette', label: 'Silhouette' },
            { id: 'ui', label: 'UI' },
            { id: 'mech', label: 'Mécaniques' },
            { id: 'fix', label: 'Correctifs' },
            { id: 'misc', label: 'Autres' },
          ];

          const content = $(`<div style="color: white;">
            <div id="scripts_all_switch" style="display: flex;gap: 1rem;margin-bottom: 1rem;">
              <p>Tout désactiver</p>
            </div>
            <div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
              <legend style="margin-right: 1rem; min-width: 60px;">Filtrer :</legend>
              <div style="display: flex; gap: 5%; flex-wrap: wrap; width: 100%;">
                ${sections
                  .map(
                    (section, index) => `
                    <div>
                      <input type="radio" id="${
                        section.id
                      }_section" name ="section" value ="${section.id}" ${
                      index === 0 ? 'checked' : ''
                    } />
                      <label for="${section.id}_section">${
                      section.label
                    }</label>
                    </div>
                `,
                  )
                  .join('')}
              </div>
            </div>
            <div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
              <legend style="margin-right: 1rem; min-width: 60px;">Filtrer :</legend>
              <div style="display: flex; gap: 5%; flex-wrap: wrap; width: 100%;">
                ${categories
                  .map(
                    (category, index) => `
                    <div>
                      <input type="radio" id="${
                        category.id
                      }_category" name ="category" value ="${category.id}" ${
                      index === 0 ? 'checked' : ''
                    } />
                      <label for="${category.id}_category">${
                      category.label
                    }</label>
                    </div>
                `,
                  )
                  .join('')}
              </div>
            </div>
            <table style="border-collapse: collapse; width: 100%; border: 1px solid white; padding: 5px; font-size: 15px; text-align: center;">
              <thead>
                <th style="padding: 5px 0 5px 5px" scope="col">#</th>
                <th class="short" style="width:58px;" />
                <th style="padding: 5px 0 5px 0" scope="col">Nom</th>
                <th style="padding: 5px 0 5px 0" scope="col">Auteurs</th>
                <th style="padding: 5px 0 5px 0" scope="col">Actif</th>
                <th class="short" style="width: 40px;" />
                <th class="short" style="width: 40px;" />
                <th class="short" style="width: 40px;" />
                <th class="short" style="width: 40px;" />
              </thead>
              <tbody></tbody>
            </table>
          </div>`);

          $(document).on('change', "input[name='category']", (e) => {
            const category = e.target.value;
            const section = $("input[name='section']:checked").val();

            // Empty the table
            $('tbody', content).empty();
            // Add filtered lines
            scripts
              .filter(
                (script) =>
                  (script.section.includes(section) || section === 'all') &&
                  (script.category.includes(category) || category === 'all'),
              )
              .forEach((script, index) => {
                const line = createScriptLine(script, index);
                $('tbody', content).append(line);
              });
          });

          $(document).on('change', "input[name='section']", (e) => {
            const section = e.target.value;
            const category = $("input[name='category']:checked").val();

            // Empty the table
            $('tbody', content).empty();
            // Add filtered lines
            scripts
              .filter(
                (script) =>
                  (script.section.includes(section) || section === 'all') &&
                  (script.category.includes(category) || category === 'all'),
              )
              .forEach((script, index) => {
                const line = createScriptLine(script, index);
                $('tbody', content).append(line);
              });
          });

          // Sauvegarder les paramètres.
          content.append(
            DC.UI.TextButton('scripts_refresh', 'Sauvegarder', () => {
              settings = newSettings;
              allDisabled = newAllDisabled;
              DC.LocalMemory.set(LIST_TAG, settings);
              DC.LocalMemory.set(ALL_DISABLED_TAG, allDisabled);
              location.reload();
            }),
          );
          content.append(
            $(
              `<p><em class="couleur5">⚠ Sauvegarder votre configuration va raffraichir la page.<br />
           Pensez à sauvegarder votre travail en cours avant.</em></p>`,
            ),
          );

          // Switch button pour désactiver tous les scripts.
          $('#scripts_all_switch', content).append(
            DC.UI.Checkbox(
              'scripts_all_check',
              newAllDisabled,
              () => (newAllDisabled = !newAllDisabled),
            ),
          );

          scripts.forEach((script, index) => {
            const line = createScriptLine(script, index);
            $('tbody', content).append(line);
          });

          return DC.UI.PopUp('scripts_modal', 'Scripts & Skins', content);
        },
        true,
      ),
      5,
    );
  };

  // Load list of scripts
  DC.Network.loadJson(
    'https://raw.githubusercontent.com/Isilin/dreadcast-scripts/main/data/scripts.json',
  )
    .then((scripts) => {
      settings = synchronizeSettings(settings, scripts);

      // Create the interface.
      if (Util.isGame()) {
        createUI(scripts, settings);
      }

      // Load the scripts
      if (!allDisabled) {
        const context = Util.getContext();

        scripts
          .filter((script) => script.section.includes(context))
          .forEach((script) => {
            if (settings[script.id]) {
              DC.Network.loadScript(script.url)
                .then(() => {
                  console.info(
                    `DCSM - '${script.name}' script has been loaded successfully.`,
                  );
                })
                .catch((err) => {
                  console.error(
                    `DCSM - Error loading '${script.name}' script: ` + err,
                  );
                });
            }
          });
      }
    })
    .catch((err) => {
      console.error('DCSM - Error loading the list of scripts :' + err);
    });
});