diff --git a/README.md b/README.md
index 7ac2ee3..dcd1655 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,81 @@
# Бибилотека первоапрельских розыгрышей для форумов MyBB
Базовое использование:
+
```html
```
---
+
## Полный список шуток
-* `shake` — **Форумо-трясение**. Страница форума начинает трястись.
-* `mirror` — **Зеркало**. Элементы форума отражаются... По крайней мере, пытаются.
-* `carnival` — **Карнавал**. Аватарки приобретают интересные цвета.
-* `humanitarian_hell` — **Ад гуманитария**. Все "тся" заменяются на "ться", а все "ться" на "тся".
-* `crazy_letters` — **Мама, Я сОшЛа С уМа!** иМеНа ЗаБоРчИкОм
-* `letter_chaos` — **Буквенный хаос**. Все буквы в словах, кроме первой и последней, перемешены.
-* `cursor` — **Красивые курсоры**. Эффекты, следующие за курсором.
+- `shake` — **Форумо-трясение**. Страница форума начинает трястись.
+- `mirror` — **Зеркало**. Элементы форума отражаются... По крайней мере, пытаются.
+- `carnival` — **Карнавал**. Аватарки приобретают интересные цвета.
+- `humanitarian_hell` — **Ад гуманитария**. Все "тся" заменяются на "ться", а все "ться" на "тся".
+- `crazy_letters` — **Мама, Я сОшЛа С уМа!** иМеНа ЗаБоРчИкОм
+- `letter_chaos` — **Буквенный хаос**. Все буквы в словах, кроме первой и последней, перемешены.
+- `cursor` — **Красивые курсоры**. Эффекты, следующие за курсором.
+- `stupid_title` — **Глупые статусы**. Каждому пользователю даётся новый глупый статус.
---
+
+## Тестирование шуток
+
+Что бы проверить работу системы, вы можете включить "режим тестера". Для этого добавьте свой `UserID` в настройку `core.testers`:
+
+```js
+window.AprilJokes.settings = {
+ // ...
+ core: {
+ testers: [ 2, 3, 1186 ],
+ },
+ // ...
+};
+```
+
+---
+
## Настройка шуток
Настройка шуток происходит при присвоении объекта `window.AprilJokes.settings`.
Это присвоедние должно произойти **после** загрузки скрипта.
Для всех шуток определны 2 общих поля:
+
```js
window.AprilJokes.settings = {
- // ...
- example: {
- /**
- * Активность шутки.
- * Установите это значение `false`, что бы отключить шутку.
- * Если `true`, то шутка будет обработана как обычно.
- */
- enabled: true,
+ // ...
+ example: {
+ /**
+ * Активность шутки.
+ * Установите это значение `false`, что бы отключить шутку.
+ * Если `true`, то шутка будет обработана как обычно.
+ */
+ enabled: true,
- /**
- * Вероятность срабатывания в процентах.
- * Если установлено значение `100`, шутка будет срабатывать всегда.
- * Если установлено значение `0`, шутка никогда не сработает.
- * Лучше отключать шутку, используя enabled = false!
- */
- chance: 20,
- },
- // ...
+ /**
+ * Вероятность срабатывания в процентах.
+ * Если установлено значение `100`, шутка будет срабатывать всегда.
+ * Если установлено значение `0`, шутка никогда не сработает.
+ * Лучше отключать шутку, используя enabled = false!
+ */
+ chance: 20,
+ },
+ // ...
};
```
-Полный список настроек с объяснениями вы можете посмотреть в файле [`settings.example.js`](./settings.example.js). Также представлен файл [`settings.compact.js`](./settings.compact.js), который также содержит все настройки, но без коментариев.
+Полный список настроек с объяснениями вы можете посмотреть в файле [`settings.example.js`](./settings.example.js). Также представлен файл [`settings.compact.js`](./settings.compact.js), который также содержит все настройки, но без коментариев.
При этом, объективно, вставлять весь этот код в HTML-начало может быть "немного" неудобно, поэтому вы можете скачать код (кнопка "Raw" над файлом), изменить его, и загрузить в файлы форума, подключив скрипт таким образом:
@@ -65,7 +86,7 @@ window.AprilJokes.settings = {
```
-Заметьте, что во всех примерах приведены значения настроек по умолчанию. Вы *можете* удалять строки, значения по умолчанию которых вас устраивают.
+Заметьте, что во всех примерах приведены значения настроек по умолчанию. Вы _можете_ удалять строки, значения по умолчанию которых вас устраивают.
Если вас интерсует только несколько шуток, вы можете использовать метод `disableAll()`, что бы выключить выполнение всех шуток, а потом включать отдельные шутки через установку настройки `enabled`:
@@ -73,20 +94,21 @@ window.AprilJokes.settings = {
window.AprilJokes.disableAll();
window.AprilJokes.settings = {
+ // ...
+ example: {
+ enabled: true,
// ...
- example: {
- enabled: true,
- // ...
- },
- // ...
+ },
+ // ...
};
```
-Обратите внимание, что метод `disableAll()` должен быть вызван *до* установки настроек `settings`, иначе заданные значения `enabled` будут сброшены.
+Обратите внимание, что метод `disableAll()` должен быть вызван _до_ установки настроек `settings`, иначе заданные значения `enabled` будут сброшены.
Так же есть метод `window.AprilJokes.enableAll()`, который включает все шутки, в том числе отключённые по умолчанию.
---
+
### Настройка `shake` (Форумо-трясение)
Тряска элементов происходит путём изменения перечисленных направлений `margin` на значение ±`force` от стандартного значения.
@@ -94,61 +116,117 @@ window.AprilJokes.settings = {
> **Важно**: эта шутка может вызвать дискомфорт у пользователей. Настоятельно рекомендуется оставить вероятность срабатывания шутки (`chance`) очень низкой, что бы у пользователей была возможность избавиться от шутки простым обновлением страницы! Вариант отключить шутку (`enabled: false`) тоже вариант.
---
+
### Настройка `mirror` (Зеркало)
-Элементы форума отражаются по весьма специфичному алгоритму... И из-за этого, шутка может работать *очень* плохо на некоторых дизайнах, поэтому данная шутка по умолчанию **отключена**
+
+Элементы форума отражаются по весьма специфичному алгоритму... И из-за этого, шутка может работать _очень_ плохо на некоторых дизайнах, поэтому данная шутка по умолчанию **отключена**
Для того, что бы попробовать, сработает ли шутка для вашего дизайна, используйте в консоли разработчика:
+
```js
window.AprilJokes.start("mirror", true);
```
-Так же можете [посмотреть](./src/jokes/mirror.ts#L16) на метод `MirrorJoke.start`, что бы узнать как именно проиходит отражение.
+Так же можете [посмотреть](./src/jokes/mirror.ts#L16) на метод `MirrorJoke.start`, что бы узнать как именно проиходит отражение.
---
+
### Настройка `cursor` (Красивые курсоры)
+
Используются курсоры, предложенные в библотеке `tholman/cursor-effects` от Tim Holman ([Github](https://github.com/tholman/cursor-effects), [сайт](https://tholman.com/cursor-effects/)).
Код предусматривает, что одновременно активировано не более 1 курсора из этой библиотеки. Так же для каждого курсора установлен свой шанс срабатывания, однако из-за необходимости активации строго одного эффекта, предусмотрено использование весового коэффициента `weight`: если говорить просто, то чем больше это значение, то тем больше шанс срабатывания эффекта относительно других эффектов.
Так же для каждого эффекта определена настройка `enabled`, позволяющая отключить эффекты, и метод `disableAll` для отключения эффектов:
+
```js
window.AprilJokes.jokes.cursor.disableAll();
```
---
+
+### Настройка `stupid_title` (Глупые статусы)
+
+> **Важно**: этот блок может быть чрезвычайно капризым из-за особенностей хранения данных, особенно блок `variants`. Изменяя его, строго следуйте этой инструкции, иначе скрипт может перестать работать ожидаемым образом.
+
+Данная шутка генерирет статус из случайных слов. За счёт использования псевдослучайного генератора, хеширования и привязки к времени UTC, ожидается что все пользователи одновременно будут видеть один и тот же статус для каждого пользователя, при этом он меняется каждые `change_frequency` минут. Установите это значение равным `0`, что бы статус генерировался при каждом обновлении страницы.
+
+Для настройки списка слов используется функция `BuildStupdiTitleVariant`, которая преобразует список слов во внутренний формат скрипта. Настоятельно рекомендуется использовать именно её, а не записывать параметры напрямую:
+
+```js
+window.AprilJokes.settings = {
+ // ...
+ stupid_title: {
+ // ...
+ variants: {
+ adj: {
+ ...window.AprilJokes.settings.stupid_title.variants.adj,
+ ...BuildStupdiTitleVariant(true, "скромный, шутливый, !грубый"),
+ ...BuildStupdiTitleVariant(false, "скромная, шутливая, !грубая"),
+ },
+ noun: { ...window.AprilJokes.settings.stupid_title.variants.noun },
+ verb: { ...window.AprilJokes.settings.stupid_title.variants.verb },
+ place: { ...window.AprilJokes.settings.stupid_title.variants.place },
+ adv: { ...window.AprilJokes.settings.stupid_title.variants.adv },
+ p: { ...window.AprilJokes.settings.stupid_title.variants.p },
+ },
+ // ...
+ },
+ // ...
+};
+```
+
+Обратите внимание на конструкции вида `{ ...something }` — эта конструкция позволяет перенести все поля нескольких старых объектов в один новый. При этом, если в двух объединяемых объектах есть совпадающие значения, то будет сохранено последнее.
+
+Говоря проще: добовляйте ваши настройки _после_ строки вида `...window.AprilJokes.settings.stupid_title.variants.adj`, что бы ваши настройки не были перезаписаны.
+
+Метод `BuildStupdiTitleVariant` принимает 2 аргумента:
+
+- `gender` — определённый род для генерации. Используйте `true` для мужского рода, `false` для женского и `null`, если добавляемые варианты могут использоваться в обоих случаях.
+- `words` — строка, со списоком вариантов через запятую. В качестве вариантов могут быть слова из русских или английских букв, пробелов или дефисов, то есть можно использовать `"..., почему-то и зачем-то , ..."` и `почему-то и зачем-то` будет воспринят как один вариант. Также варианты можно модифицировать, добавив один или несколько модификаторов:
+ - `!` отключает использование слова. Этим модификатором можно отключать слова из списка по умолчанию (см `!грубый` в примере выше).
+ - `%` помечает слово как редкое. Шанс его использования понижается в 2 раза.
+ - `^` помечает слово как частое. Шанс его использования повышается в 2 раза.
+ - `$` помечает слово как грубое. Такие слова не будут отображаться, если в настройках отключена соответсвующая настройка `rude`.
+
+Полный список слов по умолчанию вы можете посмотреть [в Google Таблице](https://docs.google.com/spreadsheets/d/1MJIfENqy7a9DczRj38c34t27ZQtEmBSi/edit?usp=sharing&ouid=112152128398997742033&rtpof=true&sd=true) или [в исходном файле](./src/data/stupid_title_variants.ts).
+
+---
+
## Разработка дополнений
Использование для тестирования:
+
```html
```
diff --git a/dist/april-fools.js b/dist/april-fools.js
index d806ee1..9de4c59 100644
--- a/dist/april-fools.js
+++ b/dist/april-fools.js
@@ -16,7 +16,7 @@
\******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n// Module\nvar code = \"\";\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (code);\n\n//# sourceURL=webpack://mybb-april-fools/./src/html/button.html?");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n// Module\nvar code = \"\\n\";\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (code);\n\n//# sourceURL=webpack://mybb-april-fools/./src/html/button.html?");
/***/ }),
@@ -26,7 +26,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n// Module\nvar code = \"\";\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (code);\n\n//# sourceURL=webpack://mybb-april-fools/./src/html/styles.html?");
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n// Module\nvar code = \"\\n\";\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (code);\n\n//# sourceURL=webpack://mybb-april-fools/./src/html/styles.html?");
/***/ }),
@@ -36,7 +36,27 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
\******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
-eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n// Module\nvar code = \"
=c-80&&(O[t-1]=c-80),j[t-1]>=s-80&&(j[t-1]=s-80)}(),function(){n.clearRect(0,0,s,c);const t=new Date,e=t.getSeconds(),i=Math.PI*(e-15)/30,o=t.getMinutes(),r=Math.PI*(o-15)/30,a=t.getHours(),l=Math.PI*(a-3)/6+Math.PI*parseInt(t.getMinutes())/360;for(let t=0;t{I.matches?U():W()},W(),{destroy:U}},e.emojiCursor=function(t){const e=t&&t.emoji||["😀","😂","😆","😊"];let n=t&&t.element,i=n||document.body,o=window.innerWidth,r=window.innerHeight;const s={x:o/2,y:o/2},c={x:o/2,y:o/2};let a=0;const l=[],h=[];let u,d,p;const f=window.matchMedia("(prefers-reduced-motion: reduce)");function g(){if(f.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;u=document.createElement("canvas"),d=u.getContext("2d"),u.style.top="0px",u.style.left="0px",u.style.pointerEvents="none",n?(u.style.position="absolute",i.appendChild(u),u.width=i.clientWidth,u.height=i.clientHeight):(u.style.position="fixed",document.body.appendChild(u),u.width=o,u.height=r),d.font="21px serif",d.textBaseline="middle",d.textAlign="center",e.forEach((t=>{let e=d.measureText(t),n=document.createElement("canvas"),i=n.getContext("2d");n.width=e.width,n.height=2*e.actualBoundingBoxAscent,i.textAlign="center",i.font="21px serif",i.textBaseline="middle",i.fillText(t,n.width/2,e.actualBoundingBoxAscent),h.push(n)})),i.addEventListener("mousemove",m,{passive:!0}),i.addEventListener("touchmove",A,{passive:!0}),i.addEventListener("touchstart",A,{passive:!0}),window.addEventListener("resize",y),v()}function y(t){o=window.innerWidth,r=window.innerHeight,n?(u.width=i.clientWidth,u.height=i.clientHeight):(u.width=o,u.height=r)}function A(t){if(t.touches.length>0)for(let e=0;e{if(n){const e=i.getBoundingClientRect();s.x=t.clientX-e.left,s.y=t.clientY-e.top}else s.x=t.clientX,s.y=t.clientY;Math.hypot(s.x-c.x,s.y-c.y)>1&&(w(s.x,s.y,h[Math.floor(Math.random()*e.length)]),c.x=s.x,c.y=s.y,a=t.timeStamp)}))}function w(t,e,n){l.push(new x(t,e,n))}function v(){!function(){if(0!=l.length){d.clearRect(0,0,o,r);for(let t=0;t=0;t--)l[t].lifeSpan<0&&l.splice(t,1);0==l.length&&d.clearRect(0,0,o,r)}}(),p=requestAnimationFrame(v)}function _(){u.remove(),cancelAnimationFrame(p),i.removeEventListener("mousemove",m),i.removeEventListener("touchmove",A),i.removeEventListener("touchstart",A),window.addEventListener("resize",y)}function x(t,e,n){const i=Math.floor(60*Math.random()+80);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:.4*Math.random()+.8},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.y+=.05;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.drawImage(this.canv,this.position.x-this.canv.width/2*e,this.position.y-this.canv.height/2,this.canv.width*e,this.canv.height*e)}}return f.onchange=()=>{f.matches?_():g()},g(),{destroy:_}},e.fairyDustCursor=function(t){let e=t&&t.colors||["#D61C59","#E7D84B","#1B8798"],n=t&&t.element,i=n||document.body,o=window.innerWidth,r=window.innerHeight;const s={x:o/2,y:o/2},c={x:o/2,y:o/2},a=[],l=[];let h,u,d;const p=window.matchMedia("(prefers-reduced-motion: reduce)");function f(){if(p.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;h=document.createElement("canvas"),u=h.getContext("2d"),h.style.top="0px",h.style.left="0px",h.style.pointerEvents="none",n?(h.style.position="absolute",i.appendChild(h),h.width=i.clientWidth,h.height=i.clientHeight):(h.style.position="fixed",i.appendChild(h),h.width=o,h.height=r),u.font="21px serif",u.textBaseline="middle",u.textAlign="center",e.forEach((t=>{let e=u.measureText("*"),n=document.createElement("canvas"),i=n.getContext("2d");n.width=e.width,n.height=e.actualBoundingBoxAscent+e.actualBoundingBoxDescent,i.fillStyle=t,i.textAlign="center",i.font="21px serif",i.textBaseline="middle",i.fillText("*",n.width/2,e.actualBoundingBoxAscent),l.push(n)})),i.addEventListener("mousemove",A),i.addEventListener("touchmove",y,{passive:!0}),i.addEventListener("touchstart",y,{passive:!0}),window.addEventListener("resize",g),w()}function g(t){o=window.innerWidth,r=window.innerHeight,n?(h.width=i.clientWidth,h.height=i.clientHeight):(h.width=o,h.height=r)}function y(t){if(t.touches.length>0)for(let e=0;e{if(n){const e=i.getBoundingClientRect();s.x=t.clientX-e.left,s.y=t.clientY-e.top}else s.x=t.clientX,s.y=t.clientY;Math.hypot(s.x-c.x,s.y-c.y)>1.5&&(m(s.x,s.y,l[Math.floor(Math.random()*e.length)]),c.x=s.x,c.y=s.y)}))}function m(t,e,n){a.push(new _(t,e,n))}function w(){!function(){if(0!=a.length){u.clearRect(0,0,o,r);for(let t=0;t=0;t--)a[t].lifeSpan<0&&a.splice(t,1);0==a.length&&u.clearRect(0,0,o,r)}}(),d=requestAnimationFrame(w)}function v(){h.remove(),cancelAnimationFrame(d),i.removeEventListener("mousemove",A),i.removeEventListener("touchmove",y),i.removeEventListener("touchstart",y),window.addEventListener("resize",g)}function _(t,e,n){const i=Math.floor(30*Math.random()+60);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:.7*Math.random()+.9},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.y+=.02;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.drawImage(this.canv,this.position.x-this.canv.width/2*e,this.position.y-this.canv.height/2,this.canv.width*e,this.canv.height*e)}}return p.onchange=()=>{p.matches?v():f()},f(),{destroy:v}},e.followingDotCursor=function(t){let e,n,i=t&&t.element,o=i||document.body,r=window.innerWidth,s=window.innerHeight,c={x:r/2,y:r/2},a=new function(t,e,n,i){this.position={x:t,y:e},this.width=10,this.lag=10,this.moveTowards=function(t,e,n){this.position.x+=(t-this.position.x)/this.lag,this.position.y+=(e-this.position.y)/this.lag,n.fillStyle=l,n.beginPath(),n.arc(this.position.x,this.position.y,this.width,0,2*Math.PI),n.fill(),n.closePath()}}(r/2,s/2,10,10),l=t?.color||"#323232a6";const h=window.matchMedia("(prefers-reduced-motion: reduce)");function u(){if(h.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",i?(e.style.position="absolute",o.appendChild(e),e.width=o.clientWidth,e.height=o.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=r,e.height=s),o.addEventListener("mousemove",p),window.addEventListener("resize",d),f()}function d(t){r=window.innerWidth,s=window.innerHeight,i?(e.width=o.clientWidth,e.height=o.clientHeight):(e.width=r,e.height=s)}function p(t){if(i){const e=o.getBoundingClientRect();c.x=t.clientX-e.left,c.y=t.clientY-e.top}else c.x=t.clientX,c.y=t.clientY}function f(){n.clearRect(0,0,r,s),a.moveTowards(c.x,c.y,n),requestAnimationFrame(f)}function g(){e.remove(),cancelAnimationFrame(f),o.removeEventListener("mousemove",p),window.addEventListener("resize",d)}return h.onchange=()=>{h.matches?g():u()},u(),{destroy:g}},e.ghostCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[],h=new Image;h.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";const u=window.matchMedia("(prefers-reduced-motion: reduce)");function d(){if(u.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",g),r.addEventListener("touchmove",f,{passive:!0}),r.addEventListener("touchstart",f,{passive:!0}),window.addEventListener("resize",p),A()}function p(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function f(t){if(t.touches.length>0)for(let e=0;e=0;t--)l[t].lifeSpan<0&&l.splice(t,1);0==l.length&&n.clearRect(0,0,s,c)}}(),i=requestAnimationFrame(A)}function m(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",g),r.removeEventListener("touchmove",f),r.removeEventListener("touchstart",f),window.addEventListener("resize",p)}function w(t,e,n){this.initialLifeSpan=40,this.lifeSpan=40,this.position={x:t,y:e},this.image=n,this.update=function(t){this.lifeSpan--;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.globalAlpha=e,t.drawImage(this.image,this.position.x,this.position.y)}}return u.onchange=()=>{u.matches?m():d()},d(),{destroy:m}},e.rainbowCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[];const h=t?.length||20,u=t?.colors||["#FE0000","#FD8C00","#FFE500","#119F0B","#0644B3","#C22EDC"],d=t?.size||3;let p=!1;const f=window.matchMedia("(prefers-reduced-motion: reduce)");function g(){if(f.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",A),window.addEventListener("resize",y),m()}function y(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function A(t){if(o){const e=r.getBoundingClientRect();a.x=t.clientX-e.left,a.y=t.clientY-e.top}else a.x=t.clientX,a.y=t.clientY;if(!1===p){p=!0;for(let t=0;t{n.beginPath(),n.strokeStyle=e,t.length&&n.moveTo(t[0].x,t[0].y+i*(d-1)),t.forEach(((t,e)=>{0!==e&&n.lineTo(t.x,t.y+i*d)})),n.lineWidth=d,n.lineCap="round",n.stroke()}))}(),i=requestAnimationFrame(m)}function w(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",A),window.addEventListener("resize",y)}function v(t,e){this.position={x:t,y:e}}return f.onchange=()=>{f.matches?w():g()},g(),{destroy:w}},e.snowflakeCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=["❄️"],c=window.innerWidth,a=window.innerHeight,l={x:c/2,y:c/2},h=[],u=[];const d=window.matchMedia("(prefers-reduced-motion: reduce)");function p(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=c,e.height=a),n.font="12px serif",n.textBaseline="middle",n.textAlign="center",s.forEach((t=>{let e=n.measureText(t),i=document.createElement("canvas"),o=i.getContext("2d");i.width=e.width,i.height=2*e.actualBoundingBoxAscent,o.textAlign="center",o.font="12px serif",o.textBaseline="middle",o.fillText(t,i.width/2,e.actualBoundingBoxAscent),u.push(i)})),r.addEventListener("mousemove",y),r.addEventListener("touchmove",g,{passive:!0}),r.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",f),m()}function f(t){c=window.innerWidth,a=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=c,e.height=a)}function g(t){if(t.touches.length>0)for(let e=0;e=0;t--)h[t].lifeSpan<0&&h.splice(t,1);0==h.length&&n.clearRect(0,0,c,a)}}(),i=requestAnimationFrame(m)}function w(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",y),r.removeEventListener("touchmove",g),r.removeEventListener("touchstart",g),window.addEventListener("resize",f)}function v(t,e,n){const i=Math.floor(60*Math.random()+80);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:1+Math.random()},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.x+=2*(Math.random()<.5?-1:1)/75,this.velocity.y-=Math.random()/300;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0),n=2*this.lifeSpan*.0174533;t.translate(this.position.x,this.position.y),t.rotate(n),t.drawImage(this.canv,-this.canv.width/2*e,-this.canv.height/2,this.canv.width*e,this.canv.height*e),t.rotate(-n),t.translate(-this.position.x,-this.position.y)}}return d.onchange=()=>{d.matches?w():p()},p(),{destroy:w}},e.springyEmojiCursor=function(t){let e,n,i,o,r=t&&t.emoji||"🤪",s=t&&t.element,c=s||document.body,a=window.innerWidth,l=window.innerHeight,h={x:a/2,y:a/2},u=[];const d=window.matchMedia("(prefers-reduced-motion: reduce)");function p(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",s?(e.style.position="absolute",c.appendChild(e),e.width=c.clientWidth,e.height=c.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=a,e.height=l),n.font="16px serif",n.textBaseline="middle",n.textAlign="center";let t=n.measureText(r),i=document.createElement("canvas"),h=i.getContext("2d");i.width=t.width,i.height=2*t.actualBoundingBoxAscent,h.textAlign="center",h.font="16px serif",h.textBaseline="middle",h.fillText(r,i.width/2,t.actualBoundingBoxAscent),o=i;let p=0;for(p=0;p<7;p++)u[p]=new _(o);c.addEventListener("mousemove",y),c.addEventListener("touchmove",g,{passive:!0}),c.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",f),A()}function f(t){a=window.innerWidth,l=window.innerHeight,s?(e.width=c.clientWidth,e.height=c.clientHeight):(e.width=a,e.height=l)}function g(t){if(t.touches.length>0)if(s){const e=c.getBoundingClientRect();h.x=t.touches[0].clientX-e.left,h.y=t.touches[0].clientY-e.top}else h.x=t.touches[0].clientX,h.y=t.touches[0].clientY}function y(t){if(s){const e=c.getBoundingClientRect();h.x=t.clientX-e.left,h.y=t.clientY-e.top}else h.x=t.clientX,h.y=t.clientY}function A(){!function(){e.width=e.width,u[0].position.x=h.x,u[0].position.y=h.y;for(let t=1;t<7;t++){let i=new w(0,0);t>0&&v(t-1,t,i),t<6&&v(t+1,t,i);let o,r,s=new w(10*-u[t].velocity.x,10*-u[t].velocity.y),c=new w((i.X+s.X)/1,(i.Y+s.Y)/1+50);u[t].velocity.x+=.01*c.X,u[t].velocity.y+=.01*c.Y,Math.abs(u[t].velocity.x)<.1&&Math.abs(u[t].velocity.y)<.1&&Math.abs(c.X)<.1&&Math.abs(c.Y)<.1&&(u[t].velocity.x=0,u[t].velocity.y=0),u[t].position.x+=u[t].velocity.x,u[t].position.y+=u[t].velocity.y,o=e.clientHeight,r=e.clientWidth,u[t].position.y>=o-11-1&&(u[t].velocity.y>0&&(u[t].velocity.y=.7*-u[t].velocity.y),u[t].position.y=o-11-1),u[t].position.x>=r-11&&(u[t].velocity.x>0&&(u[t].velocity.x=.7*-u[t].velocity.x),u[t].position.x=r-11-1),u[t].position.x<0&&(u[t].velocity.x<0&&(u[t].velocity.x=.7*-u[t].velocity.x),u[t].position.x=0),u[t].draw(n)}}(),i=requestAnimationFrame(A)}function m(){e.remove(),cancelAnimationFrame(i),c.removeEventListener("mousemove",y),c.removeEventListener("touchmove",g),c.removeEventListener("touchstart",g),window.addEventListener("resize",f)}function w(t,e){this.X=t,this.Y=e}function v(t,e,n){let i=u[t].position.x-u[e].position.x,o=u[t].position.y-u[e].position.y,r=Math.sqrt(i*i+o*o);if(r>10){let t=10*(r-10);n.X+=i/r*t,n.Y+=o/r*t}}function _(t){this.position={x:h.x,y:h.y},this.velocity={x:0,y:0},this.canv=t,this.draw=function(t){t.drawImage(this.canv,this.position.x-this.canv.width/2,this.position.y-this.canv.height/2,this.canv.width,this.canv.height)}}return d.onchange=()=>{d.matches?m():p()},p(),{destroy:m}},e.textFlag=function(t){let e,n,i,o=t||{},r=t&&t.element,s=r||document.body,c=o.text?" "+t.text:" Your Text Here",a=o.font||"monospace",l=o.textSize||12,h=l+"px "+a,u=o.gap||l+2,d=0,p=[],f=window.innerWidth,g=window.innerHeight,y={x:f/2,y:f/2};for(let t=0;t0;t--)p[t].x=p[t-1].x+u,p[t].y=p[t-1].y,n.font=h,n.fillText(p[t].letter,p[t].x,p[t].y);let i=p[0].x,o=p[0].y;i+=(y.x-i)/5+t+2,o+=(y.y-o)/5+e,p[0].x=i,p[0].y=o}(),i=requestAnimationFrame(_)}function x(){e.remove(),cancelAnimationFrame(i),s.removeEventListener("mousemove",v),window.addEventListener("resize",w)}return A.onchange=()=>{A.matches?x():m()},m(),{destroy:x}},e.trailingCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[];const h=t?.particles||15,u=t?.rate||.4,d=t?.baseImageSrc||"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";let p=!1,f=new Image;f.src=d;const g=window.matchMedia("(prefers-reduced-motion: reduce)");function y(){if(g.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",m),window.addEventListener("resize",A),w()}function A(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function m(t){if(o){const e=r.getBoundingClientRect();a.x=t.clientX-e.left,a.y=t.clientY-e.top}else a.x=t.clientX,a.y=t.clientY;if(!1===p){p=!0;for(let t=0;t{g.matches?v():y()},y(),{destroy:v}}}},__webpack_module_cache__={};function __webpack_require__(t){var e=__webpack_module_cache__[t];if(void 0!==e)return e.exports;var n=__webpack_module_cache__[t]={exports:{}};return __webpack_modules__[t](n,n.exports,__webpack_require__),n.exports}__webpack_require__.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return __webpack_require__.d(e,{a:e}),e},__webpack_require__.d=(t,e)=>{for(var n in e)__webpack_require__.o(e,n)&&!__webpack_require__.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},__webpack_require__.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var __webpack_exports__={};(()=>{var t,e,n=(t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)},function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}),i=function(){function t(){}return t.prototype.check=function(t){return Math.random()<.01*t},t.prototype.enable=function(){this.settings.enabled=!0},t.prototype.disable=function(){this.settings.enabled=!1},t.prototype.toggle=function(t){void 0===t&&(t=null),this.settings.enabled=null===t?!this.settings.enabled:t},t}(),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e}(i),r=__webpack_require__(562),s=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),c=function(){function t(){this.ui=new r.d(this),this.core_settings=new a,this.jokes={}}return t.prototype.check=function(t){return Math.random()<.01*t},t.prototype.add=function(t){this.jokes[t.id]=t},t.prototype.addCustom=function(t,e,n){void 0===n&&(n=null);var o=new(function(n){function i(){var e=null!==n&&n.apply(this,arguments)||this;return e.id=t,e.title="Пользовательская шутка",e.description="Для шутки не определено описание",e.settings={chance:10,enabled:!0},e}return s(i,n),i.prototype.start=function(){e.call(this,this.settings)},i}(i));return Object.assign(o.settings,null!=n?n:{}),this.add(o),o},Object.defineProperty(t.prototype,"settings",{get:function(){var t={};t.core=this.core_settings;for(var e=0,n=Object.entries(this.jokes);e=0)return!0;var t=new Date,e=t.getMonth()+1,n=t.getDate();return 4===e&&1===n&&this.check(this.core_settings.chance)},t.prototype.regularStart=function(){this.regularCheck()&&(this.ui.show(),this.startAll())},t}(),a=function(){this.chance=100,this.enabled=!0,this.testers=[]},l=__webpack_require__(311),h=__webpack_require__.n(l),u=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),d=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="shake",e.title="Форумо-трясение",e.description="Страница форума начинает трястись",e.settings=new p,e.data_name="april-fools-shake",e}return u(e,t),e.prototype.rand=function(t){return t+this.settings.force*(2*Math.random()-1)},e.prototype.start=function(){var t=this;this.items=h()(this.settings.selector),this.items.each((function(e,n){var i={};t.settings.directions.forEach((function(t){i[t]=parseInt(h()(n).css("margin-"+t))})),h()(n).data(t.data_name,i)})),this.interval_id=setInterval((function(){t.items.each((function(e,n){for(var i=h()(n).data(t.data_name),o=0,r=Object.entries(i);o0?"тся":"ться"})))}))},e}(i),y=function(){this.enabled=!0,this.chance=20,this.selector=".post-content"},A=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),m=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="mirror",e.title="Зеркало",e.description="Элементы форума отражаются (по крайней мере, пытаются)",e.settings=new w,e}return A(e,t),e.prototype.start=function(){h()(".post, .post h3, .post-links, .post-author, .post-content").css("transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("filter","FlipH").css("-ms-filter",'"FlipH"'),h()(".post-links, .post-links ul, .post h3 span").css("margin-left","0"),h()(".post h3 span").css("margin-right","25em")},e}(i),w=function(){this.enabled=!1,this.chance=30},v=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),_=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="carnival",e.title="Карнавал",e.description="Аватарки приобретают интересные цвета",e.settings=new x,e}return v(e,t),e.prototype.start=function(){var t=this;h()(this.settings.selector).each((function(e,n){for(var i=[],o=0,r=Object.entries(t.settings.variants);o0&&h()(n).css({filter:i.join(" ")})}))},e}(i),x=function(){this.enabled=!0,this.chance=30,this.selector='img[src*="/avatars/"]',this.variant_chance=20,this.variants={"sepia({0}%)":100,"saturate({0}%)":100,"hue-rotate({0}deg)":360,"invert({0}%)":100}},b=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),E=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="crazy_letters",e.title="Мама, Я сОшЛа С уМа!",e.description="иМеНа ЗаБоРчИкОм",e.settings=new C,e}return b(e,t),e.prototype.start=function(){var t=this;h()(this.settings.selector).each((function(e,n){for(var i=h()(t).text(),o="",r=0;r0;o--){var r=Math.floor(Math.random()*(o+1));e=[i[r],i[o]],i[o]=e[0],i[r]=e[1]}return O(O([n[0]],i,!0),[n[n.length-1]],!1).join("")},e.prototype.processNode=function(t){var e;t.textContent=null===(e=t.textContent)||void 0===e?void 0:e.replace(/([А-Яа-яA-Za-z]+)/gm,this.shuffleLetters)},e.prototype.start=function(){var t=this;h()(this.settings.selector).find("*").contents().filter((function(t,e){return e.nodeType===Node.TEXT_NODE})).each((function(e,n){return t.processNode(n)}))},e}(i),L=function(){this.enabled=!0,this.chance=20,this.selector=".post-content",this.shuffle_chance=50},k=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),B=__webpack_require__(442),R=function(t){function n(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="cursor_effects",e.title="Красивый курсор",e.description="Эффекты, следующие за курсором",e.settings=new T,e}return k(n,t),n.prototype.enableAll=function(){for(var t=0,e=Object.keys(this.settings.effects);t0&&o[o.length-1])||6!==c[0]&&2!==c[0])){s=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]>>16,2246822507)^Math.imul(o^o>>>13,3266489909);var c=4294967296*(2097151&(o=Math.imul(o^o>>>16,2246822507)^Math.imul(i^i>>>13,3266489909)))+(i>>>0);return Math.floor(Math.pow(Math.sin(c),2)*n)%n},e.prototype.resetSeed=function(){new Date;var t=60*(new Date).getUTCHours()+(new Date).getUTCMinutes();this.seed=Math.floor(t/this.settings.change_frequency),console.log("Seed reset:",t,this.seed)},e.prototype.selectRandom=function(t,e){for(var n=Object.keys(e),i=n.reduce((function(t,n){return t+e[n]}),0),o=this.prng(t,this.seed++,i),r=0,s=0,c=n;s=0,enabled:e.indexOf("!")<0,weight:10*(e.indexOf("$")>=0?.5:1)*(e.indexOf("%")>=0?.5:1)*(e.indexOf("^")>=0?2:1)}))})),n}window.BuildStupdiTitleVariant=q,window.AprilJokes=(S=[d,g,m,_,E,j,R,F],D=new c,S.forEach((function(t){D.add(new t)})),$((function(){D.regularStart()})),D)})()})();
\ No newline at end of file
+(()=>{"use strict";var __webpack_modules__={525:(e,t,i)=>{i.d(t,{Z:()=>n});const n=' '},554:(e,t,i)=>{i.d(t,{Z:()=>n});const n=' '},95:(e,t,i)=>{i.d(t,{Z:()=>n});const n='
'},562:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{d:()=>JokerUI});var jquery__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(311),jquery__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__),_html_button_html__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(525),_html_styles_html__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(554),_html_window_html__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(95);class JokerUI{constructor(e){this.joker=e,jquery__WEBPACK_IMPORTED_MODULE_0___default()('link[rel="stylesheet"]').eq(0).before(_html_styles_html__WEBPACK_IMPORTED_MODULE_1__.Z);e=this.button;e.hide(),e.mybbModal({title:"Настройки первоапрельских шуток",content:()=>this.render(),closer:!0,noscroll:!0,escClose:!0})}get button(){let e=jquery__WEBPACK_IMPORTED_MODULE_0___default()(".april-fools__button");return e=0===e.length?jquery__WEBPACK_IMPORTED_MODULE_0___default()(_html_button_html__WEBPACK_IMPORTED_MODULE_2__.Z).appendTo("body"):e}show(){this.button.show()}render(){const result=jquery__WEBPACK_IMPORTED_MODULE_0___default()("").html(_html_window_html__WEBPACK_IMPORTED_MODULE_3__.Z),row_template=jquery__WEBPACK_IMPORTED_MODULE_0___default()("#april-fools__window-row",result).html(),joke_container=jquery__WEBPACK_IMPORTED_MODULE_0___default()(".april-fools__jokes",result);for(const joke of Object.values(this.joker.jokes)){const row=jquery__WEBPACK_IMPORTED_MODULE_0___default()(eval("`"+row_template+"`"));jquery__WEBPACK_IMPORTED_MODULE_0___default()('input[type="checkbox"]',row).prop("checked",joke.settings.enabled).on("click",e=>this.joker.toggleJoke(joke.id,e.currentTarget.checked)),joke_container.append(row)}return jquery__WEBPACK_IMPORTED_MODULE_0___default()("button",result).on("click",async()=>{await this.joker.clearUserStates(),window.location.reload()}),jquery__WEBPACK_IMPORTED_MODULE_0___default()(".april-fools__content",result)}}},311:e=>{e.exports=jQuery},442:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.bubbleCursor=function(e){let t,i,n,o=e&&e.element,s=o||document.body,r=window.innerWidth,a=window.innerHeight,l={x:r/2,y:r/2},c=[],h=[];const d=window.matchMedia("(prefers-reduced-motion: reduce)");function u(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),i=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",o?(t.style.position="absolute",s.appendChild(t),t.width=s.clientWidth,t.height=s.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=r,t.height=a),s.addEventListener("mousemove",f),s.addEventListener("touchmove",g,{passive:!0}),s.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",p),m()}function p(e){r=window.innerWidth,a=window.innerHeight,o?(t.width=s.clientWidth,t.height=s.clientHeight):(t.width=r,t.height=a)}function g(t){if(0{(d.matches?w:u)()},u(),{destroy:w}},t.clockCursor=function(e){let t,s,r,i=e&&e.element,n=i||document.body,a=window.innerWidth,l=window.innerHeight,c={x:a/2,y:a/2};const W=e&&e.dateColor||"blue",T=e&&e.faceColor||"black",Y=e&&e.secondsColor||"red",H=e&&e.minutesColor||"black",P=e&&e.hoursColor||"black",h=.4;var e=new Date,F=e.getDate(),U=e.getYear()+1900;const o=(" "+["SUNDAY","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY"][e.getDay()]+" "+F+" "+["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"][e.getMonth()]+" "+U).split(""),d=["3","4","5","6","7","8","9","10","11","12","1","2"],u=d.length,p=["•","•","•"],g=["•","•","•","•"],f=["•","•","•","•","•"],A=360/u,m=360/o.length,w=45/6.5,_=[],v=[],y=[],x=[],E=[],b=[],M=[],C=[],k=[];var j=parseInt(o.length+u+p.length+g.length+f.length)+1;const L=window.matchMedia("(prefers-reduced-motion: reduce)");function B(){if(L.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),s=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",i?(t.style.position="absolute",n.appendChild(t),t.width=n.clientWidth,t.height=n.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=a,t.height=l),s.font="10px sans-serif",s.textAlign="center",s.textBaseline="middle";for(let e=0;e=l-80&&(_[e-1]=l-80),v[e-1]>=a-80&&(v[e-1]=a-80);s.clearRect(0,0,a,l);var e=new Date,t=e.getSeconds(),i=Math.PI*(t-15)/30,t=e.getMinutes(),n=Math.PI*(t-15)/30,t=e.getHours(),o=Math.PI*(t-3)/6+Math.PI*parseInt(e.getMinutes())/360;for(let e=0;e{(L.matches?O:B)()},B(),{destroy:O}},t.emojiCursor=function(e){const t=e&&e.emoji||["😀","😂","😆","😊"];let i=e&&e.element,n=i||document.body,o=window.innerWidth,s=window.innerHeight;const r={x:o/2,y:o/2},a={x:o/2,y:o/2};let l=0;const c=[],h=[];let d,u,p;const g=window.matchMedia("(prefers-reduced-motion: reduce)");function f(){if(g.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");d=document.createElement("canvas"),u=d.getContext("2d"),d.style.top="0px",d.style.left="0px",d.style.pointerEvents="none",i?(d.style.position="absolute",n.appendChild(d),d.width=n.clientWidth,d.height=n.clientHeight):(d.style.position="fixed",document.body.appendChild(d),d.width=o,d.height=s),u.font="21px serif",u.textBaseline="middle",u.textAlign="center",t.forEach(e=>{var t=u.measureText(e),i=document.createElement("canvas"),n=i.getContext("2d");i.width=t.width,i.height=2*t.actualBoundingBoxAscent,n.textAlign="center",n.font="21px serif",n.textBaseline="middle",n.fillText(e,i.width/2,t.actualBoundingBoxAscent),h.push(i)}),n.addEventListener("mousemove",w,{passive:!0}),n.addEventListener("touchmove",m,{passive:!0}),n.addEventListener("touchstart",m,{passive:!0}),window.addEventListener("resize",A),v()}function A(e){o=window.innerWidth,s=window.innerHeight,i?(d.width=n.clientWidth,d.height=n.clientHeight):(d.width=o,d.height=s)}function m(t){if(0{if(i){const t=n.getBoundingClientRect();r.x=e.clientX-t.left,r.y=e.clientY-t.top}else r.x=e.clientX,r.y=e.clientY;1{(g.matches?y:f)()},f(),{destroy:y}},t.fairyDustCursor=function(e){let t=e&&e.colors||["#D61C59","#E7D84B","#1B8798"],i=e&&e.element,n=i||document.body,o=window.innerWidth,s=window.innerHeight;const r={x:o/2,y:o/2},a={x:o/2,y:o/2},l=[],c=[];let h,d,u;const p=window.matchMedia("(prefers-reduced-motion: reduce)");function g(){if(p.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");h=document.createElement("canvas"),d=h.getContext("2d"),h.style.top="0px",h.style.left="0px",h.style.pointerEvents="none",i?(h.style.position="absolute",n.appendChild(h),h.width=n.clientWidth,h.height=n.clientHeight):(h.style.position="fixed",n.appendChild(h),h.width=o,h.height=s),d.font="21px serif",d.textBaseline="middle",d.textAlign="center",t.forEach(e=>{var t=d.measureText("*"),i=document.createElement("canvas"),n=i.getContext("2d");i.width=t.width,i.height=t.actualBoundingBoxAscent+t.actualBoundingBoxDescent,n.fillStyle=e,n.textAlign="center",n.font="21px serif",n.textBaseline="middle",n.fillText("*",i.width/2,t.actualBoundingBoxAscent),c.push(i)}),n.addEventListener("mousemove",m),n.addEventListener("touchmove",A,{passive:!0}),n.addEventListener("touchstart",A,{passive:!0}),window.addEventListener("resize",f),_()}function f(e){o=window.innerWidth,s=window.innerHeight,i?(h.width=n.clientWidth,h.height=n.clientHeight):(h.width=o,h.height=s)}function A(t){if(0{if(i){const t=n.getBoundingClientRect();r.x=e.clientX-t.left,r.y=e.clientY-t.top}else r.x=e.clientX,r.y=e.clientY;1.5{(p.matches?v:g)()},g(),{destroy:v}},t.followingDotCursor=function(e){let t,i,n=e&&e.element,o=n||document.body,s=window.innerWidth,r=window.innerHeight,a={x:s/2,y:s/2},l=new function(e,t,i,n){this.position={x:e,y:t},this.width=10,this.lag=10,this.moveTowards=function(e,t,i){this.position.x+=(e-this.position.x)/this.lag,this.position.y+=(t-this.position.y)/this.lag,i.fillStyle=c,i.beginPath(),i.arc(this.position.x,this.position.y,this.width,0,2*Math.PI),i.fill(),i.closePath()}}(s/2,r/2),c=e?.color||"#323232a6";const h=window.matchMedia("(prefers-reduced-motion: reduce)");function d(){if(h.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),i=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",n?(t.style.position="absolute",o.appendChild(t),t.width=o.clientWidth,t.height=o.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=s,t.height=r),o.addEventListener("mousemove",p),window.addEventListener("resize",u),g()}function u(e){s=window.innerWidth,r=window.innerHeight,n?(t.width=o.clientWidth,t.height=o.clientHeight):(t.width=s,t.height=r)}function p(e){var t;n?(t=o.getBoundingClientRect(),a.x=e.clientX-t.left,a.y=e.clientY-t.top):(a.x=e.clientX,a.y=e.clientY)}function g(){i.clearRect(0,0,s,r),l.moveTowards(a.x,a.y,i),requestAnimationFrame(g)}function f(){t.remove(),cancelAnimationFrame(g),o.removeEventListener("mousemove",p),window.addEventListener("resize",u)}return h.onchange=()=>{(h.matches?f:d)()},d(),{destroy:f}},t.ghostCursor=function(e){let t,i,n,o=e&&e.element,s=o||document.body,r=window.innerWidth,a=window.innerHeight,l={x:r/2,y:r/2},c=[],h=new Image;h.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";const d=window.matchMedia("(prefers-reduced-motion: reduce)");function u(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),i=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",o?(t.style.position="absolute",s.appendChild(t),t.width=s.clientWidth,t.height=s.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=r,t.height=a),s.addEventListener("mousemove",f),s.addEventListener("touchmove",g,{passive:!0}),s.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",p),m()}function p(e){r=window.innerWidth,a=window.innerHeight,o?(t.width=s.clientWidth,t.height=s.clientHeight):(t.width=r,t.height=a)}function g(t){if(0{(d.matches?w:u)()},u(),{destroy:w}},t.rainbowCursor=function(e){let t,r,i,n=e&&e.element,o=n||document.body,a=window.innerWidth,l=window.innerHeight,c={x:a/2,y:a/2},h=[];const s=e?.length||20,d=e?.colors||["#FE0000","#FD8C00","#FFE500","#119F0B","#0644B3","#C22EDC"],u=e?.size||3;let p=!1;const g=window.matchMedia("(prefers-reduced-motion: reduce)");function f(){if(g.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),r=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",n?(t.style.position="absolute",o.appendChild(t),t.width=o.clientWidth,t.height=o.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=a,t.height=l),o.addEventListener("mousemove",m),window.addEventListener("resize",A),w()}function A(e){a=window.innerWidth,l=window.innerHeight,n?(t.width=o.clientWidth,t.height=o.clientHeight):(t.width=a,t.height=l)}function m(e){if(n){const t=o.getBoundingClientRect();c.x=e.clientX-t.left,c.y=e.clientY-t.top}else c.x=e.clientX,c.y=e.clientY;if(!1===p){p=!0;for(let e=0;e{r.beginPath(),r.strokeStyle=e,n.length&&r.moveTo(n[0].x,n[0].y+i*(u-1)),n.forEach((e,t)=>{0!==t&&r.lineTo(e.x,e.y+i*u)}),r.lineWidth=u,r.lineCap="round",r.stroke()})}i=requestAnimationFrame(w)}function _(){t.remove(),cancelAnimationFrame(i),o.removeEventListener("mousemove",m),window.addEventListener("resize",A)}function v(e,t){this.position={x:e,y:t}}return g.onchange=()=>{(g.matches?_:f)()},f(),{destroy:_}},t.snowflakeCursor=function(e){let t,o,i,n=e&&e.element,s=n||document.body,r=["❄️"],a=window.innerWidth,l=window.innerHeight,c={x:a/2,y:a/2},h=[],d=[];const u=window.matchMedia("(prefers-reduced-motion: reduce)");function p(){if(u.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),o=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",n?(t.style.position="absolute",s.appendChild(t),t.width=s.clientWidth,t.height=s.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=a,t.height=l),o.font="12px serif",o.textBaseline="middle",o.textAlign="center",r.forEach(e=>{var t=o.measureText(e),i=document.createElement("canvas"),n=i.getContext("2d");i.width=t.width,i.height=2*t.actualBoundingBoxAscent,n.textAlign="center",n.font="12px serif",n.textBaseline="middle",n.fillText(e,i.width/2,t.actualBoundingBoxAscent),d.push(i)}),s.addEventListener("mousemove",A),s.addEventListener("touchmove",f,{passive:!0}),s.addEventListener("touchstart",f,{passive:!0}),window.addEventListener("resize",g),w()}function g(e){a=window.innerWidth,l=window.innerHeight,n?(t.width=s.clientWidth,t.height=s.clientHeight):(t.width=a,t.height=l)}function f(t){if(0{(u.matches?_:p)()},p(),{destroy:_}},t.springyEmojiCursor=function(e){let o,s,n,r,a=e&&e.emoji||"🤪",l=e&&e.element,c=l||document.body,h=window.innerWidth,d=window.innerHeight,u={x:h/2,y:h/2},p=[];const g=window.matchMedia("(prefers-reduced-motion: reduce)");function t(){if(g.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");o=document.createElement("canvas"),s=o.getContext("2d"),o.style.top="0px",o.style.left="0px",o.style.pointerEvents="none",l?(o.style.position="absolute",c.appendChild(o),o.width=c.clientWidth,o.height=c.clientHeight):(o.style.position="fixed",document.body.appendChild(o),o.width=h,o.height=d),s.font="16px serif",s.textBaseline="middle",s.textAlign="center";var e=s.measureText(a),t=document.createElement("canvas"),i=t.getContext("2d");t.width=e.width,t.height=2*e.actualBoundingBoxAscent,i.textAlign="center",i.font="16px serif",i.textBaseline="middle",i.fillText(a,t.width/2,e.actualBoundingBoxAscent),r=t;let n=0;for(n=0;n<7;n++)p[n]=new y(r);c.addEventListener("mousemove",m),c.addEventListener("touchmove",A,{passive:!0}),c.addEventListener("touchstart",A,{passive:!0}),window.addEventListener("resize",f),w()}function f(e){h=window.innerWidth,d=window.innerHeight,l?(o.width=c.clientWidth,o.height=c.clientHeight):(o.width=h,o.height=d)}function A(e){var t;0=i-11-1&&(0
=t-11&&(0
{(g.matches?i:t)()},t(),{destroy:i}},t.textFlag=function(e){let t,o,s,i=e||{},n=e&&e.element,r=n||document.body,a=i.text?" "+e.text:" Your Text Here",l=i.font||"monospace",c=i.textSize||12,h=c+"px "+l,d=i.gap||c+2,u=0,p=[],g=window.innerWidth,f=window.innerHeight,A={x:g/2,y:g/2};for(let e=0;e{(m.matches?x:w)()},w(),{destroy:x}},t.trailingCursor=function(e){let t,s,i,o=e&&e.element,r=o||document.body,a=window.innerWidth,l=window.innerHeight,c={x:a/2,y:a/2},h=[];const d=e?.particles||15,u=e?.rate||.4,n=e?.baseImageSrc||"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";let p=!1,g=new Image;g.src=n;const f=window.matchMedia("(prefers-reduced-motion: reduce)");function A(){if(f.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init");t=document.createElement("canvas"),s=t.getContext("2d"),t.style.top="0px",t.style.left="0px",t.style.pointerEvents="none",o?(t.style.position="absolute",r.appendChild(t),t.width=r.clientWidth,t.height=r.clientHeight):(t.style.position="fixed",document.body.appendChild(t),t.width=a,t.height=l),r.addEventListener("mousemove",w),window.addEventListener("resize",m),_()}function m(e){a=window.innerWidth,l=window.innerHeight,o?(t.width=r.clientWidth,t.height=r.clientHeight):(t.width=a,t.height=l)}function w(e){if(o){const t=r.getBoundingClientRect();c.x=e.clientX-t.left,c.y=e.clientY-t.top}else c.x=e.clientX,c.y=e.clientY;if(!1===p){p=!0;for(let e=0;e{(f.matches?v:A)()},A(),{destroy:v}}}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];return void 0!==t||(t=__webpack_module_cache__[e]={exports:{}},__webpack_modules__[e](t,t.exports,__webpack_require__)),t.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},__webpack_require__.d=(e,t)=>{for(var i in t)__webpack_require__.o(t,i)&&!__webpack_require__.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var __webpack_exports__={};(()=>{class o{check(e){return Math.random()<.01*e}enable(){this.settings.enabled=!0}disable(){this.settings.enabled=!1}toggle(e=null){this.settings.enabled=null===e?!this.settings.enabled:e}}class i extends o{}var e=__webpack_require__(562);class n{constructor(){this.ui=new e.d(this),this.core_settings=new t,this.jokes={}}check(e){return Math.random()<.01*e}add(e){this.jokes[e.id]=e}addCustom(e,t,i=null){var n=new class extends o{constructor(){super(...arguments),this.id=e,this.title="Пользовательская шутка",this.description="Для шутки не определено описание",this.settings={chance:10,enabled:!0}}start(){t.call(this,this.settings)}};return Object.assign(n.settings,null!=i?i:{}),this.add(n),n}get settings(){var e={};e.core=this.core_settings;for(const n of Object.entries(this.jokes)){var t=n[0],i=n[1];e[t]=i.settings}return e}set settings(e){for(const n of Object.entries(e)){var t=n[0],i=n[1],t="core"===t?this.core_settings:this.jokes[t].settings;Object.assign(t,i)}}enable(){this.core_settings.enabled=!0}disable(){this.core_settings.enabled=!1}enableAll(){for(const e of Object.keys(this.jokes))this.jokes[e].enable()}disableAll(){for(const e of Object.keys(this.jokes))this.jokes[e].disable()}toggleJoke(e,t){e=this.jokes[e];e.settings.enabled=t,e instanceof i&&!t&&e.stop(),this.saveUserStates()}saveUserStates(){var e={};for(const t of Object.keys(this.jokes))e[t]=this.jokes[t].settings.enabled;fetch("/api.php?"+$.param({method:"storage.set",user_id:window.UserID,key:"april-fools",token:window.ForumAPITicket,value:JSON.stringify(e),timer:1440}))}async loadUserStates(){var e=await(await fetch("/api.php?"+$.param({method:"storage.get",user_id:window.UserID,key:"april-fools"}))).json();if(!e.error){var t=JSON.parse(e.response.storage.data["april-fools"]);for(const i of Object.keys(this.jokes))this.jokes[i].settings.enabled=t[i]}}async clearUserStates(){await fetch("/api.php?"+$.param({method:"storage.delete",user_id:window.UserID,key:"april-fools",token:window.ForumAPITicket}))}start(e,t=!1){e in this.jokes&&(e=this.jokes[e],t||e.settings.enabled&&this.check(e.settings.chance))&&e.start()}startAll(e=!1){for(const t of Object.keys(this.jokes))this.start(t,e)}regularCheck(){var e,t;return!!this.core_settings.enabled&&(0<=this.core_settings.testers.indexOf(window.UserID)||(e=(t=new Date).getMonth()+1,t=t.getDate(),4===e&&1===t&&this.check(this.core_settings.chance)))}async regularStart(){this.regularCheck()&&(await this.loadUserStates(),this.ui.show(),this.startAll())}}class t{constructor(){this.chance=100,this.enabled=!0,this.testers=[]}}var s,r=__webpack_require__(311),a=__webpack_require__.n(r);i;class l{constructor(){this.enabled=!1,this.chance=1,this.directions=["top","left","bottom","left"],this.force=.5,this.selector="[id^=pun]",this.interval=100}}function c(e){return a()(e).find("*").contents().filter((e,t)=>t.nodeType===Node.TEXT_NODE)}o;class h{constructor(){this.enabled=!0,this.chance=20,this.selector=".post-content"}}o;class d{constructor(){this.enabled=!1,this.chance=30}}o;class u{constructor(){this.enabled=!0,this.chance=30,this.selector='img[src*="/avatars/"]',this.variant_chance=20,this.variants={"sepia({0}%)":100,"saturate({0}%)":100,"hue-rotate({0}deg)":360,"invert({0}%)":100}}}o;class p{constructor(){this.enabled=!0,this.chance=20,this.selector=".pa-author a"}}o;class g{constructor(){this.enabled=!0,this.chance=20,this.selector=".post-content",this.shuffle_chance=50}}const f=__webpack_require__(442);i;(r=s=s||{}).Rainbow="rainbow",r.FairyDust="fairy_dust",r.SpringyEmoji="springy_emoji",r.Emoji="emoji",r.Ghost="ghost",r.Trailing="trailing",r.TextFlag="text_flag",r.FollowingDot="following_dot",r.Bubbles="bubbles",r.Snowflake="snowflake",r.Clock="clock";class A{constructor(){this.enabled=!0,this.chance=100,this.effects={[s.Rainbow]:new class extends m{constructor(){super(...arguments),this.length=20,this.size=3,this.colors=["#9400D3","#4B0082","#0000FF","#00FF00","#FFFF00","#FF7F00","#FF0000"]}},[s.SpringyEmoji]:new class extends m{constructor(){super(...arguments),this.emoji="🤪"}},[s.Emoji]:new class extends m{constructor(){super(...arguments),this.emoji=["🔥","🐬","🦆"]}},[s.FairyDust]:new class extends m{constructor(){super(...arguments),this.colors=["#ff0000","#00ff00","#0000ff"]}},[s.Ghost]:new m,[s.Trailing]:new class extends m{constructor(){super(...arguments),this.particles=15,this.rate=.4,this.baseImageSrc=null}},[s.TextFlag]:new class extends m{constructor(){super(...arguments),this.text="С днём смеха!",this.color="#000000",this.font="monospace",this.textSize=12,this.gap=14,this.size=3}},[s.FollowingDot]:new class extends m{constructor(){super(...arguments),this.color="#323232a6"}},[s.Bubbles]:new m,[s.Snowflake]:new class extends m{constructor(){super(...arguments),this.weight=0}},[s.Clock]:new class extends m{constructor(){super(...arguments),this.dateColor="blue",this.faceColor="black",this.secondsColor="red",this.minutesColor="black",this.hoursColor="black"}}}}}class m{constructor(){this.enabled=!0,this.weight=50}}o;class w{constructor(){this.enabled=!0,this.chance=100,this.gender_resolver=null,this.rude=!1,this.change_frequency=5,this.schemas={"{adj} {noun}{p}":10,"{noun} {adj}{p}":3,"{adj}, {adj} {noun}{p}":10,"{adj} и очень {adj} {noun}{p}":10,"{adj} {noun} {verb}{p}":10,"{adj} {noun} {adv} {verb}{p}":5,"{adj} {noun} {verb} {place}{p}":5,"{adj} {noun} {adv} {verb} {place}{p}":5},this.variants={adj:Object.assign(Object.assign({},v(!0,"жесткий,^удачливый,низкий,знакомый,откровенный,^мирный,грамотный,вкусный,виртуальный,пышный,провинциальный,$жирный,заинтересованный,объективный,^оригинальный,пожилой,чистый,особенный,выгодный,громадный,обыкновенный,чужой,ударный,^солнечный,истинный,посторонний,южный,седой,$бесполезный,острый,пестрый,формальный,повседневный,невозможный,$жалкий,целый,$глупый,последний,выходной,серьезный,подземный,небесный,любимый,былой,семейный,далекий,северный,суровый,гигантский,иностранный,родной,конструктивный,показательный,нужный,чрезвычайный,гордый,банальный,открытый,независимый,запасной,мстительный,грубый,алчный,мелочный,хладнокровный,спокойный,терпеливый,креативный,активный,адекватный,застенчивый,опрятный,безнадёжный,уверенный в себе,доброжелательный,ласковый,робкий,воспитанный,самовлюблённый,деятельный,двуличный,верный")),v(!1,"жесткая,^удачливая,низкая,знакомая,откровенная,^мирная,грамотная,вкусная,виртуальная,пышная,провинциальная,$жирная,заинтересованная,объективная,^оригинальная,пожилая,чистая,особенная,выгодная,громадная,обыкновенная,чужая,ударная,^солнечная,истинная,посторонняя,южная,седая,$бесполезная,острая,пестрая,формальная,повседневная,невозможная,$жалкая,целая,$глупая,последняя,выходная,серьезная,подземная,небесная,любимая,былая,семейная,далекая,северная,суровая,гигантская,иностранная,родная,конструктивная,показательная,нужная,чрезвычайная,гордая,банальная,открытая,независимая,запасная,мстительная,грубая,алчная,мелочная,хладнокровная,спокаяная,терпеливая,креативная,активная,адекватная,застенчивая,опрятная,безнадёжная,уверенная в себе,доброжелательная,ласковая,робкая,воспитанная,самовлюблённая,деятельная,двуличная,верная")),noun:Object.assign(Object.assign(Object.assign({},v(null,"")),v(!0,"орёл, ниндзя, манекен, пузырь, бродяга, рыцарь, кентавр, зайц, диван, наследник, сын, джентльмен, басурманин, карасик, $злоумышленник, $раб, хоббит, одиночка, отец, кролик, бруталище, дворф, актёр, %дед, спортсмен, $балбес, %мальчег, лев, ёж, организм, монах, хоббит, борец, тигр, монстр, массажист, мачо, эльф, ледник, жук, кот, зомби, проказник, дракон, носорог, волк, лесоруб, кит, %дедушка, динозавр, бард, кулак, принц, %сынок, мальчик, товарищ, ёжик, сурикат")),v(!1,"заноза, волторна, $личинка, детка, %бабушка, простуда, черепашка, мышь, волчица, деревяшка, пиранья, каруселька, красотка, леди, %дочка, орлица, принцесса, $ведьма, травка, актриса, наследница, %деффачка, горгулья, белочка, акула, машинка, конфетка, знаменитость, косточка, спортсменка, панда, мать, львица, эльфийка, $злоумышленница, чертовка, дворфиха, кошка, $рабыня, скамеечка, дочь, монашка, бактерия, рыбка, бусинка, $бабка, девочка, валькирия, килька, бабочка, милашка, крышка, тигрица, рыбка, русалочка")),verb:Object.assign(Object.assign(Object.assign({},v(null,"встаёт, снимает, приказывает, указывает, собирается, чувствует опасность, орёт, болеет, поздравляет, освобождает, поздравляет, бежит, берёт и делает, молчит, радуется, прощает, читает, снимает, снимается в фильме, что-то скрывает, думает, имеет план и придерживается его, тянет, работает, меняется, ловит, отказаться куда-то идти, заходит, замирает, оформляет, нарушает, отказывается что-то делать, шепчет, снимается в сериале, скрывается, учит, спрашивает, идёт, отказывается вставать, прибывает, отказывается, забывает, получает, плывёт, отвечает, поглядывает, говорит, просыпается, соединяет, оглядывается")),v(!0,"женится, женился, успел, не успел, смог, скрылся, заработался, познал дзен, присел, обиделся, проснулся, взял и сделал, додумался, не спал")),v(!1,"выходит замуж, вышла замуж, успела, не успела, смогла, скрылась, заработалась, познала дзен, присела, обиделась, проснулась, взяла и сделала, додумалась, не спала")),place:Object.assign({},v(null,"где-то, в пустыне, на пляже, под дождём, %под проливным дождём, в квартире, %в тёплой и уютной квартире, в космосе, в театре, в филармонии, на заснеженной вершине, на конференции, на форуме, в очереди, в лесу, в джунглях, под водой")),adv:Object.assign({},v(null,"счастливо, уныло, грустно, позже, немедленно, регулярно, вновь, опять, уже, внезапно, предсказуемо, медленно, быстро, %молниеносно, $грубо, безрассудно, осмысленно, расчётливо")),p:{"":new _({weight:10}),".":new _({weight:10}),"...":new _({weight:5}),"!":new _({weight:5}),"!!!":new _({weight:2}),"?":new _({weight:2}),"?!":new _({weight:1})}}}}class _{constructor(e=new Object){this.weight=10,this.enabled=!0,this.rude=!1,this.gender=null;for(const t of Object.entries(e))t[0]in this&&(this[t[0]]=t[1])}}function v(i,e){const n={};return e.split(",").map(e=>e.trim()).forEach(e=>{var t=e.replace(/[^А-ЯЁа-яёA-Za-z\- ]/g,"");""!==t&&(n[t]=new _({gender:i,rude:0<=e.indexOf("$"),enabled:e.indexOf("!")<0,weight:10*(0<=e.indexOf("$")?.5:1)*(0<=e.indexOf("%")?.5:1)*(0<=e.indexOf("^")?2:1)}))}),n}window.BuildStupdiTitleVariant=v,window.AprilJokes=(e=>{const t=new n;return e.forEach(e=>{t.add(new e)}),$(()=>t.regularStart()),t})([class extends i{constructor(){super(...arguments),this.id="shake",this.title="Форумо-трясение",this.description="Страница форума начинает трястись",this.settings=new l,this.data_name="april-fools-shake"}rand(e){return e+this.settings.force*(2*Math.random()-1)}start(){this.items=a()(this.settings.selector),this.items.each((e,t)=>{let i={};this.settings.directions.forEach(e=>{i[e]=parseInt(a()(t).css("margin-"+e))}),a()(t).data(this.data_name,i)}),this.interval_id=setInterval(()=>{this.items.each((e,t)=>{var i=a()(t).data(this.data_name);for(const n of Object.entries(i))a()(t).css("margin-"+n[0],this.rand(n[1]))})},this.settings.interval)}stop(){clearInterval(this.interval_id),this.items.each((e,t)=>{let i={};this.settings.directions.forEach(e=>{a()(t).css("margin-"+e,i[e])}),a()(t).data(this.data_name,i)})}},class extends o{constructor(){super(...arguments),this.id="humanitarian_hell",this.title="Ад гуманитария",this.description='Все "тся" заменяются на "ться", а все "ться" на "тся"',this.settings=new h}processText(e){return e.replace(/(ть?ся)/g,function(e){return 0{var i=a()(t).html();a()(t).html(this.processText(i))})}},class extends o{constructor(){super(...arguments),this.id="mirror",this.title="Зеркало",this.description="Элементы форума отражаются (по крайней мере, пытаются)",this.settings=new d}start(){a()(".post, .post h3, .post-links, .post-author, .post-content").css("transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("filter","FlipH").css("-ms-filter",'"FlipH"'),a()(".post-links, .post-links ul, .post h3 span").css("margin-left","0"),a()(".post h3 span").css("margin-right","25em")}},class extends o{constructor(){super(...arguments),this.id="carnival",this.title="Карнавал",this.description="Аватарки приобретают интересные цвета",this.settings=new u}start(){a()(this.settings.selector).each((e,t)=>{var i=[];for(const n of Object.entries(this.settings.variants))this.check(this.settings.variant_chance)&&i.push(n[0].replace("{0}",Math.round(Math.random()*n[1]).toString()));0{var i=a()(this).text();let n="";for(let e=0;ethis.processNode(t))}},class extends i{constructor(){super(...arguments),this.id="cursor_effects",this.title="Красивый курсор",this.description="Эффекты, следующие за курсором",this.settings=new A}enableAll(){for(const e of Object.keys(this.settings.effects))this.settings.effects[e].enabled=!0}disableAll(){for(const e of Object.keys(this.settings.effects))this.settings.effects[e].enabled=!1}weight(e){e=this.settings.effects[e];return e.enabled?e.weight:0}getRandomEffect(){var e=Object.values(s).map(e=>e),t=e.reduce((e,t)=>e+this.weight(t),0),i=Math.random()*t;let n=0;for(const o of e)if(i<=(n+=this.weight(o)))return o;throw new Error("Тебя быть тут не должно...")}start(){var e=this.getRandomEffect(),t=this.settings.effects[e];this.active_cursor_effect=function(e,t){switch(e){case s.Rainbow:return f.rainbowCursor(t);case s.FairyDust:return f.fairyDustCursor(t);case s.SpringyEmoji:return f.springyEmojiCursor(t);case s.Emoji:return f.emojiCursor(t);case s.Ghost:return f.ghostCursor(t);case s.Trailing:return f.trailingCursor(t);case s.TextFlag:return f.textFlag(t);case s.FollowingDot:return f.followingDotCursor(t);case s.Bubbles:return f.bubbleCursor(t);case s.Snowflake:return f.snowflakeCursor(t);case s.Clock:return f.clockCursor(t)}}(e,t)}stop(){this.active_cursor_effect.destroy()}},class extends o{constructor(){super(...arguments),this.id="stupid_title",this.title="Глупые статусы",this.description="Каждому пользователю даётся новый глупый статус.",this.settings=new w,this.loadedGenders=null,this.seed=0}async loadGenders(){var e=a()(".pa-author a").map((e,t)=>t.innerText).toArray().reduce((e,t)=>(e.indexOf(t)<0&&e.push(t),e),[]),e="/api.php?"+a().param({method:"users.get",fields:"username,sex",username:e}),e=await(await fetch(e)).json();this.loadedGenders={},e.error||e.response.users.forEach(e=>{"0"!==e.sex&&(this.loadedGenders[e.username]="1"===e.sex)})}resolveGender(e){return null!==this.settings.gender_resolver?!!this.settings.gender_resolver.call(this,e):e in this.loadedGenders?!!this.loadedGenders[e]:(this.resetSeed(),"m"===this.selectRandom(e,{m:1,f:1}))}async start(){await this.loadGenders();const n={};a()(".post-author").each((e,t)=>{var i=a()(".pa-author a",t).text();i&&(i in n||(this.resetSeed(),n[i]=this.generatePhrase(i)),a()(".pa-title",t).text(n[i]))})}prng(i,e,t){let n=3735928559^e,o=1103547991^e;for(let e=0,t;e>>16,2246822507)^Math.imul(o^o>>>13,3266489909);e=4294967296*(2097151&(o=Math.imul(o^o>>>16,2246822507)^Math.imul(n^n>>>13,3266489909)))+(n>>>0);return Math.floor(Math.pow(Math.sin(e),2)*t)%t}resetSeed(){var e=new Date,t=60*e.getUTCHours()+e.getUTCMinutes();0===this.settings.change_frequency?this.seed=60*t+e.getUTCSeconds():this.seed=Math.floor(t/this.settings.change_frequency)}selectRandom(e,i){var t=Object.keys(i),n=t.reduce((e,t)=>e+i[t],0),o=this.prng(e,this.seed++,n);let s=0;for(const r of t)if(o<=(s+=i[r]))return r;throw new Error("Тебя быть тут не должно...")}buildVariants(e,t){var i={},e=this.settings.variants[e];for(const s of Object.entries(e)){var n=s[0],o=s[1];!o.enabled||o.gender===!t||!this.settings.rude&&o.rude||(i[n]=o.weight)}return i}capitalizeFirst(e){return e.charAt(0).toUpperCase()+e.slice(1)}generatePhrase(i){const n=this.resolveGender(i);var e=this.selectRandom(i,this.settings.schemas);return this.capitalizeFirst(e.replace(/\{(.+?)\}/g,(e,t)=>{t=this.buildVariants(t,n);return this.selectRandom(i,t)}))}}])})()})();
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 5023cce..b829856 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"prettier": "^2.8.2",
+ "terser-webpack-plugin": "^5.3.7",
"ts-loader": "^9.4.2",
"tsify": "^5.0.4",
"typescript": "^5.0.2",
diff --git a/package.json b/package.json
index abdd919..2c09fca 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"prettier": "^2.8.2",
+ "terser-webpack-plugin": "^5.3.7",
"ts-loader": "^9.4.2",
"tsify": "^5.0.4",
"typescript": "^5.0.2",
diff --git a/settings.compact.js b/settings.compact.js
index f183193..8f6cbc4 100644
--- a/settings.compact.js
+++ b/settings.compact.js
@@ -3,158 +3,158 @@ window.AprilJokes.disableAll();
window.AprilJokes.jokes.cursor.disableAll();
window.AprilJokes.settings = {
- shake: {
- enabled: true,
- chance: 1,
- directions: ['top', 'left', 'bottom', 'left'],
- force: 0.5,
- selector: '[id^=pun]',
- interval: 100,
- },
+ shake: {
+ enabled: true,
+ chance: 1,
+ directions: ["top", "left", "bottom", "left"],
+ force: 0.5,
+ selector: "[id^=pun]",
+ interval: 100,
+ },
- mirror: {
+ mirror: {
+ enabled: false,
+ chance: 30,
+ },
+
+ carnival: {
+ enabled: true,
+ chance: 30,
+ selector: 'img[src*="/avatars/"]',
+ variant_chance: 20,
+ variants: {
+ "sepia({0}%)": 100,
+ "saturate({0}%)": 100,
+ "hue-rotate({0}deg)": 360,
+ "invert({0}%)": 100,
+ },
+ },
+
+ humanitarian_hell: {
+ enabled: true,
+ chance: 20,
+ selector: ".post-content",
+ },
+
+ crazy_letters: {
+ enabled: true,
+ chance: 20,
+ selector: ".pa-author a",
+ },
+
+ letter_chaos: {
+ enabled: true,
+ chance: 20,
+ selector: ".post-content",
+ shuffle_chance: 50,
+ },
+
+ cursor: {
+ enabled: true,
+ chance: 100,
+ effects: {
+ rainbow: {
+ enabled: true,
+ weight: 50,
+ length: 20,
+ size: 3,
+ colors: [
+ "#9400D3",
+ "#4B0082",
+ "#0000FF",
+ "#00FF00",
+ "#FFFF00",
+ "#FF7F00",
+ "#FF0000",
+ ],
+ },
+ springy_emoji: {
+ enabled: true,
+ weight: 50,
+ emoji: "🤪",
+ },
+ emoji: {
+ enabled: true,
+ weight: 50,
+ emoji: ["🔥", "🐬", "🦆"],
+ },
+ fairy_dust: {
+ enabled: true,
+ weight: 50,
+ colors: ["#ff0000", "#00ff00", "#0000ff"],
+ },
+ ghost: {
+ enabled: true,
+ weight: 50,
+ },
+ trailing: {
+ enabled: true,
+ weight: 50,
+ particles: 15,
+ rate: 0.4,
+ baseImageSrc: null,
+ },
+ text_flag: {
+ enabled: true,
+ weight: 50,
+ text: "С днём смеха!",
+ color: "#000000",
+ font: "monospace",
+ textSize: 12,
+ gap: 14,
+ },
+ following_dot: {
+ enabled: true,
+ weight: 50,
+ color: "#323232a6",
+ },
+ bubbles: {
+ enabled: true,
+ weight: 50,
+ },
+ snowflake: {
enabled: false,
- chance: 30,
- },
-
- carnival: {
+ weight: 50,
+ },
+ clock: {
enabled: true,
- chance: 30,
- selector: 'img[src*="/avatars/"]',
- variant_chance: 20,
- variants: {
- 'sepia({0}%)': 100,
- 'saturate({0}%)': 100,
- 'hue-rotate({0}deg)': 360,
- 'invert({0}%)': 100,
- },
+ weight: 50,
+ dateColor: (string = "blue"),
+ faceColor: (string = "black"),
+ secondsColor: (string = "red"),
+ minutesColor: (string = "black"),
+ hoursColor: (string = "black"),
+ },
},
+ },
- humanitarian_hell: {
- enabled: true,
- chance: 20,
- selector: '.post-content',
+ stupid_title: {
+ enabled: true,
+ chance: 20,
+ gender_resolver: null,
+ rude: false,
+ change_frequency: 5,
+ schemas: {
+ "{adj} {noun}{p}": 10,
+ "{noun} {adj}{p}": 3,
+ "{adj}, {adj} {noun}{p}": 10,
+ "{adj} и очень {adj} {noun}{p}": 10,
+ "{adj} {noun} {verb}{p}": 10,
+ "{adj} {noun} {adv} {verb}{p}": 5,
+ "{adj} {noun} {verb} {place}{p}": 5,
+ "{adj} {noun} {adv} {verb} {place}{p}": 5,
},
-
- crazy_letters: {
- enabled: true,
- chance: 20,
- selector: '.pa-author a'
+ variants: {
+ adj: {
+ ...window.AprilJokes.settings.stupid_title.variants.adj,
+ ...BuildStupdiTitleVariant(true, "скромный, шутливый, !грубый"),
+ ...BuildStupdiTitleVariant(false, "скромная, шутливая, !грубая"),
+ },
+ noun: { ...window.AprilJokes.settings.stupid_title.variants.noun },
+ verb: { ...window.AprilJokes.settings.stupid_title.variants.verb },
+ place: { ...window.AprilJokes.settings.stupid_title.variants.place },
+ adv: { ...window.AprilJokes.settings.stupid_title.variants.adv },
+ p: { ...window.AprilJokes.settings.stupid_title.variants.p },
},
-
- letter_chaos: {
- enabled: true,
- chance: 20,
- selector: '.post-content',
- shuffle_chance: 50,
- },
-
- cursor: {
- enabled: true,
- chance: 100,
- effects: {
- rainbow: {
- enabled: true,
- weight: 50,
- length: 20,
- size: 3,
- colors: [
- "#9400D3",
- "#4B0082",
- "#0000FF",
- "#00FF00",
- "#FFFF00",
- "#FF7F00",
- "#FF0000"
- ],
- },
- springy_emoji: {
- enabled: true,
- weight: 50,
- emoji: "🤪",
- },
- emoji: {
- enabled: true,
- weight: 50,
- emoji: ["🔥", "🐬", "🦆"],
- },
- fairy_dust: {
- enabled: true,
- weight: 50,
- colors: ["#ff0000", "#00ff00", "#0000ff"],
- },
- ghost: {
- enabled: true,
- weight: 50,
- },
- trailing: {
- enabled: true,
- weight: 50,
- particles: 15,
- rate: 0.4,
- baseImageSrc: null,
- },
- text_flag: {
- enabled: true,
- weight: 50,
- text: "С днём смеха!",
- color: "#000000",
- font: "monospace",
- textSize: 12,
- gap: 14,
- },
- following_dot: {
- enabled: true,
- weight: 50,
- color: "#323232a6",
- },
- bubbles: {
- enabled: true,
- weight: 50,
- },
- snowflake: {
- enabled: false,
- weight: 50,
- },
- clock: {
- enabled: true,
- weight: 50,
- dateColor: string = "blue",
- faceColor: string = "black",
- secondsColor: string = "red",
- minutesColor: string = "black",
- hoursColor: string = "black",
- },
- },
- },
-
- stupid_title: {
- enabled: true,
- chance: 20,
- gender_resolver: null,
- rude: false,
- change_frequency: 5,
- schemas: {
- '{adj} {noun}{p}': 10,
- '{noun} {adj}{p}': 3,
- '{adj}, {adj} {noun}{p}': 10,
- '{adj} и очень {adj} {noun}{p}': 10,
- '{adj} {noun} {verb}{p}': 10,
- '{adj} {noun} {adv} {verb}{p}': 5,
- '{adj} {noun} {verb} {place}{p}': 5,
- '{adj} {noun} {adv} {verb} {place}{p}': 5,
- },
- variants: {
- adj: {
- ...window.AprilJokes.settings.stupid_title.variants.adj,
- ...BuildStupdiTitleVariant(true, 'скромный, шутливый, !грубый'),
- ...BuildStupdiTitleVariant(false, 'скромная, шутливая, !грубая'),
- },
- noun: { ...window.AprilJokes.settings.stupid_title.variants.noun, },
- verb: { ...window.AprilJokes.settings.stupid_title.variants.verb, },
- place: { ...window.AprilJokes.settings.stupid_title.variants.place, },
- adv: { ...window.AprilJokes.settings.stupid_title.variants.adv, },
- p: { ...window.AprilJokes.settings.stupid_title.variants.p, },
- },
- },
-}
\ No newline at end of file
+ },
+};
diff --git a/settings.example.js b/settings.example.js
index 3aaa951..85db36f 100644
--- a/settings.example.js
+++ b/settings.example.js
@@ -9,444 +9,445 @@ window.AprilJokes.disableAll();
window.AprilJokes.jokes.cursor.disableAll();
window.AprilJokes.settings = {
+ /**
+ * Глобальные настройки
+ */
+ core: {
/**
- * Глобальные настройки
+ * Активность шутки.
+ * Установите это значение `false`, что бы отключить шутку.
+ * Если `true`, то шутка будет обработана как обычно.
*/
- core: {
- /**
- * Активность шутки.
- * Установите это значение `false`, что бы отключить шутку.
- * Если `true`, то шутка будет обработана как обычно.
- */
- enabled: true,
-
- /**
- * Вероятность срабатывания в процентах.
- * Если установлено значение `100`, шутка будет срабатывать всегда.
- * Если установлено значение `0`, шутка никогда не сработает.
- * Лучше отключать шутку, используя enabled = false!
- */
- chance: 100,
+ enabled: true,
+
+ /**
+ * Вероятность срабатывания в процентах.
+ * Если установлено значение `100`, шутка будет срабатывать всегда.
+ * Если установлено значение `0`, шутка никогда не сработает.
+ * Лучше отключать шутку, используя enabled = false!
+ */
+ chance: 100,
+ },
+
+ /**
+ * Форумо-трясение
+ *
+ * Страница форума начинает трястись.
+ *
+ * @author Kozhilya
+ */
+ shake: {
+ enabled: false,
+ chance: 1,
+
+ /**
+ * Направление тряски
+ */
+ directions: ["top", "left", "bottom", "left"],
+
+ /**
+ * Сила тряски
+ */
+ force: 0.5,
+
+ /**
+ * Селектор всех элементов, которые будут трястись
+ */
+ selector: "[id^=pun]",
+
+ /**
+ * Частота тряски
+ */
+ interval: 100,
+ },
+
+ /**
+ * Зеркало
+ *
+ * Элементы форума отражаются (по крайней мере, пытаются)
+ *
+ * @author Kozhilya
+ */
+ mirror: {
+ enabled: false,
+ chance: 30,
+ },
+
+ /**
+ * Карнавал
+ *
+ * Аватарки приобретают интересные цвета
+ *
+ * @author Kozhilya
+ */
+ carnival: {
+ enabled: true,
+ chance: 30,
+
+ /**
+ * Селектор всех элементов, которые будут перекрашены.
+ */
+ selector: 'img[src*="/avatars/"]',
+
+ /**
+ * Вероятность применения фильтра (может сработать нескольк раз).
+ */
+ variant_chance: 20,
+
+ /**
+ * Список фильтров и диапозона случайных параметров.
+ */
+ variants: {
+ "sepia({0}%)": 100,
+ "saturate({0}%)": 100,
+ "hue-rotate({0}deg)": 360,
+ "invert({0}%)": 100,
},
-
+ },
+
+ /**
+ * Ад гуманитария
+ *
+ * Все "тся" заменяются на "ться", а все "ться" на "тся"
+ *
+ * @author Kozhilya
+ */
+ humanitarian_hell: {
+ enabled: true,
+ chance: 20,
+
/**
- * Форумо-трясение
- *
- * Страница форума начинает трястись.
- *
- * @author Kozhilya
+ * Селектор всех элементов, которые будут затронуты.
*/
- shake: {
+ selector: ".post-content",
+ },
+
+ /**
+ * Мама, Я сОшЛа С уМа!
+ *
+ * иМеНа ЗаБоРчИкОм
+ *
+ * @author Kozhilya
+ */
+ crazy_letters: {
+ enabled: true,
+ chance: 20,
+
+ /**
+ * Селектор всех элементов, которые будут затронуты.
+ */
+ selector: ".pa-author a",
+ },
+
+ /**
+ * **Буквенный хаос**
+ *
+ * Все буквы в словах, кроме первой и последней, перемешены.
+ *
+ * @author Kozhilya
+ */
+ letter_chaos: {
+ enabled: true,
+ chance: 20,
+
+ /**
+ * Селектор всех элементов, которые будут затронуты
+ */
+ selector: ".post-content",
+
+ /**
+ * Вероятность того, что буквы будут перемешаны
+ */
+ shuffle_chance: 50,
+ },
+
+ /**
+ * **Красивый курсор**
+ *
+ * Эффекты, следующие за курсором
+ *
+ * @author Tim Holman
+ * @author Kozhilya
+ *
+ * @link https://tholman.com/cursor-effects/
+ */
+ cursor: {
+ enabled: true,
+ chance: 100,
+
+ /**
+ * Список эффектов курсора
+ */
+ effects: {
+ /**
+ * Радужный след за курсором
+ */
+ rainbow: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Длина полосы
+ */
+ length: 20,
+
+ /**
+ * Ширина полосы
+ */
+ size: 3,
+
+ /**
+ * Цвета полос (сверху вниз)
+ */
+ colors: [
+ "#9400D3",
+ "#4B0082",
+ "#0000FF",
+ "#00FF00",
+ "#FFFF00",
+ "#FF7F00",
+ "#FF0000",
+ ],
+ },
+
+ /**
+ * Цепочка из эмодзи "на резинке" под курсором
+ */
+ springy_emoji: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Висящий эмодзи
+ */
+ emoji: "🤪",
+ },
+
+ /**
+ * Падающие от курсора эмодзи
+ */
+ emoji: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Падающие эмодзи
+ */
+ emoji: ["🔥", "🐬", "🦆"],
+ },
+
+ /**
+ * Падающие от курсора пылинки
+ */
+ fairy_dust: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Цвет пылинок
+ */
+ colors: ["#ff0000", "#00ff00", "#0000ff"],
+ },
+
+ /**
+ * След "зависших" курсоров
+ */
+ ghost: {
+ enabled: true,
+ weight: 50,
+ },
+
+ /**
+ * След курсоров "на резинке"
+ */
+ trailing: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Количество элементов
+ */
+ particles: 15,
+
+ /**
+ * Скорость частиц
+ */
+ rate: 0.4,
+
+ /**
+ * Ссылка на картинку с курсором
+ * Используйте `null`, что бы использовать картинку курсора по умолчанию
+ */
+ baseImageSrc: null,
+ },
+
+ /**
+ * Текст, развивающийся флагом от курсора
+ */
+ text_flag: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Отображаемый текст
+ */
+ text: "С днём смеха!",
+
+ /**
+ * Цвет текста
+ */
+ color: "#000000",
+
+ /**
+ * Шрифт текста
+ */
+ font: "monospace",
+
+ /**
+ * Размер текста
+ */
+ textSize: 12,
+
+ /**
+ * Растояние между бквами
+ */
+ gap: 14,
+ },
+
+ /**
+ * Точка, преследующая курсор
+ */
+ following_dot: {
+ enabled: true,
+ weight: 50,
+
+ /**
+ * Цвет догоняющей точки
+ */
+ color: "#323232a6",
+ },
+
+ /**
+ * Пузырьки от курсора
+ */
+ bubbles: {
+ enabled: true,
+ weight: 50,
+ },
+
+ /**
+ * Снежинки, летящие от курсора
+ *
+ * Отключено, потому что сейчас не зима :D
+ */
+ snowflake: {
enabled: false,
- chance: 1,
+ weight: 50,
+ },
- /**
- * Направление тряски
- */
- directions: ['top', 'left', 'bottom', 'left'],
-
- /**
- * Сила тряски
- */
- force: 0.5,
-
- /**
- * Селектор всех элементов, которые будут трястись
- */
- selector: '[id^=pun]',
-
- /**
- * Частота тряски
- */
- interval: 100,
- },
-
- /**
- * Зеркало
- *
- * Элементы форума отражаются (по крайней мере, пытаются)
- *
- * @author Kozhilya
- */
- mirror: {
- enabled: false,
- chance: 30,
- },
-
- /**
- * Карнавал
- *
- * Аватарки приобретают интересные цвета
- *
- * @author Kozhilya
- */
- carnival: {
+ /**
+ * Циферблат вокруг курсора
+ */
+ clock: {
enabled: true,
- chance: 30,
+ weight: 50,
/**
- * Селектор всех элементов, которые будут перекрашены.
+ * Цвет даты
*/
- selector: 'img[src*="/avatars/"]',
+ dateColor: (string = "blue"),
/**
- * Вероятность применения фильтра (может сработать нескольк раз).
+ * Цвет циферблата
*/
- variant_chance: 20,
+ faceColor: (string = "black"),
/**
- * Список фильтров и диапозона случайных параметров.
+ * Цвет секундной стрелки
*/
- variants: {
- 'sepia({0}%)': 100,
- 'saturate({0}%)': 100,
- 'hue-rotate({0}deg)': 360,
- 'invert({0}%)': 100,
- },
+ secondsColor: (string = "red"),
+
+ /**
+ * Цвет минутной стрелки
+ */
+ minutesColor: (string = "black"),
+
+ /**
+ * Цвет часовой стрелки
+ */
+ hoursColor: (string = "black"),
+ },
+ },
+ },
+
+ /**
+ * **Глупые статусы**
+ *
+ * Каждому пользователю даётся новый глупый статус.
+ *
+ * @author Kozhilya
+ */
+ stupid_title: {
+ enabled: true,
+ chance: 20,
+
+ /**
+ * Определение пола по имени.
+ * По умолчанию род устанавливается случайным образом
+ *
+ * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
+ *
+ * @param name Имя персонажа
+ * @returns true, если мужской род; false, если женский род
+ */
+ gender_resolver: null,
+
+ /**
+ * Разрешить возможно оскорбительные слова
+ */
+ rude: false,
+
+ /**
+ * Частота смены статусов (в минутах)
+ * Установите значение 0, что бы обновление происходило при каждом обновлении страницы
+ */
+ change_frequency: 5,
+
+ /**
+ * Разрешённые схемы формирования статусов
+ *
+ * {adj} - прилагательное
+ * {noun} - имя существительно
+ * {verb} - глагол
+ * {adv} - наречие
+ * {p} - знак препинания
+ */
+ schemas: {
+ "{adj} {noun}{p}": 10,
+ "{noun} {adj}{p}": 3,
+ "{adj}, {adj} {noun}{p}": 10,
+ "{adj} и очень {adj} {noun}{p}": 10,
+ "{adj} {noun} {verb}{p}": 10,
+ "{adj} {noun} {adv} {verb}{p}": 5,
+ "{adj} {noun} {verb} {place}{p}": 5,
+ "{adj} {noun} {adv} {verb} {place}{p}": 5,
},
/**
- * Ад гуманитария
- *
- * Все "тся" заменяются на "ться", а все "ться" на "тся"
- *
- * @author Kozhilya
+ * Вариант слов.
+ * Смотрите раздел описания скрипта, что бы настроить это поле
*/
- humanitarian_hell: {
- enabled: true,
- chance: 20,
-
- /**
- * Селектор всех элементов, которые будут затронуты.
- */
- selector: '.post-content',
+ variants: {
+ adj: {
+ ...window.AprilJokes.settings.stupid_title.variants.adj,
+ ...BuildStupdiTitleVariant(true, "скромный, шутливый, !грубый"),
+ ...BuildStupdiTitleVariant(false, "скромная, шутливая, !грубая"),
+ },
+ noun: { ...window.AprilJokes.settings.stupid_title.variants.noun },
+ verb: { ...window.AprilJokes.settings.stupid_title.variants.verb },
+ place: { ...window.AprilJokes.settings.stupid_title.variants.place },
+ adv: { ...window.AprilJokes.settings.stupid_title.variants.adv },
+ p: { ...window.AprilJokes.settings.stupid_title.variants.p },
},
-
- /**
- * Мама, Я сОшЛа С уМа!
- *
- * иМеНа ЗаБоРчИкОм
- *
- * @author Kozhilya
- */
- crazy_letters: {
- enabled: true,
- chance: 20,
-
- /**
- * Селектор всех элементов, которые будут затронуты.
- */
- selector: '.pa-author a'
- },
-
- /**
- * **Буквенный хаос**
- *
- * Все буквы в словах, кроме первой и последней, перемешены.
- *
- * @author Kozhilya
- */
- letter_chaos: {
- enabled: true,
- chance: 20,
-
- /**
- * Селектор всех элементов, которые будут затронуты
- */
- selector: '.post-content',
-
- /**
- * Вероятность того, что буквы будут перемешаны
- */
- shuffle_chance: 50,
- },
-
- /**
- * **Красивый курсор**
- *
- * Эффекты, следующие за курсором
- *
- * @author Tim Holman
- * @author Kozhilya
- *
- * @link https://tholman.com/cursor-effects/
- */
- cursor: {
- enabled: true,
- chance: 100,
-
- /**
- * Список эффектов курсора
- */
- effects: {
- /**
- * Радужный след за курсором
- */
- rainbow: {
- enabled: true,
- weight: 50,
-
- /**
- * Длина полосы
- */
- length: 20,
-
- /**
- * Ширина полосы
- */
- size: 3,
-
- /**
- * Цвета полос (сверху вниз)
- */
- colors: [
- "#9400D3",
- "#4B0082",
- "#0000FF",
- "#00FF00",
- "#FFFF00",
- "#FF7F00",
- "#FF0000"
- ],
- },
-
- /**
- * Цепочка из эмодзи "на резинке" под курсором
- */
- springy_emoji: {
- enabled: true,
- weight: 50,
-
- /**
- * Висящий эмодзи
- */
- emoji: "🤪",
- },
-
- /**
- * Падающие от курсора эмодзи
- */
- emoji: {
- enabled: true,
- weight: 50,
-
- /**
- * Падающие эмодзи
- */
- emoji: ["🔥", "🐬", "🦆"],
- },
-
- /**
- * Падающие от курсора пылинки
- */
- fairy_dust: {
- enabled: true,
- weight: 50,
-
- /**
- * Цвет пылинок
- */
- colors: ["#ff0000", "#00ff00", "#0000ff"],
- },
-
- /**
- * След "зависших" курсоров
- */
- ghost: {
- enabled: true,
- weight: 50,
- },
-
- /**
- * След курсоров "на резинке"
- */
- trailing: {
- enabled: true,
- weight: 50,
-
- /**
- * Количество элементов
- */
- particles: 15,
-
- /**
- * Скорость частиц
- */
- rate: 0.4,
-
- /**
- * Ссылка на картинку с курсором
- * Используйте `null`, что бы использовать картинку курсора по умолчанию
- */
- baseImageSrc: null,
- },
-
- /**
- * Текст, развивающийся флагом от курсора
- */
- text_flag: {
- enabled: true,
- weight: 50,
-
- /**
- * Отображаемый текст
- */
- text: "С днём смеха!",
-
- /**
- * Цвет текста
- */
- color: "#000000",
-
- /**
- * Шрифт текста
- */
- font: "monospace",
-
- /**
- * Размер текста
- */
- textSize: 12,
-
- /**
- * Растояние между бквами
- */
- gap: 14,
- },
-
- /**
- * Точка, преследующая курсор
- */
- following_dot: {
- enabled: true,
- weight: 50,
-
- /**
- * Цвет догоняющей точки
- */
- color: "#323232a6",
- },
-
- /**
- * Пузырьки от курсора
- */
- bubbles: {
- enabled: true,
- weight: 50,
- },
-
- /**
- * Снежинки, летящие от курсора
- *
- * Отключено, потому что сейчас не зима :D
- */
- snowflake: {
- enabled: false,
- weight: 50,
- },
-
- /**
- * Циферблат вокруг курсора
- */
- clock: {
- enabled: true,
- weight: 50,
-
- /**
- * Цвет даты
- */
- dateColor: string = "blue",
-
- /**
- * Цвет циферблата
- */
- faceColor: string = "black",
-
- /**
- * Цвет секундной стрелки
- */
- secondsColor: string = "red",
-
- /**
- * Цвет минутной стрелки
- */
- minutesColor: string = "black",
-
- /**
- * Цвет часовой стрелки
- */
- hoursColor: string = "black",
- },
- },
- },
-
- /**
- * **Глупые статусы**
- *
- * Каждому пользователю даётся новый глупый статус.
- *
- * @author Kozhilya
- */
- stupid_title: {
- enabled: true,
- chance: 20,
-
- /**
- * Определение пола по имени.
- * По умолчанию род устанавливается случайным образом
- *
- * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
- *
- * @param name Имя персонажа
- * @returns true, если мужской род; false, если женский род
- */
- gender_resolver: null,
-
- /**
- * Разрешить возможно оскорбительные слова
- */
- rude: false,
-
- /**
- * Частота смены статусов (в минутах)
- */
- change_frequency: 5,
-
- /**
- * Разрешённые схемы формирования статусов
- *
- * {adj} - прилагательное
- * {noun} - имя существительно
- * {verb} - глагол
- * {adv} - наречие
- * {p} - знак препинания
- */
- schemas: {
- '{adj} {noun}{p}': 10,
- '{noun} {adj}{p}': 3,
- '{adj}, {adj} {noun}{p}': 10,
- '{adj} и очень {adj} {noun}{p}': 10,
- '{adj} {noun} {verb}{p}': 10,
- '{adj} {noun} {adv} {verb}{p}': 5,
- '{adj} {noun} {verb} {place}{p}': 5,
- '{adj} {noun} {adv} {verb} {place}{p}': 5,
- },
-
- /**
- * Вариант слов.
- * Смотрите раздел описания скрипта, что бы настроить это поле
- */
- variants: {
- adj: {
- ...window.AprilJokes.settings.stupid_title.variants.adj,
- ...BuildStupdiTitleVariant(true, 'скромный, шутливый, !грубый'),
- ...BuildStupdiTitleVariant(false, 'скромная, шутливая, !грубая'),
- },
- noun: { ...window.AprilJokes.settings.stupid_title.variants.noun, },
- verb: { ...window.AprilJokes.settings.stupid_title.variants.verb, },
- place: { ...window.AprilJokes.settings.stupid_title.variants.place, },
- adv: { ...window.AprilJokes.settings.stupid_title.variants.adv, },
- p: { ...window.AprilJokes.settings.stupid_title.variants.p, },
- },
- },
-}
\ No newline at end of file
+ },
+};
diff --git a/src/common.ts b/src/common.ts
new file mode 100644
index 0000000..4d5a4cb
--- /dev/null
+++ b/src/common.ts
@@ -0,0 +1,14 @@
+import $ from "jquery";
+
+/**
+ * Получение всех элементов, являющимися TextNode.
+ * Используется некоторыми шутками для уменьшения влияния на существующее DOM-дерево.
+ */
+export function getTextNodes(
+ root: any
+): JQuery {
+ return $(root)
+ .find("*")
+ .contents()
+ .filter((_, node) => node.nodeType === Node.TEXT_NODE);
+}
diff --git a/src/data/stupid_title_variants.ts b/src/data/stupid_title_variants.ts
new file mode 100644
index 0000000..7868214
--- /dev/null
+++ b/src/data/stupid_title_variants.ts
@@ -0,0 +1,57 @@
+import {
+ StupidTitleVariants,
+ BuildStupdiTitleVariant,
+} from "../jokes/stupid_title";
+export function StupidTitleDefaultVariants(vars: StupidTitleVariants) {
+ vars.adj = {
+ ...BuildStupdiTitleVariant(
+ true,
+ "адекватный,$адский,активный,алчный,%ангельский,банальный,безнадёжный,$бесполезный,былой,верный,виртуальный,вкусный,воспитанный,выгодный,выходной,гигантский,$глупый,гордый,грамотный,громадный,грубый,далекий,двуличный,деятельный,доброжелательный,$жалкий,жёсткий,жестокий,$жирный,заинтересованный,запасной,застенчивый,знакомый,иностранный,истинный,конструктивный,креативный,ласковый,любимый,мелочный,^мирный,мстительный,небесный,невозможный,независимый,низкий,нужный,объективный,обыкновенный,опрятный,^оригинальный,особенный,острый,откровенный,открытый,пестрый,повседневный,подземный,пожилой,показательный,последний,посторонний,провинциальный,пышный,робкий,родной,самовлюблённый,северный,седой,семейный,серьёзный,сногсшибательный,^солнечный,спокойный,суровый,терпеливый,уверенный в себе,ударный,^удачливый,формальный,хладнокровный,целый,чистый,чрезвычайный,чужой,южный"
+ ),
+ ...BuildStupdiTitleVariant(
+ false,
+ "адекватная,$адская,активная,алчная,%ангельская,банальная,безнадёжная,$бесполезная,былая,верная,виртуальная,вкусная,воспитанная,выгодная,выходная,гигантская,$глупая,гордая,грамотная,громадная,грубая,далекая,двуличная,деятельная,доброжелательная,$жалкая,жёсткая,жестокая,$жирная,заинтересованная,запасная,застенчивая,знакомая,иностранная,истинная,конструктивная,креативная,ласковая,любимая,мелочная,^мирная,мстительная,небесная,невозможная,независимая,низкая,нужная,объективная,обыкновенная,опрятная,^оригинальная,особенная,острая,откровенная,открытая,пестрая,повседневная,подземная,пожилая,показательная,последняя,посторонняя,провинциальная,пышная,робкая,родная,самовлюблённая,северная,седая,семейная,серьезная,сногсшибательная,^солнечная,спокаяная,суровая,терпеливая,уверенная в себе,ударная,^удачливая,формальная,хладнокровная,целая,чистая,чрезвычайная,чужая,южная"
+ ),
+ ...vars.adj,
+ };
+ vars.noun = {
+ ...BuildStupdiTitleVariant(
+ true,
+ "актёр,$балбес,бард,басурманин,борец,бродяга,бруталище,волк,дворф,%дед,%дедушка,джентльмен,диван,динозавр,дракон,ёж,%ёжик,жук,зайц,$злоумышленник,зомби,карасик,кентавр,кит,кот,кролик,кулак,лев,ледник,лесоруб,%мальчег,мальчик,манекен,массажист,мачо,монах,монстр,наследник,ниндзя,носорог,одиночка,организм,орёл,отец,принц,проказник,пузырь,$раб,рыцарь,спортсмен,сурикат,сын,%сынок,тигр,товарищ,хоббит,эльф"
+ ),
+ ...BuildStupdiTitleVariant(
+ false,
+ "актриса,акула,$бабка,бабочка,%бабушка,бактерия,белочка,бусинка,валькирия,$ведьма,волторна,волчица,горгулья,дворфиха,девочка,деревяшка,детка,%деффачка,%дочка,дочь,заноза,$злоумышленница,знаменитость,каруселька,килька,конфетка,косточка,кошка,красотка,крышка,леди,$личинка,львица,мать,машина,милашка,монашка,мышь,наследница,орлица,панда,пиранья,принцесса,простуда,$рабыня,русалочка,рыбка,рыбка,скамеечка,спортсменка,тигрица,травка,черепашка,чертовка,эльфийка"
+ ),
+ ...vars.noun,
+ };
+ vars.verb = {
+ ...BuildStupdiTitleVariant(
+ null,
+ "бежит,берёт и делает,болеет,встаёт,говорит,думает,забывает,замирает,заходит,идёт,%имеет план и придерживается его,ловит,меняется,молчит,нарушает,оглядывается,орёт,освобождает,отвечает,отказаться куда-то идти,отказывается,отказывается вставать,отказывается что-то делать,оформляет,плывёт,поглядывает,поздравляет,поздравляет,получает,прибывает,приказывает,просыпается,прощает,работает,радуется,скрывается,снимает,снимает,снимается в сериале,снимается в фильме,собирается,соединяет,спрашивает,тянет,указывает,учит,читает,что-то скрывает,чувствует опасность,шепчет"
+ ),
+ ...BuildStupdiTitleVariant(
+ true,
+ "взял и сделал,додумался,женился,женится,заработался,не спал,не успел,обиделся,познал дзен,присел,проснулся,скрылся,смог,успел"
+ ),
+ ...BuildStupdiTitleVariant(
+ false,
+ "взяла и сделала,выходит замуж,вышла замуж,додумалась,заработалась,не спала,не успела,обиделась,познала дзен,присела,проснулась,скрылась,смогла,успела"
+ ),
+ ...vars.verb,
+ };
+ vars.place = {
+ ...BuildStupdiTitleVariant(
+ null,
+ "в джунглях,в квартире,в космосе,в лесу,в очереди,в пустыне,в театре,%в тёплой и уютной квартире,в филармонии,где-то,на заснеженной вершине,на конференции,на пляже,на форуме,под водой,под дождём,%под проливным дождём"
+ ),
+ ...vars.place,
+ };
+ vars.adv = {
+ ...BuildStupdiTitleVariant(
+ null,
+ "безрассудно,быстро,внезапно,вновь,$грубо,грустно,медленно,%молниеносно,немедленно,опять,осмысленно,позже,предсказуемо,расчётливо,регулярно,счастливо,уже,уныло"
+ ),
+ ...vars.adv,
+ };
+}
diff --git a/src/html.d.ts b/src/html.d.ts
index 56b1935..3a031d8 100644
--- a/src/html.d.ts
+++ b/src/html.d.ts
@@ -1,4 +1,4 @@
declare module "*.html" {
- const content: string;
- export default content;
-}
\ No newline at end of file
+ const content: string;
+ export default content;
+}
diff --git a/src/html/button.html b/src/html/button.html
index a3646ae..9d8db74 100644
--- a/src/html/button.html
+++ b/src/html/button.html
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/src/html/styles.html b/src/html/styles.html
index 14ee2e5..50fe776 100644
--- a/src/html/styles.html
+++ b/src/html/styles.html
@@ -1,5 +1,5 @@
\ No newline at end of file
+ }
+
diff --git a/src/html/window.html b/src/html/window.html
index 5d4b981..8e41faa 100644
--- a/src/html/window.html
+++ b/src/html/window.html
@@ -1,15 +1,23 @@
-
+
-
+
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/src/joke.ts b/src/joke.ts
index 9377908..9c04a95 100644
--- a/src/joke.ts
+++ b/src/joke.ts
@@ -2,87 +2,85 @@
* Объект настроек шутки
*/
export interface JokeSettings {
- /**
- * Флаг блокировки срабатывания
- */
- enabled: boolean;
+ /**
+ * Флаг блокировки срабатывания
+ */
+ enabled: boolean;
- /**
- * Вероятность срабатывания в процентах
- */
- chance: number;
+ /**
+ * Вероятность срабатывания в процентах
+ */
+ chance: number;
}
/**
* Объект, описывающий шутку
*/
export abstract class Joke {
- /**
- * Идентификатор шутки
- */
- abstract id: string;
+ /**
+ * Идентификатор шутки
+ */
+ abstract id: string;
- /**
- * Заголовок шутки
- */
- abstract title: string;
+ /**
+ * Заголовок шутки
+ */
+ abstract title: string;
- /**
- * Описание шутки
- */
- abstract description: string;
+ /**
+ * Описание шутки
+ */
+ abstract description: string;
- /**
- * Настройки шутки
- */
- settings: JokeSettings;
+ /**
+ * Настройки шутки
+ */
+ settings: JokeSettings;
- /**
- * Проверка вероятности
- *
- * @param {number} chance Вероятность срабатывания
- * @returns {boolean} Флаг срабатывания (true - сработало)
- */
- check(chance: number): boolean {
- return Math.random() < 0.01 * chance;
- }
-
- /**
- * Разблокировать шутку
- */
- enable() {
- this.settings.enabled = true;
- }
-
- /**
- * Заблокировать шутку
- */
- disable() {
- this.settings.enabled = false;
- }
+ /**
+ * Проверка вероятности
+ *
+ * @param {number} chance Вероятность срабатывания
+ * @returns {boolean} Флаг срабатывания (true - сработало)
+ */
+ check(chance: number): boolean {
+ return Math.random() < 0.01 * chance;
+ }
- /**
- * Переключить блокировку шутки
- * @param value Новое значение
- */
- toggle(value: boolean|null = null) {
- this.settings.enabled = (value === null) ? !this.settings.enabled : value;
- }
+ /**
+ * Разблокировать шутку
+ */
+ enable() {
+ this.settings.enabled = true;
+ }
- /**
- * Запуск шутки
- */
- abstract start(): void;
+ /**
+ * Заблокировать шутку
+ */
+ disable() {
+ this.settings.enabled = false;
+ }
+
+ /**
+ * Переключить блокировку шутки
+ * @param value Новое значение
+ */
+ toggle(value: boolean | null = null) {
+ this.settings.enabled = value === null ? !this.settings.enabled : value;
+ }
+
+ /**
+ * Запуск шутки
+ */
+ abstract start(): void;
}
/**
* Объект, описывающий шутку, которая может быть остановлена
*/
-export abstract class StopableJoke extends Joke
-{
- /**
- * Остановка шутки, если возможно
- */
- abstract stop(): void;
+export abstract class StopableJoke extends Joke {
+ /**
+ * Остановка шутки, если возможно
+ */
+ abstract stop(): void;
}
-
diff --git a/src/joker.ts b/src/joker.ts
index 0452246..989d10f 100644
--- a/src/joker.ts
+++ b/src/joker.ts
@@ -4,279 +4,294 @@ import { JokerUI } from "./ui";
/**
* Блок обработки перво-апрельских шуток
- *
+ *
* @author Kozhilya
*/
export class JokerClass {
- ui: JokerUI = new JokerUI(this);
+ ui: JokerUI = new JokerUI(this);
- /**
- * Проверка вероятности
- *
- * @param {number} chance Вероятность срабатывания
- * @returns {boolean} Флаг срабатывания (true - сработало)
- */
- check(chance: number): boolean {
- return Math.random() < 0.01 * chance;
+ /**
+ * Проверка вероятности
+ *
+ * @param {number} chance Вероятность срабатывания
+ * @returns {boolean} Флаг срабатывания (true - сработало)
+ */
+ check(chance: number): boolean {
+ return Math.random() < 0.01 * chance;
+ }
+
+ /**
+ * Добавление шутки
+ *
+ * @param joke Объект шутки
+ */
+ add(joke: Joke): void {
+ this.jokes[joke.id] = joke;
+ }
+
+ /**
+ * Регистрация новой шутки
+ *
+ * @param id Идентификатор шутки
+ * @param handler Метод, выполняющий шутку
+ * @param settings Объект настроек шутки
+ */
+ addCustom(
+ id: string,
+ handler: Function,
+ settings: JokeSettings | null = null
+ ): Joke {
+ const joke = new (class extends Joke {
+ id = id;
+
+ title = "Пользовательская шутка";
+
+ description = "Для шутки не определено описание";
+
+ settings: JokeSettings = { chance: 10, enabled: true };
+
+ start() {
+ handler.call(this, this.settings);
+ }
+ })();
+
+ Object.assign(joke.settings, settings ?? {});
+
+ this.add(joke);
+
+ return joke;
+ }
+
+ /**
+ * Получение полного списка настроек
+ */
+ get settings(): SettingsMap {
+ const result: SettingsMap = {};
+
+ result["core"] = this.core_settings;
+
+ for (const entries of Object.entries(this.jokes)) {
+ const id = entries[0];
+ const joke = entries[1];
+
+ result[id] = joke.settings;
}
- /**
- * Добавление шутки
- *
- * @param joke Объект шутки
- */
- add(joke: Joke): void {
- this.jokes[joke.id] = joke;
+ return result;
+ }
+
+ /**
+ * Запись новых настроек
+ *
+ * @param settings
+ */
+ set settings(settings: SettingsMap) {
+ for (const entry of Object.entries(settings)) {
+ const id = entry[0];
+ const jokeSettings = entry[1];
+ const targetSettings =
+ id === "core" ? this.core_settings : this.jokes[id].settings;
+
+ Object.assign(targetSettings, jokeSettings);
+ }
+ }
+
+ /**
+ * Разблокировать систему
+ */
+ enable() {
+ this.core_settings.enabled = true;
+ }
+
+ /**
+ * Заблокировать систему
+ */
+ disable() {
+ this.core_settings.enabled = false;
+ }
+
+ /**
+ * Разблокировать все шутки
+ */
+ enableAll() {
+ for (const key of Object.keys(this.jokes)) {
+ this.jokes[key].enable();
+ }
+ }
+
+ /**
+ * Заблокировать все шутки
+ */
+ disableAll() {
+ for (const key of Object.keys(this.jokes)) {
+ this.jokes[key].disable();
+ }
+ }
+
+ /**
+ * Переключение шутки
+ */
+ toggleJoke(joke_id: string, state: boolean) {
+ const joke = this.jokes[joke_id];
+ joke.settings.enabled = state;
+
+ if (joke instanceof StopableJoke && !state) {
+ joke.stop();
}
- /**
- * Регистрация новой шутки
- *
- * @param id Идентификатор шутки
- * @param handler Метод, выполняющий шутку
- * @param settings Объект настроек шутки
- */
- addCustom(id: string, handler: Function, settings: JokeSettings|null = null): Joke {
- const joke = new class extends Joke {
- id = id;
+ this.saveUserStates();
+ }
- title = 'Пользовательская шутка';
+ /**
+ * Сохранение пользовательских настроек
+ */
+ saveUserStates() {
+ const states: any = {};
- description = 'Для шутки не определено описание';
-
- settings: JokeSettings = { chance: 10, enabled: true };
-
- start() {
- handler.call(this, this.settings);
- }
- };
-
- Object.assign(joke.settings, settings ?? {});
-
- this.add(joke);
-
- return joke;
+ for (const key of Object.keys(this.jokes)) {
+ states[key] = this.jokes[key].settings.enabled;
}
- /**
- * Получение полного списка настроек
- */
- get settings(): SettingsMap {
- const result: SettingsMap = {};
+ fetch(
+ "/api.php?" +
+ $.param({
+ method: "storage.set",
+ user_id: (window).UserID,
+ key: "april-fools",
+ token: (window).ForumAPITicket,
+ value: JSON.stringify(states),
+ timer: 24 * 60,
+ })
+ );
+ }
- result['core'] = this.core_settings;
+ /**
+ * Загрузка пользовательских настроек
+ */
+ async loadUserStates() {
+ const response = await fetch(
+ "/api.php?" +
+ $.param({
+ method: "storage.get",
+ user_id: (window).UserID,
+ key: "april-fools",
+ })
+ );
+ const json = await response.json();
- for (const entries of Object.entries(this.jokes)) {
- const id = entries[0];
- const joke = entries[1];
-
- result[id] = joke.settings;
- }
-
- return result;
+ if (json.error) {
+ return;
}
- /**
- * Запись новых настроек
- *
- * @param settings
- */
- set settings(settings: SettingsMap) {
- for (const entry of Object.entries(settings)) {
- const id = entry[0];
- const jokeSettings = entry[1];
- const targetSettings = (id === 'core') ? this.core_settings : this.jokes[id].settings;
+ const data = JSON.parse(json.response.storage.data["april-fools"]);
- Object.assign(targetSettings, jokeSettings);
- }
+ for (const key of Object.keys(this.jokes)) {
+ this.jokes[key].settings.enabled = data[key];
}
-
- /**
- * Разблокировать систему
- */
- enable() {
- this.core_settings.enabled = true;
- }
-
- /**
- * Заблокировать систему
- */
- disable() {
- this.core_settings.enabled = false;
- }
-
- /**
- * Разблокировать все шутки
- */
- enableAll() {
- for (const key of Object.keys(this.jokes)) {
- this.jokes[key].enable();
- }
- }
-
- /**
- * Заблокировать все шутки
- */
- disableAll() {
- for (const key of Object.keys(this.jokes)) {
- this.jokes[key].disable();
- }
+ }
+
+ async clearUserStates() {
+ await fetch(
+ "/api.php?" +
+ $.param({
+ method: "storage.delete",
+ user_id: (window).UserID,
+ key: "april-fools",
+ token: (window).ForumAPITicket,
+ })
+ );
+
+ return;
+ }
+
+ /**
+ * Запуск шутки
+ *
+ * @param id
+ * @param {boolean} forced
+ */
+ start(id: string, forced: boolean = false) {
+ if (!(id in this.jokes)) return;
+
+ const joke = this.jokes[id];
+
+ if (
+ !forced &&
+ (!joke.settings.enabled || !this.check(joke.settings.chance))
+ ) {
+ return;
}
- /**
- * Переключение шутки
- */
- toggleJoke(joke_id: string, state: boolean) {
- const joke = this.jokes[joke_id];
- joke.settings.enabled = state;
+ joke.start();
+ }
- if (joke instanceof StopableJoke && !state) {
- joke.stop();
- }
+ /**
+ * Запуск всех шуток
+ */
+ startAll(forced: boolean = false): void {
+ for (const key of Object.keys(this.jokes)) {
+ this.start(key, forced);
+ }
+ }
- this.saveUserStates();
+ regularCheck(): boolean {
+ if (!this.core_settings.enabled) {
+ return false;
}
- /**
- * Сохранение пользовательских настроек
- */
- saveUserStates() {
- const states: any = {};
-
- for (const key of Object.keys(this.jokes)) {
- states[key] = this.jokes[key].settings.enabled;
- }
-
- fetch('/api.php?' + $.param({
- method: 'storage.set',
- user_id: ( window).UserID,
- key: 'april-fools',
- token: ( window).ForumAPITicket,
- value: JSON.stringify(states),
- timer: 24 * 60
- }));
+ if (this.core_settings.testers.indexOf((window).UserID) >= 0) {
+ return true;
}
- /**
- * Загрузка пользовательских настроек
- */
- async loadUserStates() {
- const response = await fetch('/api.php?' + $.param({
- method: 'storage.get',
- user_id: ( window).UserID,
- key: 'april-fools',
- }));
- const json = await response.json();
+ const today = new Date();
+ const month = today.getMonth() + 1;
+ const day = today.getDate();
- if (json.error) {
- return;
- }
-
- const data = JSON.parse(json.response.storage.data['april-fools']);
-
- for (const key of Object.keys(this.jokes)) {
- this.jokes[key].settings.enabled = data[key];
- }
+ if (month !== 4 || day !== 1) {
+ return false;
}
- async clearUserStates() {
- await fetch('/api.php?' + $.param({
- method: 'storage.delete',
- user_id: ( window).UserID,
- key: 'april-fools',
- token: ( window).ForumAPITicket,
- }));
+ return this.check(this.core_settings.chance);
+ }
- return;
+ async regularStart() {
+ if (!this.regularCheck()) {
+ return;
}
+ await this.loadUserStates();
+ this.ui.show();
+ this.startAll();
+ }
- /**
- * Запуск шутки
- *
- * @param id
- * @param {boolean} forced
- */
- start(id: string, forced: boolean = false) {
- if (!(id in this.jokes))
- return;
+ /**
+ * Общие настройки системы
+ */
+ core_settings = new CoreSettings();
- const joke = this.jokes[id];
-
- if (!forced && (!joke.settings.enabled || !this.check(joke.settings.chance))) {
- return;
- }
-
- joke.start();
- }
-
- /**
- * Запуск всех шуток
- */
- startAll(forced: boolean = false): void {
- for (const key of Object.keys(this.jokes)) {
- this.start(key, forced);
- }
- }
-
- regularCheck(): boolean {
- if (!this.core_settings.enabled) {
- return false;
- }
-
- if (this.core_settings.testers.indexOf(( window).UserID) >= 0) {
- return true;
- }
-
- const today = new Date();
- const month = today.getMonth() + 1;
- const day = today.getDate();
-
- if (month !== 4 || day !== 1) {
- return false;
- }
-
- return this.check(this.core_settings.chance);
- }
-
- async regularStart() {
- if (!this.regularCheck()) {
- return;
- }
-
- await this.loadUserStates();
- this.ui.show();
- this.startAll();
- }
-
- /**
- * Общие настройки системы
- */
- core_settings = new CoreSettings;
-
- /**
- * Перечисление всех шуток
- */
- jokes: JokeMap = {}
+ /**
+ * Перечисление всех шуток
+ */
+ jokes: JokeMap = {};
}
/**
* Объект хранения шуток
*/
interface JokeMap {
- [key: string]: Joke;
+ [key: string]: Joke;
}
/**
* Объект задания настроек
*/
interface SettingsMap {
- [key: string]: JokeSettings;
+ [key: string]: JokeSettings;
}
class CoreSettings implements JokeSettings {
- chance = 100;
- enabled = true;
+ chance = 100;
+ enabled = true;
- testers: number[] = [];
-}
\ No newline at end of file
+ testers: number[] = [];
+}
diff --git a/src/jokes/carnival.ts b/src/jokes/carnival.ts
index 47873b5..10cf3f4 100644
--- a/src/jokes/carnival.ts
+++ b/src/jokes/carnival.ts
@@ -4,65 +4,70 @@ import { JokerClass } from "../joker";
/**
* **Карнавал**
- *
+ *
* Аватарки приобретают интересные цвета
- *
+ *
* @author Kozhilya
*/
export class CarnivalJoke extends Joke {
- id = 'carnival';
+ id = "carnival";
- title = 'Карнавал';
+ title = "Карнавал";
- description = 'Аватарки приобретают интересные цвета';
+ description = "Аватарки приобретают интересные цвета";
- settings = new CarnivalJokeSettings;
-
- start(): void {
- $(this.settings.selector).each((_, elem) => {
- const filters: string[] = [];
+ settings = new CarnivalJokeSettings();
- for (const entry of Object.entries(this.settings.variants)) {
- if (!this.check(this.settings.variant_chance)) {
- continue;
- }
+ start(): void {
+ $(this.settings.selector).each((_, elem) => {
+ const filters: string[] = [];
- filters.push(entry[0].replace('{0}', Math.round(Math.random() * entry[1]).toString()));
- }
+ for (const entry of Object.entries(this.settings.variants)) {
+ if (!this.check(this.settings.variant_chance)) {
+ continue;
+ }
- if (filters.length > 0) {
- $(elem).css({'filter': filters.join(' ')});
- }
- });
- }
+ filters.push(
+ entry[0].replace(
+ "{0}",
+ Math.round(Math.random() * entry[1]).toString()
+ )
+ );
+ }
+
+ if (filters.length > 0) {
+ $(elem).css({ filter: filters.join(" ") });
+ }
+ });
+ }
}
export class CarnivalJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = true;
- chance: number = 30;
+ chance: number = 30;
- /**
- * Селектор всех элементов, которые будут перекрашены
- */
- selector: string = 'img[src*="/avatars/"]';
+ /**
+ * Селектор всех элементов, которые будут перекрашены
+ */
+ selector: string = 'img[src*="/avatars/"]';
- /**
- * Вероятность применения некоторого фильтра
- */
- variant_chance: number = 20;
+ /**
+ * Вероятность применения некоторого фильтра
+ */
+ variant_chance: number = 20;
- /**
- * Список фильтров и диапозона случайных параметров
- */
- variants: CarnivalJokeFilters = {
- 'sepia({0}%)': 100,
- 'saturate({0}%)': 100,
- 'hue-rotate({0}deg)': 360,
- 'invert({0}%)': 100,
- }
+ /**
+ * Список фильтров и диапозона случайных параметров
+ */
+ variants: CarnivalJokeFilters = {
+ "sepia({0}%)": 100,
+ "saturate({0}%)": 100,
+ "hue-rotate({0}deg)": 360,
+ "invert({0}%)": 100,
+ };
}
export interface CarnivalJokeFilters {
- [key: string]: number;
-}
\ No newline at end of file
+ [key: string]: number;
+}
diff --git a/src/jokes/crazy_letters.ts b/src/jokes/crazy_letters.ts
index 2b0f91e..bfff8b1 100644
--- a/src/jokes/crazy_letters.ts
+++ b/src/jokes/crazy_letters.ts
@@ -3,41 +3,41 @@ import $ from "jquery";
/**
* **Мама, Я сОшЛа С уМа!**
- *
+ *
* иМеНа ЗаБоРчИкОм
- *
+ *
* @author Kozhilya
*/
export class CrazyLettersJoke extends Joke {
- id = 'crazy_letters';
+ id = "crazy_letters";
- title = 'Мама, Я сОшЛа С уМа!';
+ title = "Мама, Я сОшЛа С уМа!";
- description = 'иМеНа ЗаБоРчИкОм';
+ description = "иМеНа ЗаБоРчИкОм";
- settings = new CrazyLettersJokeSettings;
+ settings = new CrazyLettersJokeSettings();
- start(): void {
- $(this.settings.selector).each((_, elem) => {
- let text = $(this).text();
- let result = '';
+ start(): void {
+ $(this.settings.selector).each((_, elem) => {
+ let text = $(this).text();
+ let result = "";
- for (let i = 0; i < text.length; i++) {
- result += text[i][(i % 2) ? 'toLowerCase' : 'toUpperCase']();
- }
+ for (let i = 0; i < text.length; i++) {
+ result += text[i][i % 2 ? "toLowerCase" : "toUpperCase"]();
+ }
- $(this).text(result);
- });
- }
+ $(this).text(result);
+ });
+ }
}
export class CrazyLettersJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = true;
- chance: number = 20;
+ chance: number = 20;
- /**
- * Селектор всех элементов, которые будут затронуты
- */
- selector: string = '.pa-author a';
-}
\ No newline at end of file
+ /**
+ * Селектор всех элементов, которые будут затронуты
+ */
+ selector: string = ".pa-author a";
+}
diff --git a/src/jokes/cursor.ts b/src/jokes/cursor.ts
index 298c82a..a98efc1 100644
--- a/src/jokes/cursor.ts
+++ b/src/jokes/cursor.ts
@@ -6,319 +6,333 @@ const cursor_effects = require("cursor-effects");
/**
* **Красивый курсор**
- *
+ *
* Эффекты, следующие за курсором
- *
+ *
* @author Tim Holman
* @link https://tholman.com/cursor-effects/
*/
export class CursorEffectsJoke extends StopableJoke {
- id = 'cursor_effects';
+ id = "cursor_effects";
- title = 'Красивый курсор';
+ title = "Красивый курсор";
- description = 'Эффекты, следующие за курсором';
+ description = "Эффекты, следующие за курсором";
- settings = new CursorEffectsJokeSettings;
+ settings = new CursorEffectsJokeSettings();
- active_cursor_effect: any;
-
- /**
- * Разблокировать все варианты курсоров
- */
- enableAll() {
- for (const key of Object.keys(this.settings.effects)) {
- this.settings.effects[ key].enabled = true;
- }
- }
-
- /**
- * Заблокировать все варианты курсоров
- */
- disableAll() {
- for (const key of Object.keys(this.settings.effects)) {
- this.settings.effects[ key].enabled = false;
- }
+ active_cursor_effect: any;
+
+ /**
+ * Разблокировать все варианты курсоров
+ */
+ enableAll() {
+ for (const key of Object.keys(this.settings.effects)) {
+ this.settings.effects[key].enabled = true;
+ }
+ }
+
+ /**
+ * Заблокировать все варианты курсоров
+ */
+ disableAll() {
+ for (const key of Object.keys(this.settings.effects)) {
+ this.settings.effects[key].enabled = false;
+ }
+ }
+
+ private weight(effect: CursorEffectsEnum): number {
+ const s = this.settings.effects[effect];
+ return s.enabled ? s.weight : 0;
+ }
+
+ getRandomEffect(): CursorEffectsEnum {
+ const elements = Object.values(CursorEffectsEnum).map(
+ (e) => e
+ );
+ const totalWeight = elements.reduce(
+ (sum, element) => sum + this.weight(element),
+ 0
+ );
+ const randomWeight = Math.random() * totalWeight;
+
+ let currentWeight = 0;
+ for (const element of elements) {
+ currentWeight += this.weight(element);
+ if (randomWeight <= currentWeight) {
+ return element;
+ }
}
- private weight(effect: CursorEffectsEnum): number {
- const s = this.settings.effects[effect];
- return s.enabled ? s.weight : 0;
- }
+ throw new Error("Тебя быть тут не должно...");
+ }
- getRandomEffect(): CursorEffectsEnum {
- const elements = Object.values(CursorEffectsEnum).map(e => e);
- const totalWeight = elements.reduce((sum, element) => sum + this.weight(element), 0);
- const randomWeight = Math.random() * totalWeight;
-
- let currentWeight = 0;
- for (const element of elements) {
- currentWeight += this.weight(element);
- if (randomWeight <= currentWeight) {
- return element;
- }
- }
+ start(): void {
+ const effect = this.getRandomEffect();
+ const settings = this.settings.effects[effect];
- throw new Error("Тебя быть тут не должно...");
- }
-
- start(): void {
- const effect = this.getRandomEffect();
- const settings = this.settings.effects[effect];
+ this.active_cursor_effect = resolveCursorEffectsEnum(effect, settings);
+ }
- this.active_cursor_effect = resolveCursorEffectsEnum(effect, settings);
- }
-
- stop(): void {
- this.active_cursor_effect.destroy();
- }
+ stop(): void {
+ this.active_cursor_effect.destroy();
+ }
}
enum CursorEffectsEnum {
- Rainbow = 'rainbow',
- FairyDust = 'fairy_dust',
- SpringyEmoji = 'springy_emoji',
- Emoji = 'emoji',
- Ghost = 'ghost',
- Trailing = 'trailing',
- TextFlag = 'text_flag',
- FollowingDot = 'following_dot',
- Bubbles = 'bubbles',
- Snowflake = 'snowflake',
- Clock = 'clock'
+ Rainbow = "rainbow",
+ FairyDust = "fairy_dust",
+ SpringyEmoji = "springy_emoji",
+ Emoji = "emoji",
+ Ghost = "ghost",
+ Trailing = "trailing",
+ TextFlag = "text_flag",
+ FollowingDot = "following_dot",
+ Bubbles = "bubbles",
+ Snowflake = "snowflake",
+ Clock = "clock",
}
-function resolveCursorEffectsEnum(effect: CursorEffectsEnum|string, options: CursorEffectsSetting): any {
- switch (effect) {
- case CursorEffectsEnum.Rainbow:
- return cursor_effects.rainbowCursor(options);
+function resolveCursorEffectsEnum(
+ effect: CursorEffectsEnum | string,
+ options: CursorEffectsSetting
+): any {
+ switch (effect) {
+ case CursorEffectsEnum.Rainbow:
+ return cursor_effects.rainbowCursor(options);
- case CursorEffectsEnum.FairyDust:
- return cursor_effects.fairyDustCursor(options);
+ case CursorEffectsEnum.FairyDust:
+ return cursor_effects.fairyDustCursor(options);
- case CursorEffectsEnum.SpringyEmoji:
- return cursor_effects.springyEmojiCursor(options);
+ case CursorEffectsEnum.SpringyEmoji:
+ return cursor_effects.springyEmojiCursor(options);
- case CursorEffectsEnum.Emoji:
- return cursor_effects.emojiCursor(options);
+ case CursorEffectsEnum.Emoji:
+ return cursor_effects.emojiCursor(options);
- case CursorEffectsEnum.Ghost:
- return cursor_effects.ghostCursor(options);
+ case CursorEffectsEnum.Ghost:
+ return cursor_effects.ghostCursor(options);
- case CursorEffectsEnum.Trailing:
- return cursor_effects.trailingCursor(options);
+ case CursorEffectsEnum.Trailing:
+ return cursor_effects.trailingCursor(options);
- case CursorEffectsEnum.TextFlag:
- return cursor_effects.textFlag(options);
+ case CursorEffectsEnum.TextFlag:
+ return cursor_effects.textFlag(options);
- case CursorEffectsEnum.FollowingDot:
- return cursor_effects.followingDotCursor(options);
+ case CursorEffectsEnum.FollowingDot:
+ return cursor_effects.followingDotCursor(options);
- case CursorEffectsEnum.Bubbles:
- return cursor_effects.bubbleCursor(options);
+ case CursorEffectsEnum.Bubbles:
+ return cursor_effects.bubbleCursor(options);
- case CursorEffectsEnum.Snowflake:
- return cursor_effects.snowflakeCursor(options);
+ case CursorEffectsEnum.Snowflake:
+ return cursor_effects.snowflakeCursor(options);
- case CursorEffectsEnum.Clock:
- return cursor_effects.clockCursor(options);
- }
+ case CursorEffectsEnum.Clock:
+ return cursor_effects.clockCursor(options);
+ }
}
export class CursorEffectsJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = true;
- chance: number = 100;
+ chance: number = 100;
- effects: { [key in CursorEffectsEnum]?: CursorEffectsSetting } = {
+ effects: { [key in CursorEffectsEnum]?: CursorEffectsSetting } = {
+ /**
+ * Радужный след за курсором
+ */
+ [CursorEffectsEnum.Rainbow]:
+ new (class extends DefaultCursorEffectsSetting {
/**
- * Радужный след за курсором
+ * Длина полосы
*/
- [CursorEffectsEnum.Rainbow]: new class extends DefaultCursorEffectsSetting {
- /**
- * Длина полосы
- */
- length: number = 20;
-
- /**
- * Ширина полосы
- */
- size: number = 3;
-
- /**
- * Цвета полос (сверху вниз)
- */
- colors: string[] = [
- "#9400D3",
- "#4B0082",
- "#0000FF",
- "#00FF00",
- "#FFFF00",
- "#FF7F00",
- "#FF0000"
- ];
- },
+ length: number = 20;
/**
- * Цепочка из эмодзи "на резинке" под курсором
+ * Ширина полосы
*/
- [CursorEffectsEnum.SpringyEmoji]: new class extends DefaultCursorEffectsSetting {
- /**
- * Висящий эмодзи
- */
- emoji: string = "🤪"
- },
+ size: number = 3;
/**
- * Падающие от курсора эмодзи
+ * Цвета полос (сверху вниз)
*/
- [CursorEffectsEnum.Emoji]: new class extends DefaultCursorEffectsSetting {
- /**
- * Падающие эмодзи
- */
- emoji: string[] = ["🔥", "🐬", "🦆"]
- },
+ colors: string[] = [
+ "#9400D3",
+ "#4B0082",
+ "#0000FF",
+ "#00FF00",
+ "#FFFF00",
+ "#FF7F00",
+ "#FF0000",
+ ];
+ })(),
+
+ /**
+ * Цепочка из эмодзи "на резинке" под курсором
+ */
+ [CursorEffectsEnum.SpringyEmoji]:
+ new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Висящий эмодзи
+ */
+ emoji: string = "🤪";
+ })(),
+
+ /**
+ * Падающие от курсора эмодзи
+ */
+ [CursorEffectsEnum.Emoji]: new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Падающие эмодзи
+ */
+ emoji: string[] = ["🔥", "🐬", "🦆"];
+ })(),
+
+ /**
+ * Падающие от курсора пылинки
+ */
+ [CursorEffectsEnum.FairyDust]:
+ new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Цвет пылинок
+ */
+ colors: string[] = ["#ff0000", "#00ff00", "#0000ff"];
+ })(),
+
+ /**
+ * След "зависших" курсоров
+ */
+ [CursorEffectsEnum.Ghost]: new DefaultCursorEffectsSetting(),
+
+ /**
+ * След курсоров "на резинке"
+ */
+ [CursorEffectsEnum.Trailing]:
+ new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Количество элементов
+ */
+ particles: number = 15;
/**
- * Падающие от курсора пылинки
+ * Скорость частиц
*/
- [CursorEffectsEnum.FairyDust]: new class extends DefaultCursorEffectsSetting {
- /**
- * Цвет пылинок
- */
- colors: string[] = ["#ff0000", "#00ff00", "#0000ff"];
- },
+ rate: number = 0.4;
/**
- * След "зависших" курсоров
+ * Ссылка на картинку с курсором
+ * Используйте `null`, что бы использовать картинку курсора по умолчанию
*/
- [CursorEffectsEnum.Ghost]: new DefaultCursorEffectsSetting,
+ baseImageSrc: string | null = null;
+ })(),
+
+ /**
+ * Текст, развивающийся флагом от курсора
+ */
+ [CursorEffectsEnum.TextFlag]:
+ new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Отображаемый текст
+ */
+ text: string = "С днём смеха!";
/**
- * След курсоров "на резинке"
+ * Цвет текста
*/
- [CursorEffectsEnum.Trailing]: new class extends DefaultCursorEffectsSetting {
- /**
- * Количество элементов
- */
- particles: number = 15;
-
- /**
- * Скорость частиц
- */
- rate: number = 0.4;
-
- /**
- * Ссылка на картинку с курсором
- * Используйте `null`, что бы использовать картинку курсора по умолчанию
- */
- baseImageSrc: string | null = null;
- },
+ color: string = "#000000";
/**
- * Текст, развивающийся флагом от курсора
+ * Шрифт текста
*/
- [CursorEffectsEnum.TextFlag]: new class extends DefaultCursorEffectsSetting {
- /**
- * Отображаемый текст
- */
- text: string = "С днём смеха!";
-
- /**
- * Цвет текста
- */
- color: string = "#000000";
-
- /**
- * Шрифт текста
- */
- font: string = "monospace";
-
- /**
- * Размер текста
- */
- textSize: number = 12;
-
- /**
- * Растояние между бквами
- */
- gap: number = 14;
-
- /**
- * Размер... чего-то.
- */
- size: number = 3;
-
- },
+ font: string = "monospace";
/**
- * Точка, преследующая курсор
+ * Размер текста
*/
- [CursorEffectsEnum.FollowingDot]: new class extends DefaultCursorEffectsSetting {
- /**
- * Цвет догоняющей точки
- */
- color: string = "#323232a6";
- },
-
- /**
- * Пузырьки от курсора
- */
- [CursorEffectsEnum.Bubbles]: new DefaultCursorEffectsSetting,
+ textSize: number = 12;
/**
- * Снежинки, летящие от курсора
- *
- * Отключено, потому что сейчас не зима :D
+ * Растояние между бквами
*/
- [CursorEffectsEnum.Snowflake]: new class extends DefaultCursorEffectsSetting {
- weight: number = 0;
- },
+ gap: number = 14;
/**
- * Циферблат вокруг курсора
+ * Размер... чего-то.
*/
- [CursorEffectsEnum.Clock]: new class extends DefaultCursorEffectsSetting {
- /**
- * Цвет даты
- */
- dateColor: string = "blue";
+ size: number = 3;
+ })(),
- /**
- * Цвет циферблата
- */
- faceColor: string = "black";
-
- /**
- * Цвет секундной стрелки
- */
- secondsColor: string = "red";
-
- /**
- * Цвет минутной стрелки
- */
- minutesColor: string = "black";
-
- /**
- * Цвет часовой стрелки
- */
- hoursColor: string = "black";
- },
- };
+ /**
+ * Точка, преследующая курсор
+ */
+ [CursorEffectsEnum.FollowingDot]:
+ new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Цвет догоняющей точки
+ */
+ color: string = "#323232a6";
+ })(),
+
+ /**
+ * Пузырьки от курсора
+ */
+ [CursorEffectsEnum.Bubbles]: new DefaultCursorEffectsSetting(),
+
+ /**
+ * Снежинки, летящие от курсора
+ *
+ * Отключено, потому что сейчас не зима :D
+ */
+ [CursorEffectsEnum.Snowflake]:
+ new (class extends DefaultCursorEffectsSetting {
+ weight: number = 0;
+ })(),
+
+ /**
+ * Циферблат вокруг курсора
+ */
+ [CursorEffectsEnum.Clock]: new (class extends DefaultCursorEffectsSetting {
+ /**
+ * Цвет даты
+ */
+ dateColor: string = "blue";
+
+ /**
+ * Цвет циферблата
+ */
+ faceColor: string = "black";
+
+ /**
+ * Цвет секундной стрелки
+ */
+ secondsColor: string = "red";
+
+ /**
+ * Цвет минутной стрелки
+ */
+ minutesColor: string = "black";
+
+ /**
+ * Цвет часовой стрелки
+ */
+ hoursColor: string = "black";
+ })(),
+ };
}
interface CursorEffectsSetting {
- [key: string]: any;
+ [key: string]: any;
}
class DefaultCursorEffectsSetting implements CursorEffectsSetting {
- /**
- * Флаг возможности срабатывания
- */
- enabled: boolean = true;
+ /**
+ * Флаг возможности срабатывания
+ */
+ enabled: boolean = true;
- /**
- * Весовой коэффициент вероятности курсора
- */
- weight: number = 50;
+ /**
+ * Весовой коэффициент вероятности курсора
+ */
+ weight: number = 50;
}
diff --git a/src/jokes/humanitarian_hell.ts b/src/jokes/humanitarian_hell.ts
index 658de9d..5869a6d 100644
--- a/src/jokes/humanitarian_hell.ts
+++ b/src/jokes/humanitarian_hell.ts
@@ -1,38 +1,45 @@
import { Joke, JokeSettings, StopableJoke } from "../joke";
+import { getTextNodes } from "../common";
import $ from "jquery";
/**
* **Ад гуманитария**
- *
+ *
* Все "тся" заменяются на "ться", а все "ться" на "тся"
- *
+ *
* @author Kozhilya
*/
export class HumanitarianHellJoke extends Joke {
- id = 'humanitarian_hell';
+ id = "humanitarian_hell";
- title = 'Ад гуманитария';
+ title = "Ад гуманитария";
- description = 'Все "тся" заменяются на "ться", а все "ться" на "тся"';
-
- settings = new HumanitarianHellJokeSettings;
+ description = 'Все "тся" заменяются на "ться", а все "ться" на "тся"';
- start(): void {
- $(this.settings.selector).each(function () {
- $(this).html($(this).html().replace(/(ть?ся)/g, function (fnd) {
- return (fnd.indexOf('ь') > 0) ? 'тся' : 'ться';
- }));
- });
- }
+ settings = new HumanitarianHellJokeSettings();
+
+ processText(text: string): string {
+ return text.replace(/(ть?ся)/g, function (fnd) {
+ return fnd.indexOf("ь") > 0 ? "тся" : "ться";
+ });
+ }
+
+ start(): void {
+ const textNodes = getTextNodes(this.settings.selector);
+
+ textNodes.each((_, elem) => {
+ elem.textContent = this.processText(elem.textContent);
+ });
+ }
}
export class HumanitarianHellJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = true;
- chance: number = 20;
+ chance: number = 20;
- /**
- * Селектор всех элементов, которые будут затронуты
- */
- selector: string = '.post-content';
-}
\ No newline at end of file
+ /**
+ * Селектор всех элементов, которые будут затронуты
+ */
+ selector: string = ".post-content";
+}
diff --git a/src/jokes/letter_chaos.ts b/src/jokes/letter_chaos.ts
index 771e023..c53c469 100644
--- a/src/jokes/letter_chaos.ts
+++ b/src/jokes/letter_chaos.ts
@@ -1,68 +1,79 @@
import { Joke, JokeSettings, StopableJoke } from "../joke";
+import { getTextNodes } from "../common";
import $ from "jquery";
import { JokerClass } from "../joker";
/**
* **Буквенный хаос**
- *
- * Все буквы в словах, кроме первой и последней, перемешены.
- *
+ *
+ * Все буквы в словах, кроме первой и последней, перемешены
+ *
* @author Kozhilya
*/
export class LetterChaosJoke extends Joke {
- id = 'letter_chaos';
+ id = "letter_chaos";
- title = 'Буквенный хаос';
+ title = "Буквенный хаос";
- description = 'Все буквы в словах, кроме первой и последней, перемешены.';
+ description = "Все буквы в словах, кроме первой и последней, перемешены";
- settings = new LetterChaosJokeSettings;
+ settings = new LetterChaosJokeSettings();
- punctuationSymbols: string[] | null = null;
+ punctuationSymbols: string[] | null = null;
- private shuffleLetters(word: string): string {
- // Check if the word has two or more letters
- if ((word.length < 3) || !this.check(this.settings.shuffle_chance)) {
- return word;
- }
-
- // Split the word into an array of characters
- const characters = word.split("");
-
- // Shuffle the characters between the first and last letters
- const middleCharacters = characters.slice(1, -1);
- for (let i = middleCharacters.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1));
- [middleCharacters[i], middleCharacters[j]] = [middleCharacters[j], middleCharacters[i]];
- }
-
- // Join the shuffled characters back into a word
- return [characters[0], ...middleCharacters, characters[characters.length - 1]].join("");
+ private shuffleLetters(word: string): string {
+ // Check if the word has two or more letters
+ if (word.length < 3 || !this.check(this.settings.shuffle_chance)) {
+ return word;
}
- private processNode(node: any) {
- node.textContent = node.textContent?.replace(/([А-Яа-яA-Za-z]+)/gm, this.shuffleLetters);
+ // Split the word into an array of characters
+ const characters = word.split("");
+
+ // Shuffle the characters between the first and last letters
+ const middleCharacters = characters.slice(1, -1);
+ for (let i = middleCharacters.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [middleCharacters[i], middleCharacters[j]] = [
+ middleCharacters[j],
+ middleCharacters[i],
+ ];
}
- start(): void {
- const items = $(this.settings.selector).find('*').contents().filter((_, node) => node.nodeType === Node.TEXT_NODE);
+ // Join the shuffled characters back into a word
+ return [
+ characters[0],
+ ...middleCharacters,
+ characters[characters.length - 1],
+ ].join("");
+ }
- items.each((_, node) => this.processNode(node))
- }
+ private processNode(node: any) {
+ node.textContent = node.textContent?.replace(
+ /([А-Яа-яA-Za-z]+)/gm,
+ this.shuffleLetters
+ );
+ }
+
+ start(): void {
+ const textNodes = getTextNodes(this.settings.selector);
+
+ textNodes.each((_, node) => this.processNode(node));
+ }
}
export class LetterChaosJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = true;
- chance: number = 20;
+ chance: number = 20;
- /**
- * Селектор всех элементов, которые будут затронуты
- */
- selector: string = '.post-content';
+ /**
+ * Селектор всех элементов, которые будут затронуты
+ */
+ selector: string = ".post-content";
- /**
- * Вероятность того, что буквы будут перемешаны
- */
- shuffle_chance: number = 50;
+ /**
+ * Вероятность того, что буквы будут перемешаны
+ */
+ shuffle_chance: number = 50;
}
diff --git a/src/jokes/mirror.ts b/src/jokes/mirror.ts
index 53cd7a1..451805d 100644
--- a/src/jokes/mirror.ts
+++ b/src/jokes/mirror.ts
@@ -3,30 +3,34 @@ import $ from "jquery";
/**
* **Зеркало**
- *
+ *
* Элементы форума отражаются (по крайней мере, пытаются)
- *
+ *
* @author Kozhilya
*/
export class MirrorJoke extends Joke {
- id = 'mirror';
+ id = "mirror";
- title = 'Зеркало';
+ title = "Зеркало";
- description = 'Элементы форума отражаются (по крайней мере, пытаются)';
-
- settings = new MirrorJokeSettings;
+ description = "Элементы форума отражаются (по крайней мере, пытаются)";
- start(): void {
- $('.post, .post h3, .post-links, .post-author, .post-content')
- .css('transform', 'scaleX(-1)').css('-moz-transform', 'scaleX(-1)').css('-moz-transform', 'scaleX(-1)').css('filter', 'FlipH').css('-ms-filter', '"FlipH"');
- $('.post-links, .post-links ul, .post h3 span').css('margin-left', '0');
- $('.post h3 span').css('margin-right', '25em');
- }
+ settings = new MirrorJokeSettings();
+
+ start(): void {
+ $(".post, .post h3, .post-links, .post-author, .post-content")
+ .css("transform", "scaleX(-1)")
+ .css("-moz-transform", "scaleX(-1)")
+ .css("-moz-transform", "scaleX(-1)")
+ .css("filter", "FlipH")
+ .css("-ms-filter", '"FlipH"');
+ $(".post-links, .post-links ul, .post h3 span").css("margin-left", "0");
+ $(".post h3 span").css("margin-right", "25em");
+ }
}
export class MirrorJokeSettings implements JokeSettings {
- enabled: boolean = false;
+ enabled: boolean = false;
- chance: number = 30;
-}
\ No newline at end of file
+ chance: number = 30;
+}
diff --git a/src/jokes/shake.ts b/src/jokes/shake.ts
index d3542bc..49ec8e4 100644
--- a/src/jokes/shake.ts
+++ b/src/jokes/shake.ts
@@ -3,95 +3,95 @@ import $ from "jquery";
/**
* **Форумо-трясение**
- *
+ *
* Страница форума начинает трястись
- *
+ *
* @author Kozhilya
*/
export class ShakeJoke extends StopableJoke {
- id = 'shake';
+ id = "shake";
- title = 'Форумо-трясение';
+ title = "Форумо-трясение";
- description = 'Страница форума начинает трястись';
+ description = "Страница форума начинает трястись";
- settings = new ShakeJokeSettings;
+ settings = new ShakeJokeSettings();
- private interval_id: NodeJS.Timer;
+ private interval_id: NodeJS.Timer;
- private items: JQuery;
+ private items: JQuery;
- private data_name: string = 'april-fools-shake';
+ private data_name: string = "april-fools-shake";
- rand(a: number): number {
- return a + this.settings.force * (Math.random() * 2 - 1);
- }
+ rand(a: number): number {
+ return a + this.settings.force * (Math.random() * 2 - 1);
+ }
- start(): void {
- this.items = $(this.settings.selector);
+ start(): void {
+ this.items = $(this.settings.selector);
- this.items.each((_, elem) => {
- let data: ShakeJokeMargin = {};
+ this.items.each((_, elem) => {
+ let data: ShakeJokeMargin = {};
- this.settings.directions.forEach(d => {
- data[d] = parseInt($(elem).css('margin-' + d));
- });
+ this.settings.directions.forEach((d) => {
+ data[d] = parseInt($(elem).css("margin-" + d));
+ });
- $(elem).data(this.data_name, data);
- });
+ $(elem).data(this.data_name, data);
+ });
- this.interval_id = setInterval(() => {
- this.items.each((_, elem) => {
- const data: ShakeJokeMargin = $(elem).data(this.data_name);
+ this.interval_id = setInterval(() => {
+ this.items.each((_, elem) => {
+ const data: ShakeJokeMargin = $(elem).data(this.data_name);
- for (const entry of Object.entries(data)) {
- $(elem).css('margin-' + entry[0], this.rand(entry[1]))
- }
- });
- }, this.settings.interval);
- }
+ for (const entry of Object.entries(data)) {
+ $(elem).css("margin-" + entry[0], this.rand(entry[1]));
+ }
+ });
+ }, this.settings.interval);
+ }
- stop(): void {
- clearInterval(this.interval_id);
-
- this.items.each((_, elem) => {
- let data: ShakeJokeMargin = {};
+ stop(): void {
+ clearInterval(this.interval_id);
- this.settings.directions.forEach(d => {
- $(elem).css('margin-' + d, data[d]);
- });
+ this.items.each((_, elem) => {
+ let data: ShakeJokeMargin = {};
- $(elem).data(this.data_name, data);
- });
- }
+ this.settings.directions.forEach((d) => {
+ $(elem).css("margin-" + d, data[d]);
+ });
+
+ $(elem).data(this.data_name, data);
+ });
+ }
}
export class ShakeJokeSettings implements JokeSettings {
- enabled: boolean = false;
+ enabled: boolean = false;
- chance: number = 1;
+ chance: number = 1;
- /**
- * Направление тряски (тряска происходит путём изменения перечисленных направлений margin на значение ±force от стандартного значения)
- */
- directions: string[] = ['top', 'left', 'bottom', 'left'];
+ /**
+ * Направление тряски (тряска происходит путём изменения перечисленных направлений margin на значение ±force от стандартного значения)
+ */
+ directions: string[] = ["top", "left", "bottom", "left"];
- /**
- * Сила тряски - насколько отклоняется элемент от стандартного значения
- */
- force: number = 0.5;
+ /**
+ * Сила тряски - насколько отклоняется элемент от стандартного значения
+ */
+ force: number = 0.5;
- /**
- * Селектор всех элементов, которые будут трястись
- */
- selector: string = '[id^=pun]';
+ /**
+ * Селектор всех элементов, которые будут трястись
+ */
+ selector: string = "[id^=pun]";
- /**
- * Частота тряски
- */
- interval: number = 100;
+ /**
+ * Частота тряски
+ */
+ interval: number = 100;
}
export interface ShakeJokeMargin {
- [key: string]: number;
-}
\ No newline at end of file
+ [key: string]: number;
+}
diff --git a/src/jokes/stupid_title.ts b/src/jokes/stupid_title.ts
index 9ef6a3f..83ffba4 100644
--- a/src/jokes/stupid_title.ts
+++ b/src/jokes/stupid_title.ts
@@ -1,88 +1,99 @@
import { Joke, JokeSettings, StopableJoke } from "../joke";
import $ from "jquery";
+import { StupidTitleDefaultVariants } from "../data/stupid_title_variants";
/**
* **Глупые статусы**
- *
- * Каждому пользователю даётся новый глупый статус.
- *
+ *
+ * Каждому пользователю даётся новый глупый статус
+ *
* @author Kozhilya
*/
export class StupidTitleJoke extends Joke {
- id = 'stupid_title';
+ id = "stupid_title";
- title = 'Глупые статусы';
+ title = "Глупые статусы";
- description = 'Каждому пользователю даётся новый глупый статус.';
+ description = "Каждому пользователю даётся новый глупый статус";
- settings = new StupidTitleJokeSettings;
+ settings = new StupidTitleJokeSettings();
- private loadedGenders: any = null;
+ private loadedGenders: any = null;
- async loadGenders(): Promise {
- const names = $('.pa-author a')
- .map((_, elem) => elem.innerText).toArray()
- .reduce((arr: string[], name: string) => {
- if (arr.indexOf(name) < 0){
- arr.push(name);
- }
- return arr
- }, []);
-
- const url = '/api.php?' + $.param({ method: 'users.get', fields: 'username,sex', username: names });
- const response = await fetch(url);
- const json = await response.json();
-
- this.loadedGenders = {};
-
- if (json.error) {
- return;
+ async loadGenders(): Promise {
+ const names = $(".pa-author a")
+ .map((_, elem) => elem.innerText)
+ .toArray()
+ .reduce((arr: string[], name: string) => {
+ if (arr.indexOf(name) < 0) {
+ arr.push(name);
}
+ return arr;
+ }, []);
- json.response.users.forEach((element: any) => {
- if (element.sex === '0') {
- return;
- }
+ const url =
+ "/api.php?" +
+ $.param({ method: "users.get", fields: "username,sex", username: names });
+ const response = await fetch(url);
+ const json = await response.json();
- this.loadedGenders[element.username] = element.sex === '1';
- });
+ this.loadedGenders = {};
+
+ if (json.error) {
+ return;
}
- resolveGender(name: string): boolean {
- if (this.settings.gender_resolver !== null) {
- return !!this.settings.gender_resolver.call(this, name);
- }
+ json.response.users.forEach((element: any) => {
+ if (element.sex === "0") {
+ return;
+ }
- if (name in this.loadedGenders) {
- return !!this.loadedGenders[name];
- }
+ this.loadedGenders[element.username] = element.sex === "1";
+ });
+ }
+ resolveGender(name: string): boolean {
+ if (this.settings.gender_resolver !== null) {
+ return !!this.settings.gender_resolver.call(this, name);
+ }
+
+ if (name in this.loadedGenders) {
+ return !!this.loadedGenders[name];
+ }
+
+ this.resetSeed();
+ return this.selectRandom(name, { m: 1, f: 1 }) === "m";
+ }
+
+ private variantsGenerated = false;
+
+ async start(): Promise {
+ if (!this.variantsGenerated) {
+ StupidTitleDefaultVariants(this.settings.variants);
+ this.variantsGenerated = true;
+ }
+
+ await this.loadGenders();
+
+ const cache: any = {};
+
+ $(".post-author").each((_, root) => {
+ const name = $(".pa-author a", root).text();
+ if (!name) {
+ return;
+ }
+
+ if (!(name in cache)) {
this.resetSeed();
- return this.selectRandom(name, { 'm': 1, 'f': 1 }) === 'm';
- }
+ cache[name] = this.generatePhrase(name);
+ }
- async start(): Promise {
- await this.loadGenders();
+ $(".pa-title", root).text(cache[name]);
+ });
+ }
- const cache: any = {};
-
- $('.post-author').each((_, root) => {
- const name = $('.pa-author a', root).text();
- if (!name) {
- return;
- }
-
- if (!(name in cache)) {
- this.resetSeed();
- cache[name] = this.generatePhrase(name);
- }
-
- $('.pa-title', root).text(cache[name]);
- });
- }
-
- private prng(name: string, seed: number, limit: number): number {
- /*
+ private prng(name: string, seed: number, limit: number): number {
+ /*
https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
cyrb53 (c) 2018 bryc (github.com/bryc)
@@ -90,223 +101,230 @@ export class StupidTitleJoke extends Joke {
Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
Public domain. Attribution appreciated.
*/
- let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
- for (let i = 0, ch; i < name.length; i++) {
- ch = name.charCodeAt(i);
- h1 = Math.imul(h1 ^ ch, 2654435761);
- h2 = Math.imul(h2 ^ ch, 1597334677);
- }
- h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909);
- h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909);
-
- const value = 4294967296 * (2097151 & h2) + (h1>>>0);
-
- return Math.floor(Math.pow(Math.sin(value), 2) * limit) % limit;
+ let h1 = 0xdeadbeef ^ seed,
+ h2 = 0x41c6ce57 ^ seed;
+ for (let i = 0, ch; i < name.length; i++) {
+ ch = name.charCodeAt(i);
+ h1 = Math.imul(h1 ^ ch, 2654435761);
+ h2 = Math.imul(h2 ^ ch, 1597334677);
+ }
+ h1 =
+ Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
+ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
+ h2 =
+ Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
+ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
+
+ const value = 4294967296 * (2097151 & h2) + (h1 >>> 0);
+
+ return Math.floor(Math.pow(Math.sin(value), 2) * limit) % limit;
+ }
+
+ private seed = 0;
+
+ private resetSeed(): void {
+ const date = new Date();
+ const time = date.getUTCHours() * 60 + date.getUTCMinutes();
+
+ if (this.settings.change_frequency === 0) {
+ this.seed = time * 60 + date.getUTCSeconds();
+ return;
}
- private seed = 0;
+ this.seed = Math.floor(time / this.settings.change_frequency);
+ }
- private resetSeed(): void {
- const date = new Date();
- const time = new Date().getUTCHours() * 60 + new Date().getUTCMinutes();
- this.seed = Math.floor(time / this.settings.change_frequency);
+ private selectRandom(
+ name: string,
+ variants: StupidTitleWeightedVariants
+ ): string {
+ const elements = Object.keys(variants);
+ const totalWeight = elements.reduce(
+ (sum, element) => sum + variants[element],
+ 0
+ );
+ const randomWeight = this.prng(name, this.seed++, totalWeight);
- console.log('Seed reset:', time, this.seed);
+ let currentWeight = 0;
+ for (const element of elements) {
+ currentWeight += variants[element];
+ if (randomWeight <= currentWeight) {
+ return element;
+ }
}
- private selectRandom(name: string, variants: StupidTitleWeightedVariants): string {
- const elements = Object.keys(variants);
- const totalWeight = elements.reduce((sum, element) => sum + variants[element], 0);
- const randomWeight = this.prng(name, this.seed++, totalWeight);
-
- let currentWeight = 0;
- for (const element of elements) {
- currentWeight += variants[element];
- if (randomWeight <= currentWeight) {
- return element;
- }
- }
+ throw new Error("Тебя быть тут не должно...");
+ }
- throw new Error("Тебя быть тут не должно...");
+ private buildVariants(
+ type: string,
+ gender: boolean
+ ): StupidTitleWeightedVariants {
+ const result: StupidTitleWeightedVariants = {};
+ const variants: StupidTitleVariant = this.settings.variants[type];
+
+ for (const iterator of Object.entries(variants)) {
+ const word = iterator[0];
+ const wordSettings = iterator[1];
+
+ if (!wordSettings.enabled || wordSettings.gender === !gender) {
+ continue;
+ }
+
+ if (!this.settings.rude && wordSettings.rude) {
+ continue;
+ }
+
+ result[word] = wordSettings.weight;
}
- private buildVariants(type: string, gender: boolean): StupidTitleWeightedVariants {
- const result: StupidTitleWeightedVariants = {};
- const variants: StupidTitleVariant = this.settings.variants[type];
+ return result;
+ }
- for (const iterator of Object.entries(variants)) {
- const word = iterator[0];
- const wordSettings = iterator[1];
-
- if (!wordSettings.enabled || (wordSettings.gender === !gender)) {
- continue;
- }
+ private capitalizeFirst(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ }
- if (!this.settings.rude && wordSettings.rude) {
- continue;
- }
+ generatePhrase(name: string): string {
+ const gender = this.resolveGender(name);
+ const prhase = this.selectRandom(name, this.settings.schemas);
- result[word] = wordSettings.weight;
- }
-
- return result;
- }
+ return this.capitalizeFirst(
+ prhase.replace(/\{(.+?)\}/g, (_, type: string) => {
+ const variants = this.buildVariants(type, gender);
- private capitalizeFirst(str: string): string {
- return str.charAt(0).toUpperCase() + str.slice(1);
- }
-
- generatePhrase(name: string): string {
- const gender = this.resolveGender(name);
- const prhase = this.selectRandom(name, this.settings.schemas);
-
- return this.capitalizeFirst(prhase.replace(/\{(.+?)\}/g, (_, type: string) => {
- const variants = this.buildVariants(type, gender);
-
- return this.selectRandom(name, variants);
- }));
- }
+ return this.selectRandom(name, variants);
+ })
+ );
+ }
}
export class StupidTitleJokeSettings implements JokeSettings {
- enabled: boolean = true;
- chance: number = 100;
+ enabled: boolean = true;
+ chance: number = 100;
- /**
- * Определение пола по имени.
- * По умолчанию род устанавливается случайным образом
- *
- * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
- *
- * @param name Имя персонажа
- * @returns true, если мужской род; false, если женский род
- */
- gender_resolver: Function | null = null;
+ /**
+ * Определение пола по имени.
+ * По умолчанию род устанавливается случайным образом
+ *
+ * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
+ *
+ * @param name Имя персонажа
+ * @returns true, если мужской род; false, если женский род
+ */
+ gender_resolver: Function | null = null;
- /**
- * Разрешить возможно оскорбительные варианты
- */
- rude: boolean = false;
+ /**
+ * Разрешить возможно оскорбительные варианты
+ */
+ rude: boolean = false;
- /**
- * Частота смены статусов (в минутах)
- */
- change_frequency: number = 5;
+ /**
+ * Частота смены статусов (в минутах)
+ * Установите значение 0, что бы обновление происходило при каждом обновлении страницы
+ */
+ change_frequency: number = 5;
- /**
- * Разрешённые схемы формирования статусов
- *
- * {adj} - прилагательное
- * {noun} - имя существительно
- * {verb} - глагол
- * {adv} - наречие
- * {place} - дополнение места
- * {p} - знак препинания
- */
- schemas: StupidTitleWeightedVariants = {
- '{adj} {noun}{p}': 10,
- '{noun} {adj}{p}': 3,
- '{adj}, {adj} {noun}{p}': 10,
- '{adj} и очень {adj} {noun}{p}': 10,
- '{adj} {noun} {verb}{p}': 10,
- '{adj} {noun} {adv} {verb}{p}': 5,
- '{adj} {noun} {verb} {place}{p}': 5,
- '{adj} {noun} {adv} {verb} {place}{p}': 5,
- };
-
- /**
- * Вариант слов
- * Смотрите раздел описания скрипта, что бы настроить это поле
- */
- variants: StupidTitleVariants = {
- adj: {
- ...buildVariant(true, 'жесткий,^удачливый,низкий,знакомый,откровенный,^мирный,грамотный,вкусный,виртуальный,пышный,провинциальный,$жирный,заинтересованный,объективный,^оригинальный,пожилой,чистый,особенный,выгодный,громадный,обыкновенный,чужой,ударный,^солнечный,истинный,посторонний,южный,седой,$бесполезный,острый,пестрый,формальный,повседневный,невозможный,$жалкий,целый,$глупый,последний,выходной,серьезный,подземный,небесный,любимый,былой,семейный,далекий,северный,суровый,гигантский,иностранный,родной,конструктивный,показательный,нужный,чрезвычайный,гордый,банальный,открытый,независимый,запасной,мстительный,грубый,алчный,мелочный,хладнокровный,спокойный,терпеливый,креативный,активный,адекватный,застенчивый,опрятный,безнадёжный,уверенный в себе,доброжелательный,ласковый,робкий,воспитанный,самовлюблённый,деятельный,двуличный,верный'),
- ...buildVariant(false, 'жесткая,^удачливая,низкая,знакомая,откровенная,^мирная,грамотная,вкусная,виртуальная,пышная,провинциальная,$жирная,заинтересованная,объективная,^оригинальная,пожилая,чистая,особенная,выгодная,громадная,обыкновенная,чужая,ударная,^солнечная,истинная,посторонняя,южная,седая,$бесполезная,острая,пестрая,формальная,повседневная,невозможная,$жалкая,целая,$глупая,последняя,выходная,серьезная,подземная,небесная,любимая,былая,семейная,далекая,северная,суровая,гигантская,иностранная,родная,конструктивная,показательная,нужная,чрезвычайная,гордая,банальная,открытая,независимая,запасная,мстительная,грубая,алчная,мелочная,хладнокровная,спокаяная,терпеливая,креативная,активная,адекватная,застенчивая,опрятная,безнадёжная,уверенная в себе,доброжелательная,ласковая,робкая,воспитанная,самовлюблённая,деятельная,двуличная,верная'),
- },
- noun: {
- ...buildVariant(null, ''),
-
- ...buildVariant(true, 'орёл, ниндзя, манекен, пузырь, бродяга, рыцарь, кентавр, зайц, диван, наследник, сын, джентльмен, басурманин, карасик, $злоумышленник, $раб, хоббит, одиночка, отец, кролик, бруталище, дворф, актёр, %дед, спортсмен, $балбес, %мальчег, лев, ёж, организм, монах, хоббит, борец, тигр, монстр, массажист, мачо, эльф, ледник, жук, кот, зомби, проказник, дракон, носорог, волк, лесоруб, кит, %дедушка, динозавр, бард, кулак, принц, %сынок, мальчик, товарищ, ёжик, сурикат'),
-
- ...buildVariant(false, 'заноза, волторна, $личинка, детка, %бабушка, простуда, черепашка, мышь, волчица, деревяшка, пиранья, каруселька, красотка, леди, %дочка, орлица, принцесса, $ведьма, травка, актриса, наследница, %деффачка, горгулья, белочка, акула, машинка, конфетка, знаменитость, косточка, спортсменка, панда, мать, львица, эльфийка, $злоумышленница, чертовка, дворфиха, кошка, $рабыня, скамеечка, дочь, монашка, бактерия, рыбка, бусинка, $бабка, девочка, валькирия, килька, бабочка, милашка, крышка, тигрица, рыбка, русалочка'),
- },
- verb: {
- ...buildVariant(null, 'встаёт, снимает, приказывает, указывает, собирается, чувствует опасность, орёт, болеет, поздравляет, освобождает, поздравляет, бежит, берёт и делает, молчит, радуется, прощает, читает, снимает, снимается в фильме, что-то скрывает, думает, имеет план и придерживается его, тянет, работает, меняется, ловит, отказаться куда-то идти, заходит, замирает, оформляет, нарушает, отказывается что-то делать, шепчет, снимается в сериале, скрывается, учит, спрашивает, идёт, отказывается вставать, прибывает, отказывается, забывает, получает, плывёт, отвечает, поглядывает, говорит, просыпается, соединяет, оглядывается'),
-
- ...buildVariant(true, 'женится, женился, успел, не успел, смог, скрылся, заработался, познал дзен, присел, обиделся, проснулся, взял и сделал, додумался, не спал'),
-
- ...buildVariant(false, 'выходит замуж, вышла замуж, успела, не успела, смогла, скрылась, заработалась, познала дзен, присела, обиделась, проснулась, взяла и сделала, додумалась, не спала'),
- },
- place: {
- ...buildVariant(null, 'где-то, в пустыне, на пляже, под дождём, %под проливным дождём, в квартире, %в тёплой и уютной квартире, в космосе, в театре, в филармонии, на заснеженной вершине, на конференции, на форуме, в очереди, в лесу, в джунглях, под водой'),
- },
- adv: {
- ...buildVariant(null, 'счастливо, уныло, грустно, позже, немедленно, регулярно, вновь, опять, уже, внезапно, предсказуемо, медленно, быстро, %молниеносно, $грубо, безрассудно, осмысленно, расчётливо'),
- },
- p: {
- '': new StupidTitleSetting({ weight: 10 }),
- '.': new StupidTitleSetting({ weight: 10 }),
- '...': new StupidTitleSetting({ weight: 5 }),
- '!': new StupidTitleSetting({ weight: 5 }),
- '!!!': new StupidTitleSetting({ weight: 2 }),
- '?': new StupidTitleSetting({ weight: 2 }),
- '?!': new StupidTitleSetting({ weight: 1 }),
- },
- };
+ /**
+ * Разрешённые схемы формирования статусов
+ *
+ * {adj} - прилагательное
+ * {noun} - имя существительно
+ * {verb} - глагол
+ * {adv} - наречие
+ * {place} - дополнение места
+ * {p} - знак препинания
+ */
+ schemas: StupidTitleWeightedVariants = {
+ "{adj} {noun}{p}": 10,
+ "{noun} {adj}{p}": 3,
+ "{adj}, {adj} {noun}{p}": 10,
+ "{adj} и очень {adj} {noun}{p}": 10,
+ "{adj} {noun} {verb}{p}": 10,
+ "{adj} {noun} {adv} {verb}{p}": 5,
+ "{adj} {noun} {verb} {place}{p}": 5,
+ "{adj} {noun} {adv} {verb} {place}{p}": 5,
+ };
+ /**
+ * Вариант слов
+ * Смотрите раздел описания скрипта, что бы настроить это поле
+ */
+ variants: StupidTitleVariants = {
+ adj: {},
+ noun: {},
+ verb: {},
+ place: {},
+ adv: {},
+ p: {
+ "": new StupidTitleSetting({ weight: 10 }),
+ ".": new StupidTitleSetting({ weight: 10 }),
+ "...": new StupidTitleSetting({ weight: 5 }),
+ "!": new StupidTitleSetting({ weight: 5 }),
+ "!!!": new StupidTitleSetting({ weight: 2 }),
+ "?": new StupidTitleSetting({ weight: 2 }),
+ "?!": new StupidTitleSetting({ weight: 1 }),
+ },
+ };
}
-interface StupidTitleVariants {
- [key: string]: StupidTitleVariant
+export interface StupidTitleVariants {
+ [key: string]: StupidTitleVariant;
}
interface StupidTitleVariant {
- [key: string]: StupidTitleSetting
+ [key: string]: StupidTitleSetting;
}
interface StupidTitleWeightedVariants {
- [key: string]: number
+ [key: string]: number;
}
class StupidTitleSetting {
- weight: number = 10;
+ weight: number = 10;
- enabled: boolean = true;
-
- rude: boolean = false;
+ enabled: boolean = true;
- gender: boolean|null = null;
+ rude: boolean = false;
- constructor(options: any = new Object) {
- for (const iterator of Object.entries(options)) {
- if (iterator[0] in this) {
- ( this)[iterator[0]] = iterator[1];
- }
- }
+ gender: boolean | null = null;
+
+ constructor(options: any = new Object()) {
+ for (const iterator of Object.entries(options)) {
+ if (iterator[0] in this) {
+ (this)[iterator[0]] = iterator[1];
+ }
}
+ }
}
-function buildVariant(gender: boolean|null, input: string): StupidTitleVariant
-{
- const result: StupidTitleVariant = {};
- const words = input.split(',').map(word => word.trim());
-
- words.forEach(word => {
- const clean_word = word.replace(/[^А-ЯЁа-яёA-Za-z\- ]/g, '');
+export function BuildStupdiTitleVariant(
+ gender: boolean | null,
+ input: string
+): StupidTitleVariant {
+ const result: StupidTitleVariant = {};
+ const words = input.split(",").map((word) => word.trim());
- if (clean_word === '') {
- return;
- }
+ words.forEach((word) => {
+ const clean_word = word.replace(/[^А-ЯЁа-яёA-Za-z\- ]/g, "");
- result[clean_word] = new StupidTitleSetting({
- gender,
- rude: word.indexOf('$') >= 0,
- enabled: word.indexOf('!') < 0,
- weight: 10 * ((word.indexOf('$') >= 0) ? 0.5 : 1) * ((word.indexOf('%') >= 0) ? 0.5 : 1) * ((word.indexOf('^') >= 0) ? 2 : 1)
- });
+ if (clean_word === "") {
+ return;
+ }
+
+ result[clean_word] = new StupidTitleSetting({
+ gender,
+ rude: word.indexOf("$") >= 0,
+ enabled: word.indexOf("!") < 0,
+ weight:
+ 10 *
+ (word.indexOf("$") >= 0 ? 0.5 : 1) *
+ (word.indexOf("%") >= 0 ? 0.5 : 1) *
+ (word.indexOf("^") >= 0 ? 2 : 1),
});
+ });
- return result;
+ return result;
}
-(window as any).BuildStupdiTitleVariant = buildVariant;
\ No newline at end of file
+(window as any).BuildStupdiTitleVariant = BuildStupdiTitleVariant;
diff --git a/src/main.ts b/src/main.ts
index f364710..3f0fb7b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,24 +10,24 @@ import { CursorEffectsJoke } from "./jokes/cursor";
import { StupidTitleJoke } from "./jokes/stupid_title";
(window as any).AprilJokes = ((jokes: JokeClassList): JokerClass => {
- const cl = new JokerClass();
+ const cl = new JokerClass();
- jokes.forEach(joke => {
- cl.add(new joke());
- });
+ jokes.forEach((joke) => {
+ cl.add(new joke());
+ });
- $(() => cl.regularStart());
+ $(() => cl.regularStart());
- return cl;
+ return cl;
})([
- ShakeJoke,
- HumanitarianHellJoke,
- MirrorJoke,
- CarnivalJoke,
- CrazyLettersJoke,
- LetterChaosJoke,
- CursorEffectsJoke,
- StupidTitleJoke,
+ ShakeJoke,
+ HumanitarianHellJoke,
+ MirrorJoke,
+ CarnivalJoke,
+ CrazyLettersJoke,
+ LetterChaosJoke,
+ CursorEffectsJoke,
+ StupidTitleJoke,
]);
-interface JokeClassList extends Array Joke> {}
\ No newline at end of file
+interface JokeClassList extends Array Joke> {}
diff --git a/src/ui.ts b/src/ui.ts
index 7d67cdb..db3401c 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -5,58 +5,60 @@ import button_html from "./html/button.html";
import styles_html from "./html/styles.html";
import window_html from "./html/window.html";
-
export class JokerUI {
- joker: JokerClass;
+ joker: JokerClass;
- constructor(joker: JokerClass) {
- this.joker = joker;
+ constructor(joker: JokerClass) {
+ this.joker = joker;
- $('link[rel="stylesheet"]').eq(0).before(styles_html);
+ $('link[rel="stylesheet"]').eq(0).before(styles_html);
- const button = this.button;
+ const button = this.button;
- button.hide();
- (button).mybbModal({
- title: "Настройки первоапрельских шуток",
- content: () => this.render(),
- closer: true,
- noscroll: true,
- escClose: true,
- });
+ button.hide();
+ (button).mybbModal({
+ title: "Настройки первоапрельских шуток",
+ content: () => this.render(),
+ closer: true,
+ noscroll: true,
+ escClose: true,
+ });
+ }
+
+ get button() {
+ let button = $(".april-fools__button");
+
+ if (button.length === 0) {
+ button = $(button_html).appendTo("body");
}
- get button() {
- let button = $('.april-fools__button');
+ return button;
+ }
- if (button.length === 0) {
- button = $(button_html).appendTo('body');
- }
+ show() {
+ this.button.show();
+ }
- return button;
+ render(): JQuery {
+ const result: JQuery = $("").html(window_html);
+ const row_template = $("#april-fools__window-row", result).html();
+ const joke_container = $(".april-fools__jokes", result);
+
+ for (const joke of Object.values(this.joker.jokes)) {
+ const row = $(eval("`" + row_template + "`"));
+ $('input[type="checkbox"]', row)
+ .prop("checked", joke.settings.enabled)
+ .on("click", (e) =>
+ this.joker.toggleJoke(joke.id, (e).currentTarget.checked)
+ );
+ joke_container.append(row);
}
- show() {
- this.button.show();
- }
+ $("button", result).on("click", async () => {
+ await this.joker.clearUserStates();
+ window.location.reload();
+ });
- render(): JQuery {
- const result: JQuery = $('').html(window_html);
- const row_template = $('#april-fools__window-row', result).html();
- const joke_container = $('.april-fools__jokes', result);
-
- for (const joke of Object.values(this.joker.jokes)) {
- const row = $(eval('`' + row_template + '`'));
- $('input[type="checkbox"]', row).prop('checked', joke.settings.enabled)
- .on('click', e => this.joker.toggleJoke(joke.id, (e).currentTarget.checked));
- joke_container.append(row);
- }
-
- $('button', result).on('click', async () => {
- await this.joker.clearUserStates();
- window.location.reload();
- });
-
- return $('.april-fools__content', result);
- }
-}
\ No newline at end of file
+ return $(".april-fools__content", result);
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
index 27937c3..836bf50 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,13 +3,10 @@
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"module": "es6",
- "target": "es5",
+ "target": "es2017",
"allowJs": true,
"moduleResolution": "node",
- "typeRoots": [
- "./node_modules/@types",
- "./types"
- ]
+ "typeRoots": ["./node_modules/@types", "./types"]
},
"files": ["src/main.ts"]
-}
\ No newline at end of file
+}
diff --git a/webpack.config.js b/webpack.config.js
index 3a95cfb..7ed4bf5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -2,8 +2,9 @@
const path = require("path");
const webpack = require("webpack");
-const BomPlugin = require('webpack-utf8-bom');
-const { exec } = require('node:child_process');
+const BomPlugin = require("webpack-utf8-bom");
+const { exec } = require("node:child_process");
+const TerserPlugin = require("terser-webpack-plugin");
const isProduction = process.env.NODE_ENV == "production";
@@ -11,21 +12,21 @@ const config = {
entry: "./src/main.ts",
output: {
path: path.resolve(__dirname, "dist"),
- filename: () => ('april-fools' + (isProduction ? '.min' : '') + '.js'),
+ filename: () => "april-fools" + (isProduction ? ".min" : "") + ".js",
},
plugins: [
new BomPlugin(true),
{
apply: (compiler) => {
- compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
- exec('./upload.sh', (err, stdout, stderr) => {
+ compiler.hooks.afterEmit.tap("AfterEmitPlugin", (compilation) => {
+ exec("./upload.sh", (err, stdout, stderr) => {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
console.log("Uploaded.");
});
});
- }
- }
+ },
+ },
// Add your plugins here
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
],
@@ -52,11 +53,20 @@ const config = {
extensions: [".tsx", ".ts", ".jsx", ".js", "..."],
},
externals: {
- jquery: 'jQuery',
+ jquery: "jQuery",
},
watchOptions: {
aggregateTimeout: 20,
},
+ optimization: {
+ minimize: isProduction,
+ minimizer: [new TerserPlugin({
+ terserOptions: {
+ compress: true,
+ },
+ minify: TerserPlugin.uglifyJsMinify,
+ })],
+ },
};
module.exports = () => {
@@ -82,4 +92,4 @@ module.exports = () => {
"src/main.ts"
]
}
-*/
\ No newline at end of file
+*/