您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
13/11/2023 02:55:01
当前为
// ==UserScript== // @name Dreadcast Development Kit // @namespace Violentmonkey Scripts // @match https://www.dreadcast.net/Main // @version 1.0.10 // @author Pelagia/Isilin // @description 13/11/2023 02:55:01 // @license http://creativecommons.org/licenses/by-nc-nd/4.0/ // @connect docs.google.com // @connect googleusercontent.com // @connect sheets.googleapis.com // @connect raw.githubusercontent.com // @grant GM_xmlhttpRequest // @grant GM_addStyle // @downloadURL // @updateURL // ==/UserScript== // TODO add guards in each function to check Game/EDC/Forum // TODO add function to add deck command console.log('DDK - Loading ...'); // ===== JQuery utilities ===== $.fn.insertAt = function (index, element) { var lastIndex = this.children().size(); if (index < 0) { index = Math.max(0, lastIndex + 1 + index); } this.append(element); if (index < lastIndex) { this.children().eq(index).before(this.children().last()); } return this; }; // ===== Lib ===== const Util = { guard: (condition, message) => { if (!condition) throw new Error(message); return; }, deprecate: (name, replacement) => { console.warn( name + ': this function has been deprecated and should not be used anymore.' + (replacement && replacement !== '' ? 'Prefer: ' + replacement + '.' : ''), ); }, isArray: (o, optional = false) => $.type(o) === 'array' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isString: (o, optional = false) => $.type(o) === 'string' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isBoolean: (o, optional = false) => $.type(o) === 'boolean' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isNumber: (o, optional = false) => $.type(o) === 'number' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isFunction: (o, optional = false) => $.type(o) === 'function' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isDate: (o, optional = false) => $.type(o) === 'date' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isError: (o, optional = false) => $.type(o) === 'error' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isRegex: (o, optional = false) => $.type(o) === 'regexp' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isObject: (o, optional = false) => $.type(o) === 'object' || (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')), isColor: (o, optional = false) => { if (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')) return true; else { const colors = ['rouge', 'bleu', 'vert', 'jaune']; return ( $.type(o) === 'string' && (colors.includes(o) || o.match(/^[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3}$/gi)) ); } }, isGame: () => window.location.href.includes('https://www.dreadcast.net/Main'), isForum: () => window.location.href.includes('https://www.dreadcast.net/Forum'), isEDC: () => window.location.href.includes('https://www.dreadcast.net/EDC'), isWiki: () => window.location.href.includes('http://wiki.dreadcast.eu/wiki'), getContext: () => { return Util.isGame() ? 'game' : Util.isForum() ? 'forum' : Util.isEDC() ? 'edc' : 'wiki'; }, }; // ===== Overwrite DC functions ===== if (Util.isGame() && MenuChat.prototype.originalSend === undefined) { MenuChat.prototype.originalSend = MenuChat.prototype.send; MenuChat.prototype.sendCallbacks = []; MenuChat.prototype.afterSendCallbacks = []; MenuChat.prototype.send = function () { const $nextFn = () => true; const $abortFn = () => false; const $message = $('#chatForm .text_chat').val(); const $res = this.sendCallbacks.every((callback) => callback($message, $nextFn, $abortFn), ); if (!$res) { throw new Error('MenuChat.prototype.send: Error on sending message.'); } this.originalSend(); this.afterSendCallbacks.every((callback) => callback($message)); }; MenuChat.prototype.onSend = (callback) => { MenuChat.prototype.sendCallbacks.push(callback); }; MenuChat.prototype.onAfterSend = (callback) => { MenuChat.prototype.afterSendCallbacks.push(callback); }; } // ============================ const DC = {}; DC.LocalMemory = { init: (label, defaultValue) => { const $currentVal = GM_getValue(label); if ($currentVal === undefined) { GM_setValue(label, defaultValue); return defaultValue; } else { return $currentVal; } }, set: (label, value) => GM_setValue(label, value), get: (label) => GM_getValue(label), delete: (label) => GM_deleteValue(label), list: () => GM_listValues(), }; DC.Style = { apply: (css) => { Util.guard( Util.isString(css, true), "DC.Style.apply: 'css' parameter should be a string.", ); if (typeof GM_addStyle !== 'undefined') { GM_addStyle(css); } else { let $styleNode = document.createElement('style'); $styleNode.appendChild(document.createTextNode(css)); (document.querySelector('head') || document.documentElement).appendChild( $styleNode, ); } }, }; DC.TopMenu = { get: () => { return $('.menus'); }, add: (element, index = 0) => { Util.guard( Util.isNumber(index), "DC.TopMenu.add: 'index' parameter should be a number.", ); const $dom = DC.TopMenu.get(); if (index === 0) { $dom.prepend(element); } else { $dom.insertAt(index, element); } }, }; DC.UI = { Separator: () => $('<li class="separator" />'), Menu: (label, fn) => { Util.guard( Util.isString(label), "DC.UI.Menu: 'label' parameter should be a string.", ); Util.guard( Util.isFunction(fn), "DC.UI.Menu: 'fn' parameter should be a function.", ); return $(`<li id="${label}" class="couleur5">${label}</li>`).bind( 'click', fn, ); }, SubMenu: (label, fn, separatorBefore = false) => { Util.guard( Util.isString(label), "DC.UI.SubMenu: 'label' parameter should be a string.", ); Util.guard( Util.isFunction(fn), "DC.UI.SubMenu: 'fn' parameter should be a function.", ); Util.guard( Util.isBoolean(separatorBefore), "DC.UI.SubMenu: 'separatorBefore' parameter should be a boolean.", ); return $( `<li class="link couleur2 ${ separatorBefore ? 'separator' : '' }">${label}</li>`, ).bind('click', fn); }, DropMenu: (label, submenu) => { Util.guard( Util.isString(label), "DC.UI.DropMenu: 'label' parameter should be a string.", ); const $label = label + '▾'; const $list = $('<ul></ul>'); if (!Array.isArray(submenu)) { throw new Error("'submenu' should be an array in DC.UI.DropMenu !"); } submenu.forEach(($submenu) => { $($list).append($submenu); }); return $( `<li id="${label}" class="parametres couleur5 right hover" onclick="$(this).find('ul').slideDown();">${$label}</li>`, ).append($list); }, addSubMenuTo: (name, element, index = 0) => { Util.guard( Util.isString(name), "DC.UI.addSubMenuTo: 'name' parameter should be a string.", ); Util.guard( Util.isNumber(index), "DC.UI.addSubMenuTo: 'index' parameter should be a string.", ); const $menu = $(`.menus li:contains("${name}") ul`); if (index === 0) { $menu.prepend(element); } else { $menu.insertAt(index, element); } }, TextButton: (id, label, fn) => { Util.guard( Util.isString(id), "DC.UI.TextButton: 'id' parameter should be a string.", ); Util.guard( Util.isString(label), "DC.UI.TextButton: 'label' parameter should be a string.", ); Util.guard( Util.isFunction(fn), "DC.UI.TextButton: 'fn' parameter should be a function.", ); return $(`<div id="${id}" class="btnTxt">${label}</div>`).bind('click', fn); }, Button: (id, label, fn) => { Util.guard( Util.isString(id), "DC.UI.Button: 'id' parameter should be a string.", ); Util.guard( Util.isString(label), "DC.UI.Button: 'label' parameter should be a string.", ); Util.guard( Util.isFunction(fn), "DC.UI.Button: 'fn' parameter should be a function.", ); return $( `<div id="${id}" class="btn add link infoAide"><div class="gridCenter">${label}</div></div>`, ).bind('click', fn); }, Tooltip: (text, content) => { DC.Style.apply(` .tooltip { position: relative; display: inline-block; } .tooltip .tooltiptext { visibility: hidden; background-color: rgba(24,24,24,0.95); color: #fff; text-align: center; padding: 5px; border-radius: 6px; position: absolute; z-index: 1; font-size: 1rem; } .tooltip:hover .tooltiptext { visibility: visible; } `); return $(`<div class="tooltip"> <span class="tooltiptext">${text}</span> </div>`).prepend(content); }, Checkbox: (id, defaultEnable = true, onAfterClick) => { Util.guard( Util.isString(id), "DC.UI.Checkbox: 'id' parameter should be a string.", ); Util.guard( Util.isBoolean(defaultEnable), "DC.UI.Checkbox: 'defaultEnable' parameter should be a boolean.", ); Util.guard( Util.isFunction(onAfterClick, true), "DC.UI.Checkbox: 'onAfterClick' optional parameter should be a function.", ); DC.Style.apply(` .dc_ui_checkbox { cursor: pointer; width: 30px; height: 18px; background: url(../../../images/fr/design/boutons/b_0.png) 0 0 no-repeat; } .dc_ui_checkbox_on { background: url(../../../images/fr/design/boutons/b_1.png) 0 0 no-repeat; } `); return $( `<div id="${id}" class="dc_ui_checkbox ${ defaultEnable ? 'dc_ui_checkbox_on' : '' }" />`, ).bind('click', () => { $(`#${id}`).toggleClass('dc_ui_checkbox_on'); onAfterClick?.($(`#${id}`).hasClass('dc_ui_checkbox_on')); }); }, PopUp: (id, title, content) => { Util.guard( Util.isString(id), "DC.UI.PopUp: 'id' parameter should be a string.", ); Util.guard( Util.isString(title), "DC.UI.PopUp: 'title' parameter should be a string.", ); $('#loader').fadeIn('fast'); const html = ` <div id="${id}" class="dataBox" onClick="engine.switchDataBox(this)" style="display: block; z-index: 5; left: 764px; top: 16px;"> <relative> <div class="head" ondblclick="$('#${id}').toggleClass('reduced');"> <div title="Fermer la fenêtre (Q)" class="info1 link close transition3s" onClick="engine.closeDataBox($(this).parent().parent().parent().attr('id'));" alt="$('${id}').removeClass('active')"> <i class="fas fa-times"></i> </div> <div title="Reduire/Agrandir la fenêtre" class="info1 link reduce transition3s" onClick="$('#${id}').toggleClass('reduced');"> <span>-</span> </div> <div class="title">${title}</div> </div> <div class="dbloader"></div> <div class="content" style="max-width: 800px; max-height: 600px; overflow-y: auto; overflow-x: hidden;"> </div> </relative> </div>`; engine.displayDataBox(html); $(`#${id} .content`).append(content); $('#loader').hide(); }, SideMenu: (id, label, content) => { Util.guard( Util.isString(id), "DC.UI.SideMenu: 'id' parameter should be a string.", ); Util.guard( Util.isString(label), "DC.UI.SideMenu: 'label' parameter should be a string.", ); Util.guard( Util.isString(content), "DC.UI.SideMenu: 'content' parameter should be a string.", ); const idContainer = id + '_container'; const idButton = id + '_button'; const idContent = id + '_content'; if ($('div#zone_sidemenu').length === 0) { $('body').append('<div id="zone_sidemenu"></div>'); } $('#zone_sidemenu').append( `<div id="${idContainer}" class="sidemenu_container"></div>`, ); $(`#${idContainer}`).append( DC.UI.TextButton( idButton, '<i class="fas fa-chevron-left"></i>' + label, () => { const isOpen = $(`#${idButton}`).html().includes('fa-chevron-right'); if (isOpen) { $(`#${idButton}`) .empty() .append('<i class="fas fa-chevron-left"></i>' + label); $(`#${idContainer}`).css('right', '-220px'); } else { $(`#${idButton}`) .empty() .append('<i class="fas fa-chevron-right"></i>' + label); $(`#${idContainer}`).css('right', '0px'); } }, ), ); $(`#${idContainer}`).append( `<div id="${idContent}" class="sidemenu_content">${content}</div>`, ); DC.Style.apply(` #zone_sidemenu { display: flex; flex-direction: column; position: absolute; right: 0px; top: 80px; z-index: 999999; } .sidemenu_container { display: flex; right: -220px; } #zone_sidemenu .btnTxt { margin: 0 auto; min-width: 100px; max-width: 100px; font-size: 1rem; padding: 1%; display: grid; height: 100%; box-sizing: border-box; grid-template-columns: 10% 1fr; align-items: center; text-transform: uppercase; font-family: Arial !important; line-height: normal !important; } #zone_sidemenu .btnTxt:hover { background: #0b9bcb; color: #fff; } .sidemenu_content { background-color: #000; color: #fff !important; box-shadow: 0 0 15px -5px inset #a2e4fc !important; padding: 10px; width: 200px; } `); }, }; DC.Network = { fetch: (args) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest( Object.assign({}, args, { onload: (e) => resolve(e.response), onerror: reject, ontimeout: reject, }), ); }); }, loadSpreadsheet: async (sheetId, tabName, range, apiKey, onLoad) => { Util.guard( Util.isString(sheetId), "DC.Network.loadSpreadsheet: 'sheetId' parameter should be a string.", ); Util.guard( Util.isString(tabName), "DC.Network.loadSpreadsheet: 'tabName' parameter should be a string.", ); Util.guard( Util.isString(range), "DC.Network.loadSpreadsheet: 'range' parameter should be a string.", ); Util.guard( Util.isString(apiKey), "DC.Network.loadSpreadsheet: 'apiKey' parameter should be a string.", ); Util.guard( Util.isFunction(onLoad), "DC.Network.loadSpreadsheet: 'onLoad' parameter should be a function.", ); const urlGoogleSheetDatabase = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${tabName}!${range}?key=${apiKey}`; const result = await DC.Network.fetch({ method: 'GET', url: urlGoogleSheetDatabase, headers: { 'Content-Type': 'application/json', }, responseType: 'json', }); onLoad(result.values); }, loadScript: async (url, onAfterLoad) => { Util.guard( Util.isString(url), "DC.Network.loadScript: 'url' parameter should be a string.", ); Util.guard( Util.isFunction(onAfterLoad, true), "DC.Network.loadScript: 'onAfterLoad' optional parameter should be a function.", ); // TODO we should check that url is from a valid and secure source. const result = await DC.Network.fetch({ method: 'GET', url, headers: { 'Content-Type': 'text/javascript', }, }); // TODO we have to secure more this call eval(result); onAfterLoad?.(); }, loadJson: async (url) => { Util.guard( Util.isString(url), "DC.Network.loadJson: 'url' parameter should be a string.", ); const result = await DC.Network.fetch({ method: 'GET', url, headers: { 'Content-Type': 'application/json', }, responseType: 'json', }); return result; }, }; DC.Chat = { sendMessage: (message) => { Util.guard( Util.isString(message), "DC.Chat.sendMessage: 'message' parameter should be a string.", ); $('#chatForm .text_chat').val(message); $('#chatForm .text_valider').click(); }, t: (message, decoration) => { Util.guard( Util.isString(message, true), "DC.Chat.t: 'message' parameter should be a string.", ); Util.guard( Util.isBoolean(decoration.bold, true), "DC.Chat.t: 'bold' optional parameter should be a boolean.", ); Util.guard( Util.isBoolean(decoration.italic, true), "DC.Chat.t: 'italic' optional parameter should be a boolean.", ); Util.guard( Util.isColor(decoration.color, true), "DC.Chat.t: 'color' optional parameter should be a color string.", ); var prefix = ''; var suffix = ''; if (decoration.bold) { prefix += '[b]'; suffix += '[b]'; } if (decoration.italic) { prefix += '[i]'; suffix = '[/i]' + suffix; } if (decoration.color && decoration.color !== '') { prefix += '[c=' + decoration.color + ']'; suffix = '[/c]' + suffix; } return prefix + message + suffix; }, addCommand: (label, fn) => { Util.guard( Util.isString(label), "DC.Chat.addCommand: 'label' parameter should be a string.", ); Util.guard( Util.isFunction(fn), "DC.Chat.addCommand: 'fn' parameter should be a function.", ); nav.getChat().onSend((message, next, abort) => { const forbiden = ['me', 'y', 'ye', 'yme', 'w', 'we', 'wme', 'roll', '']; const labelUsed = message.split(' ')[0].substr(1); if ( message[0] !== '/' || labelUsed !== label || forbiden.includes(labelUsed) ) { return next(); } const content = message.substr(labelUsed.length + 1); if (fn(labelUsed, content)) { return next(); } else { return abort(); } }); }, };