// ==UserScript== // @name Dreadcast Development Kit // @namespace Violentmonkey Scripts // @match https://www.dreadcast.net/Main // @version 1.0.12 // @author Pelagia/Isilin // @description Development kit to ease Dreadcast scripts integration. // @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 none // ==/UserScript== // 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)) ); } }, isJQuery: (o, optional = false) => (optional && ($.type(o) === 'undefined' || $.type(o) === 'null')) || o instanceof $, guardArray: (context, name, parameter, optional = false) => Util.guard( Util.isArray(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be an array.`, ), guardString: (context, name, parameter, optional = false) => Util.guard( Util.isString(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a string.`, ), guardBoolean: (context, name, parameter, optional = false) => Util.guard( Util.isBoolean(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a boolean.`, ), guardNumber: (context, name, parameter, optional = false) => Util.guard( Util.isNumber(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a number.`, ), guardFunction: (context, name, parameter, optional = false) => Util.guard( Util.isFunction(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a a function.`, ), guardDate: (context, name, parameter, optional = false) => Util.guard( Util.isDate(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a date.`, ), guardError: (context, name, parameter, optional = false) => Util.guard( Util.isError(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be an error.`, ), guardRegex: (context, name, parameter, optional = false) => Util.guard( Util.isRegex(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a regex.`, ), guardObject: (context, name, parameter, optional = false) => Util.guard( Util.isObject(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be an object.`, ), guardColor: (context, name, parameter, optional = false) => Util.guard( Util.isColor(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a color.`, ), guardJQuery: (context, name, parameter, optional = false) => Util.guard( Util.isJQuery(parameter, optional), `${context}: '${name}' ${ optional ? 'optional ' : '' }parameter should be a jQuery element.`, ), 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) => { Util.guard( Util.isGame(), 'MenuChat.prototype.onSend: this function should be called in Game only.', ); MenuChat.prototype.sendCallbacks.push(callback); }; MenuChat.prototype.onAfterSend = (callback) => { Util.guard( Util.isGame(), 'MenuChat.prototype.onSend: this function should be called in Game only.', ); 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.guardString('DC.Style.apply', 'css', css); 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: () => { Util.guard( Util.isGame(), 'MenuChat.prototype.onSend: this function should be called in Game only.', ); return $('.menus'); }, add: (element, index = 0) => { Util.guard( Util.isGame(), 'MenuChat.prototype.onSend: this function should be called in Game only.', ); Util.guardJQuery('DC.TopMenu.add', 'element', element); Util.guardNumber('DC.TopMenu.add', 'index', index); const $dom = DC.TopMenu.get(); if (index === 0) { $dom.prepend(element); } else { $dom.insertAt(index, element); } }, }; DC.UI = { Separator: () => $('
'), Menu: (label, fn) => { Util.guardString('DC.UI.Menu', 'label', label); Util.guardFunction('DC.UI.Menu', 'fn', fn); return $(`