├── Resources └── Gif │ ├── example1.gif │ ├── example1a.gif │ ├── example1b.gif │ ├── example2.gif │ ├── example2a.gif │ ├── example2b.gif │ ├── example3.gif │ ├── example3a.gif │ ├── example3b.gif │ ├── example4.gif │ ├── example4a.gif │ ├── example4b.gif │ └── example4d.gif ├── WordWheel ├── wordwheel.css ├── README.md └── wordwheel.js ├── Notification ├── LICENSE ├── Version 2.36 or below │ ├── flash.min.css │ ├── flash.css │ ├── flash.min.js │ └── flash.js ├── Version 2.37 or above │ ├── flash.min.css │ ├── flash.css │ ├── flash.min.js │ └── flash.js └── README.md ├── LICENSE ├── README.md ├── Spoiler ├── spoiler.js └── README.md └── Dyslexia ├── README.md └── dyslexia.js /Resources/Gif/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example1.gif -------------------------------------------------------------------------------- /Resources/Gif/example1a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example1a.gif -------------------------------------------------------------------------------- /Resources/Gif/example1b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example1b.gif -------------------------------------------------------------------------------- /Resources/Gif/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example2.gif -------------------------------------------------------------------------------- /Resources/Gif/example2a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example2a.gif -------------------------------------------------------------------------------- /Resources/Gif/example2b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example2b.gif -------------------------------------------------------------------------------- /Resources/Gif/example3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example3.gif -------------------------------------------------------------------------------- /Resources/Gif/example3a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example3a.gif -------------------------------------------------------------------------------- /Resources/Gif/example3b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example3b.gif -------------------------------------------------------------------------------- /Resources/Gif/example4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example4.gif -------------------------------------------------------------------------------- /Resources/Gif/example4a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example4a.gif -------------------------------------------------------------------------------- /Resources/Gif/example4b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example4b.gif -------------------------------------------------------------------------------- /Resources/Gif/example4d.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjoerdHekking/custom-macros-sugarcube2/HEAD/Resources/Gif/example4d.gif -------------------------------------------------------------------------------- /WordWheel/wordwheel.css: -------------------------------------------------------------------------------- 1 | #outerCircleText { 2 | color: #fff; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | z-index: 3000; 7 | cursor: default; 8 | } 9 | 10 | #outerCircleText div { 11 | position: relative; 12 | } 13 | 14 | #outerCircleText div div { 15 | position: absolute; 16 | top: 0; 17 | left: 0; 18 | text-align: center; 19 | } -------------------------------------------------------------------------------- /Notification/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 betaWeb 4 | Copyright (c) 2022 SjoerdHekking 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # custom-macros-sugarcube2 2 | 3 | Small macros for Twine, SugarCube 2 4 | 5 | ## Installation 6 | 7 | Download this repository and pick which macros you need. Each macro comes with a `README.md` which has further instructions on installation and usage. 8 | 9 | ## Macro sets 10 | 11 | 1. [Flash](./Notification) 12 | - Advanced notification system with 14 optional settings. 13 | - JavaScript method. 14 | - Insertion of your own custom notification types. 15 | - Screen reader ready. (Thanks RynGM for the suggestion) 16 | - Mobile ready. 17 | - Highly customizable! 18 | 19 | ![Flash example](./Resources/Gif/example4a.gif) 20 | 21 | 2. [Dyslexia](./Dyslexia) 22 | - Simulate what dyslexia feels like to players/user. 23 | - Customizable: 24 | - Word size, the minimum amount of letters a word needs to be changed 25 | - Chance, the minimum chance a word has to have letters switched 26 | - Delay, the delay per interval for words to change in milliseconds 27 | 28 | ![Dyslexia example](./Resources/Gif/example1.gif) 29 | 30 | 3. [Spoiler](./Spoiler) 31 | - Hides words behind a blur. 32 | - Customizable: 33 | - Initial blur, the amount of blur in pixels. 34 | - Hover over blur, the amount of blur in pixels when hovering over the blurred text. 35 | - Hint text, help the player by hinting them to press or show another message you like. 36 | 37 | ![Spoiler example](/Resources/Gif/example1a.gif) 38 | 39 | 4. [Word Wheel](./WordWheel) 40 | - Creates a wheel of words that follows the cursor. 41 | - Customizable: 42 | - Text, the message to whirl around the cursor. 43 | - font size, the size of the text. 44 | - circle size, the diameter of the circle which whirls around the cursor. 45 | 46 | ![Spoiler example](/Resources/Gif/example1b.gif) 47 | -------------------------------------------------------------------------------- /Spoiler/spoiler.js: -------------------------------------------------------------------------------- 1 | Macro.add("spoiler", { 2 | tags: null, 3 | handler: function () { 4 | // Spoiler macro by SjoerdHekking 5 | // with the help of Cyrus Firheir 6 | "use strict"; 7 | 8 | let errorArray = []; 9 | const storeArg = Math.min(10, Math.max(0, parseInt(this.args[0] ?? 4))); 10 | const storeArg2 = Math.min(10, Math.max(0, parseInt(this.args[1] ?? 2))); 11 | const storeArg3 = String(this.args[2] ?? "Click to reveal completely"); 12 | 13 | if (isNaN(storeArg)) { 14 | errorArray.push("Check first argument. The spoiler macro used the default"); 15 | } 16 | 17 | if (isNaN(storeArg2)) { 18 | errorArray.push("Check second argument. The spoiler macro used the default"); 19 | } 20 | 21 | if (errorArray.length > 0) { 22 | let joinedArray = errorArray.join("\n") 23 | return this.error(joinedArray); 24 | } 25 | 26 | let out = $(``) 27 | .wiki(this.payload[0].contents) 28 | .appendTo(this.output); 29 | 30 | spoiler(out, { 31 | max: storeArg, 32 | partial: storeArg2, 33 | hintText: storeArg3 34 | }); 35 | 36 | function spoiler(selector, options) { 37 | const elements = $(selector); 38 | 39 | elements.each(function(index, el) { 40 | el.dataset.spoilerState = "shrouded"; 41 | el.style.transition = "filter 250ms"; 42 | 43 | function applyBlur(radius) { 44 | el.style.filter = `blur(${radius}px)`; 45 | } 46 | 47 | applyBlur(options.max); 48 | 49 | el.addEventListener("mouseover", function () { 50 | el.style.cursor = "pointer"; 51 | el.title = options.hintText; 52 | if (el.dataset.spoilerState === "shrouded") applyBlur(options.partial); 53 | }); 54 | 55 | el.addEventListener("mouseout", function () { 56 | el.title = options.hintText; 57 | if (el.dataset.spoilerState === "shrouded") applyBlur(options.max); 58 | }); 59 | 60 | el.addEventListener("click", function () { 61 | if (el.dataset.spoilerState === "shrouded") { 62 | el.dataset.spoilerState = "revealed"; 63 | el.title = ""; 64 | el.style.cursor = "auto"; 65 | applyBlur(0); 66 | } else { 67 | el.dataset.spoilerState = "shrouded"; 68 | el.title = options.hintText; 69 | el.style.cursor = "pointer"; 70 | applyBlur(options.max); 71 | } 72 | }); 73 | }); 74 | } 75 | } 76 | }); -------------------------------------------------------------------------------- /Dyslexia/README.md: -------------------------------------------------------------------------------- 1 | # Dyslexia 2 | 3 | Shuffle letters inside words to simulate dyslexia. 4 | 5 | ## Installation 6 | 7 | If using the Twine desktop/web app, copy contents of `dyslexia.js` to the `Story JavaScript` section. 8 | 9 | If using a compiler like Tweego, drop `dyslexia.js` to your source folder. 10 | 11 | ## Example Usage 12 | 13 | The following example uses the default values of dyslexia. 14 | 15 | ```html 16 | <> 17 | given text will get it's letters randomly shuffled within the words. Numbers within the text, such as 4 or 20 will be ignored. 18 | <> 19 | ``` 20 | ![Dyslexia example](../Resources/Gif/example1.gif) 21 | --- 22 | 23 | ## Usage - Dyslexia arguments 24 | 25 | 1. 'Dyslexia' has three arguments which allow things to be customized for your desire. 26 | - [Chance of shuffle](#Chance), default is 10%. (Argument1) 27 | - [Selecting word size](#Word-size), default is 3 letters. (Argument2) 28 | - [Delay of interval](#Delay), default is 50 milliseconds. (Argument3) 29 | 30 | **Argument placement:** 31 | 32 | ```html 33 | <> 34 | any text given. 35 | <> 36 | ``` 37 | 38 | **Example:** 39 | 40 | ```html 41 | <> 42 | This example will have 20% chance to shuffle words. 43 | The words needs to be of a minimum length of 4 letters. 44 | The delay is 100 milliseconds or 0.1 second. 45 | <> 46 | ``` 47 | ![Dyslexia example](../Resources/Gif/example2.gif) 48 | --- 49 | 50 | ## Chance 51 | 52 | - `Argument1`: *(integer)* chance. 53 | 54 | 1. 'Dyslexia' has a default chance set to `10%`. 55 | - Only integers are accepted. 56 | - Minimum will be `1%` or an error will be thrown. 57 | - Maximum will be `100%` or an error will be thrown. 58 | 59 | ## Word size 60 | 61 | - `Argument2`: *(integer)* size. 62 | 63 | 1. 'Dyslexia' has a default word size set to `3`. 64 | - Only integers are accepted. 65 | - Minimum will be `2` or an error will be thrown. 66 | 67 | ## Delay 68 | 69 | - `Argument3`: *(integer)* delay. 70 | 71 | 1. 'Dyslexia' has a default delay of `50ms`, to prevent your browser from crashing. 72 | - Only integers are accepted. 73 | - Minimum will be `50ms` or an error will be thrown. 74 | 75 | **NOTE:** Time is set in milliseconds and not in seconds. 76 | 77 | ## Styling 78 | 79 | 1. 'Dyslexia' uses a `` as output. 80 | - ``: *(class)*. 81 | 82 | **Example:** 83 | 84 | ```css 85 | .macro-mess-up-words { 86 | font-size: 20px; 87 | color: red; 88 | } 89 | ``` 90 | ![Dyslexia styling example](../Resources/Gif/example3.gif) -------------------------------------------------------------------------------- /WordWheel/README.md: -------------------------------------------------------------------------------- 1 | # Word wheel 2 | 3 | Words will circle around the cursor and follow it, where it ventures. 4 | 5 | ## Installation 6 | 7 | If using the Twine desktop/web app, copy contents of `wordwheel.js` to the `Story JavaScript` section, and copy contents of `wordwheel.css` to the `Story Stylesheet` section. 8 | 9 | If using a compiler like Tweego, drop `wordwheel.js` and `wordwheel.css` to your source folder. 10 | 11 | ## Example Usage 12 | 13 | The following example uses the default values of wordwheel. 14 | 15 | ```html 16 | <> 17 | <> 18 | ``` 19 | ![Wordwheel example](../Resources/Gif/example1b.gif) 20 | --- 21 | 22 | ## Usage - Word wheel arguments 23 | 24 | 1. 'Word wheel' has three arguments which allow things to be customized for your desire. 25 | - [Input text](#Text),There is no default, this must be user provided. (Argument1) 26 | - [Selecting Font-size](#Font-size), default is 14. (Argument2) 27 | - [Circle diameter](#Diameter), default is 22. (Argument3) 28 | 29 | **Argument placement:** 30 | 31 | ```html 32 | <> 33 | <> 34 | <> 35 | <> 36 | ``` 37 | 38 | **Example:** 39 | 40 | ```html 41 | <> 42 | <> 43 | <> 44 | <> 45 | ``` 46 | ![wordwheel example](../Resources/Gif/example2b.gif) 47 | --- 48 | 49 | ## Text 50 | 51 | - `Argument1`: *(string)* text. 52 | 53 | 1. 'wordwheel' has no default, user must submit a string with text. 54 | - Only UTF-8 is accepted. 55 | - Must be user supplied. 56 | 57 | ## Font size 58 | 59 | - `Argument2`: *(integer)* font size. 60 | 61 | 1. 'wordwheel' has a default word size set to `14`. 62 | - Only integers are accepted. 63 | - Minimum will be `1` or an error will be thrown. 64 | - Maximum will be `30` or an error will be thrown. 65 | 66 | ## Diameter 67 | 68 | - `Argument3`: *(integer)* circle diameter. 69 | 70 | 1. 'wordwheel' has a default diameter of `22`. 71 | - Only integers are accepted. 72 | - Minimum will be `20` or an error will be thrown. 73 | - Maximum will be `70` or an error will be thrown. 74 | 75 | **NOTE:** Diameter is also dependend on [Font size](#Font-size). 76 | 77 | ## Styling 78 | 79 | 1. 'wordwheel' uses a nested `
` as output. 80 | - `
`: *(id)*. 81 | 82 | **Example:** 83 | 84 | ```css 85 | #outerCircleText { 86 | color: red; 87 | font-style: italic; 88 | position: absolute; 89 | top: 0; 90 | left: 0; 91 | z-index: 3000; 92 | cursor: default; 93 | } 94 | ``` 95 | 96 | As seen above, one needs to change the rules of `#outerCircleText` in order to style the word wheel. In this example the color was changed from red to white and italics were applied. 97 | 98 | ```css 99 | #outerCircleText { 100 | color: red; 101 | font-style: italic; 102 | } 103 | ``` 104 | 105 | **NOTE:** Don't touch any position property. 106 | 107 | ![wordwheel styling example](../Resources/Gif/example3b.gif) -------------------------------------------------------------------------------- /Spoiler/README.md: -------------------------------------------------------------------------------- 1 | # Spoiler 2 | 3 | Hide words behind a blur with a hover over effect, reveal them by clicking words. 4 | 5 | ## Installation 6 | 7 | If using the Twine desktop/web app, copy contents of `spoiler.js` to the `Story JavaScript` section. 8 | 9 | If using a compiler like Tweego, drop `spoiler.js` to your source folder. 10 | 11 | ## Example Usage 12 | 13 | The following example uses the default values of spoiler. 14 | 15 | ```html 16 | <> 17 | This text will be hidden behind a blur. 18 | <> 19 | 20 | Or you could hide a <>secret<> word. 21 | ``` 22 | ![Spoiler example](../Resources/Gif/example1a.gif) 23 | --- 24 | 25 | ## Usage - Spoiler arguments 26 | 27 | 1. 'Spoiler' has three arguments which allow things to be customized for your desire. 28 | - [Initial blur effect](#Initial-blur), default is 4px blur. (Argument1) 29 | - [Hover over blur effect](#Hover-blur), default is 2px blur. (Argument2) 30 | - [Hint text](#Hint-text), default is `Click to reveal completely`. (Argument3) 31 | 32 | **Argument placement:** 33 | 34 | ```html 35 | <> 36 | any text given. 37 | <> 38 | ``` 39 | 40 | **Example:** 41 | 42 | ```html 43 | <> 44 | This example will have an initial blur of 6px. 45 | a hover over blur of 1px. 46 | a hint text saying "Dare to press me". 47 | <> 48 | 49 | Or you could hide a <>secret<> word. 50 | ``` 51 | ![Spoiler custom example](../Resources/Gif/example2a.gif) 52 | --- 53 | 54 | ## Initial blur 55 | 56 | - `Argument1`: *(integer)* blur in pixels. 57 | 58 | 1. 'Spoiler' has a default initial blur set to `4px`. 59 | - Only integers are accepted. 60 | - Minimum will be `1px` or the setting will conform to the closests safe number. 61 | - Maximum will be `10px` or the setting will conform to the closests safe number. 62 | 63 | **NOTE:** Blurs with a greater value then 10 will be impossible to use. 64 | 65 | ## Hover blur 66 | 67 | - `Argument2`: *(integer)* blur in pixels. 68 | 69 | 1. 'Spoiler' has a default hover over blur set to `2px`. 70 | - Only integers are accepted. 71 | - Minimum will be `1px` or the setting will conform to the closests safe number. 72 | - Maximum will be `10px` or the setting will conform to the closests safe number. 73 | 74 | **NOTE:** Blurs with a greater value then 10 will be impossible to use. 75 | 76 | ## Hint text 77 | 78 | - `Argument3`: *(string)* hover over text. 79 | 80 | 1. 'Spoiler' has a default hover over text stating `Click to reveal completely`. 81 | - Only string are accepted. Use quotation. 82 | - The string `null` won't be accepted and will be replaced with the default. 83 | - Using `Argument3` without quotation is possible, but only the first word gets read. 84 | 85 | **NOTE:** leaving the string empty, `""`, will result in no hint text. 86 | 87 | ## Styling 88 | 89 | 1. 'Spoiler' uses a `` as output. 90 | - ``: *(class)*. 91 | 92 | **Example:** 93 | 94 | ```css 95 | .macro-spoiler { 96 | font-size: 20px; 97 | color: red; 98 | } 99 | ``` 100 | ![Spoiler styling example](../Resources/Gif/example3a.gif) -------------------------------------------------------------------------------- /Notification/Version 2.36 or below/flash.min.css: -------------------------------------------------------------------------------- 1 | :root{--success-icon-content:'\e803';--warning-icon-content:'\e80d';--error-icon-content:'\e80c';--info-icon-content:'\e80a';--bug-icon-content:'\e838';--disabled-icon-content:'\e80f'}.flash-container{z-index:1000;max-width:25%;position:fixed}.top-left-flash-layout{top:75px;left:15px}.top-right-flash-layout{top:75px;right:15px}.middle-right-flash-layout{top:50%;right:15px;transform:translateY(-50%)}.bottom-right-flash-layout{bottom:75px;right:15px}.middle-bottom-flash-layout{bottom:75px;left:50%;transform:translate(-50%,-50%)}.bottom-left-flash-layout{bottom:75px;left:15px}.middle-left-flash-layout{top:50%;left:15px;transform:translateY(-50%)}.middle-top-flash-layout{top:75px;left:50%;transform:translate(-50%,-50%)}.flash-container .flash-message{position:relative;opacity:0;transform:translateX(-20px);transition:all .5s;background-color:#fff;color:#2C3433;border-radius:0;border-top-right-radius:4px;border-bottom-right-radius:4px;margin-bottom:10px;padding:5px 35px 5px 20px;box-shadow:2px 2px 33px 8px rgb(0 0 0 / .1);line-height:1.4;cursor:pointer}.flash-container .flash-message .flash-text{font-size:1em}.flash-container .flash-not-interactive{cursor:default}.flash-container .flash-message .flash-progress{position:absolute;right:0;top:auto;bottom:0;left:0;width:0;height:3px;opacity:1;background-color:rgb(0 0 0 / .15);transition:opacity .1s}.flash-container .flash-message .flash-progress.flash-is-hidden{opacity:0}.flash-container .flash-message .flash-progress.flash-progress-top{top:0;bottom:auto}.flash-container .flash-message:before{position:absolute;content:'';width:7px;height:100%;top:0;bottom:0;left:-7px;background-color:#fff0;border-top-left-radius:4px;border-bottom-left-radius:4px}.flash-container .flash-message:after{position:absolute;content:'';font-family:'tme-fa-icons';top:5px;right:8px;text-align:center;vertical-align:middle;color:#9e9e9e}.flash-container .flash-message.flash-is-visible{opacity:1;transform:translateX(0)}.flash-container .flash-message.flash-success .flash-progress{background-color:rgb(76 175 80 / .15)}.flash-container .flash-message.flash-success:before{background-color:#4CAF50}.flash-container .flash-message.flash-success:after{color:rgb(76 175 80 / .5);content:var(--success-icon-content)}.flash-container .flash-message.flash-warning .flash-progress{background-color:rgb(255 133 27 / .15)}.flash-container .flash-message.flash-warning:before{background-color:#FF851B}.flash-container .flash-message.flash-warning:after{color:rgb(255 133 27 / .5);content:var(--warning-icon-content)}.flash-container .flash-message.flash-error .flash-progress{background-color:rgb(255 65 54 / .15)}.flash-container .flash-message.flash-error:before{background-color:#FF4136}.flash-container .flash-message.flash-error:after{color:rgb(255 65 54 / .5);content:var(--error-icon-content)}.flash-container .flash-message.flash-info .flash-progress{background-color:rgb(0 116 217 / .15)}.flash-container .flash-message.flash-info:before{background-color:#0074D9}.flash-container .flash-message.flash-info:after{color:rgb(0 116 217 / .5);content:var(--info-icon-content)}.flash-container .flash-message.flash-bug .flash-progress{background-color:rgb(138 43 226 / .15)}.flash-container .flash-message.flash-bug:before{background-color:#8A2BE2}.flash-container .flash-message.flash-bug:after{color:rgb(138 43 226 / .5);content:var(--bug-icon-content)}.flash-container .flash-message.flash-disabled .flash-progress{background-color:rgb(170 170 170 / .15)}.flash-container .flash-message.flash-disabled:before{background-color:#aaa}.flash-container .flash-message.flash-disabled:after{color:rgb(170 170 170 / .5);content:var(--disabled-icon-content)}.flash-container .flash-message.flash-default{padding-right:20px;border-top-left-radius:4px;border-bottom-left-radius:4px}@media all and (max-width:1280px){.flash-container{max-width:33.33%}}@media all and (max-width:768px){.flash-container{max-width:50%}}@media all and (max-width:480px){.flash-container{right:10px;left:10px;max-width:100%}}.flash-container .flash-message.dark-theme{background-color:#2C3433;color:#fff}.flash-container .flash-message.dark-theme .flash-progress{background-color:rgb(255 255 255 / .5)} -------------------------------------------------------------------------------- /Dyslexia/dyslexia.js: -------------------------------------------------------------------------------- 1 | Macro.add('dyslexia', { 2 | tags: null, 3 | handler : function () { 4 | // Dyslexia by SjoerdHekking 5 | 'use strict'; 6 | 7 | let out = $(``) 8 | .wiki(this.payload[0].contents) 9 | .appendTo(this.output); 10 | 11 | // returns texts 12 | let getTextNodesIn = function(el) { 13 | return $(el).find(":not(iframe,script)").addBack().contents().filter(function() { 14 | return this.nodeType === 3; 15 | }); 16 | }; 17 | 18 | // only allow scanning within these tags 19 | let textNodes = getTextNodesIn(out); 20 | 21 | let errorArray = []; 22 | 23 | let storeArg = this.args[0] || 10; 24 | let storeArg2 = this.args[1] || 3; 25 | let storeArg3 = this.args[2] || 50; 26 | 27 | if (storeArg) { 28 | storeArg = parseInt(storeArg); 29 | if (isNaN(storeArg) || storeArg < 0 || storeArg > 100) { 30 | errorArray.push("Check first argument. Invalid value."); 31 | } 32 | } 33 | if (storeArg2) { 34 | storeArg2 = parseInt(storeArg2); 35 | if (isNaN(storeArg2) || storeArg2 < 2) { 36 | errorArray.push("Check second argument. Invalid value."); 37 | } 38 | } 39 | if (storeArg3) { 40 | storeArg3 = parseInt(storeArg3); 41 | if (isNaN(storeArg3) || storeArg3 < 50) { 42 | errorArray.push("Check third argument. Invalid value."); 43 | } 44 | } 45 | 46 | if (errorArray.length > 0) { 47 | let joinedArray = errorArray.join('\n') 48 | return this.error(joinedArray); 49 | } 50 | 51 | let wordsInTextNodes = []; 52 | for (let i = 0; i < textNodes.length; i++) { 53 | const node = textNodes[i]; 54 | const wordsInNode = node.nodeValue.match(/\w+/g) || []; 55 | 56 | wordsInTextNodes[i] = wordsInNode.map((word, index) => ({ 57 | length: word.length, 58 | position: node.nodeValue.indexOf(word, index === 0 ? 0 : wordsInNode[index - 1].length + wordsInNode[index - 1].index) 59 | })); 60 | } 61 | 62 | /** 63 | * Messes up the words in text nodes according to the probability and word length delay set in the dyslexia macro. 64 | */ 65 | function messUpWords () { 66 | for (let i = 0; i < textNodes.length; i++) { 67 | let node = textNodes[i]; 68 | 69 | for (let j = 0; j < wordsInTextNodes[i].length; j++) { 70 | 71 | if (Math.random() < (100 - storeArg) / 100 ) { 72 | continue; 73 | } 74 | 75 | let wordMeta = wordsInTextNodes[i][j]; 76 | let word = node.nodeValue.slice(wordMeta.position, wordMeta.position + wordMeta.length); 77 | node.nodeValue = node.nodeValue.slice(0, wordMeta.position) + messUpWord(word) + node.nodeValue.slice(wordMeta.position + wordMeta.length); 78 | }; 79 | }; 80 | } 81 | 82 | /** 83 | * Messes up a single word by swapping random letters in the "messy part" of the word (i.e. all letters except the first and last). 84 | * @param {string} word - The word to mess up. 85 | * @returns {string} The messed up word, or the original word if its length is less than the minimum word length specified in the dyslexia macro. 86 | */ 87 | function messUpWord (word) { 88 | if (word.length < storeArg2) return word; 89 | const middle = messUpMessyPart(word.slice(1, -1)); 90 | return `${word[0]}${middle}${word.slice(-1)}`; 91 | } 92 | 93 | /** 94 | * Messes up the "messy part" of a word by swapping two random letters. 95 | * @param {string} messyPart - The "messy part" of a word to mess up. 96 | * @returns {string} The messed up "messy part", or the original "messy part" if its length is less than 2. 97 | */ 98 | function messUpMessyPart (messyPart) { 99 | if (messyPart.length < 2) return messyPart; 100 | 101 | let a, b; 102 | while (!(a < b)) { 103 | a = getRandomInt(0, messyPart.length - 1); 104 | b = getRandomInt(0, messyPart.length - 1); 105 | } 106 | 107 | function getRandomInt(min, max) { 108 | return Math.floor(Math.random() * (max - min + 1) + min); 109 | } 110 | 111 | return `${messyPart.slice(0, a)}${messyPart[b]}${messyPart.slice(a+1, b)}${messyPart[a]}${messyPart.slice(b+1)}`; 112 | } 113 | 114 | setInterval(messUpWords, storeArg3); 115 | } 116 | }); 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Notification/Version 2.37 or above/flash.min.css: -------------------------------------------------------------------------------- 1 | :root{--success-icon-content:'\f00c';--warning-icon-content:'\f071';--error-icon-content:'\f06a';--info-icon-content:'\f05a';--bug-icon-content:'\f188';--disabled-icon-content:'\f09c'}.flash-container{z-index:1000;max-width:25%;position:fixed}.top-left-flash-layout{top:75px;left:15px}.top-right-flash-layout{top:75px;right:15px}.middle-right-flash-layout{top:50%;right:15px;transform:translateY(-50%)}.bottom-right-flash-layout{bottom:75px;right:15px}.middle-bottom-flash-layout{bottom:75px;left:50%;transform:translate(-50%,-50%)}.bottom-left-flash-layout{bottom:75px;left:15px}.middle-left-flash-layout{top:50%;left:15px;transform:translateY(-50%)}.middle-top-flash-layout{top:75px;left:50%;transform:translate(-50%,-50%)}.flash-container .flash-message{position:relative;opacity:0;transform:translateX(-20px);transition:all .5s;background-color:#fff;color:#2C3433;border-radius:0;border-top-right-radius:4px;border-bottom-right-radius:4px;margin-bottom:10px;padding:5px 35px 5px 20px;box-shadow:2px 2px 33px 8px rgb(0 0 0 / .1);line-height:1.4;cursor:pointer}.flash-container .flash-message .flash-text{font-size:1em}.flash-container .flash-not-interactive{cursor:default}.flash-container .flash-message .flash-progress{position:absolute;right:0;top:auto;bottom:0;left:0;width:0;height:3px;opacity:1;background-color:rgb(0 0 0 / .15);transition:opacity .1s}.flash-container .flash-message .flash-progress.flash-is-hidden{opacity:0}.flash-container .flash-message .flash-progress.flash-progress-top{top:0;bottom:auto}.flash-container .flash-message:before{position:absolute;content:'';width:7px;height:100%;top:0;bottom:0;left:-7px;background-color:#fff0;border-top-left-radius:4px;border-bottom-left-radius:4px}.flash-container .flash-message:after{position:absolute;content:'';top:5px;right:8px;text-align:center;vertical-align:middle;color:#9e9e9e;font-family:sc-icons!important;font-style:normal;font-weight:400;font-variant:normal;speak:never;text-rendering:auto;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flash-container .flash-message.flash-is-visible{opacity:1;transform:translateX(0)}.flash-container .flash-message.flash-success .flash-progress{background-color:rgb(76 175 80 / .15)}.flash-container .flash-message.flash-success:before{background-color:#4CAF50}.flash-container .flash-message.flash-success:after{color:rgb(76 175 80 / .5);content:var(--success-icon-content)}.flash-container .flash-message.flash-warning .flash-progress{background-color:rgb(255 133 27 / .15)}.flash-container .flash-message.flash-warning:before{background-color:#FF851B}.flash-container .flash-message.flash-warning:after{color:rgb(255 133 27 / .5);content:var(--warning-icon-content)}.flash-container .flash-message.flash-error .flash-progress{background-color:rgb(255 65 54 / .15)}.flash-container .flash-message.flash-error:before{background-color:#FF4136}.flash-container .flash-message.flash-error:after{color:rgb(255 65 54 / .5);content:var(--error-icon-content)}.flash-container .flash-message.flash-info .flash-progress{background-color:rgb(0 116 217 / .15)}.flash-container .flash-message.flash-info:before{background-color:#0074D9}.flash-container .flash-message.flash-info:after{color:rgb(0 116 217 / .5);content:var(--info-icon-content)}.flash-container .flash-message.flash-bug .flash-progress{background-color:rgb(138 43 226 / .15)}.flash-container .flash-message.flash-bug:before{background-color:#8A2BE2}.flash-container .flash-message.flash-bug:after{color:rgb(138 43 226 / .5);content:var(--bug-icon-content)}.flash-container .flash-message.flash-disabled .flash-progress{background-color:rgb(170 170 170 / .15)}.flash-container .flash-message.flash-disabled:before{background-color:#aaa}.flash-container .flash-message.flash-disabled:after{color:rgb(170 170 170 / .5);content:var(--disabled-icon-content)}.flash-container .flash-message.flash-default{padding-right:20px;border-top-left-radius:4px;border-bottom-left-radius:4px}@media all and (max-width:1280px){.flash-container{max-width:33.33%}}@media all and (max-width:768px){.flash-container{max-width:50%}}@media all and (max-width:480px){.flash-container{right:10px;left:10px;max-width:100%}}.flash-container .flash-message.dark-theme{background-color:#2C3433;color:#fff}.flash-container .flash-message.dark-theme .flash-progress{background-color:rgb(255 255 255 / .5)} -------------------------------------------------------------------------------- /WordWheel/wordwheel.js: -------------------------------------------------------------------------------- 1 | // Made by SjoerdHekking with too much help from Gwen 2 | // SETUP 3 | setup.circle = { 4 | msg: "Error".split(""), 5 | size: 14, 6 | circleY: 1, 7 | circleX: 1, 8 | letter_spacing: 5, 9 | diameter: 22, 10 | rotation: 0.2, 11 | speed: 0.5, 12 | currStep: 20, 13 | y: [], 14 | x: [], 15 | Y: [], 16 | X: [], 17 | circleInitnopy: false, 18 | circleFirstDiv: document.createElement("div"), 19 | circleSecondDiv: document.createElement("div"), 20 | circleMouse: function (e) { 21 | e = e || window.event; 22 | setup.circle.ymouse = !isNaN(e.pageY) ? e.pageY : e.clientY; // setup.circle.y-position 23 | setup.circle.xmouse = !isNaN(e.pageX) ? e.pageX : e.clientX; // setup.circle.x-position 24 | }, 25 | makeCircle: function () { // rotation/positioning 26 | if (setup.circle.circleInitnopy) { 27 | setup.circle.circleFirstDiv.style.top = document.body.scrollTop + "px"; 28 | setup.circle.circleFirstDiv.style.left = document.body.scrollLeft + "px"; 29 | } 30 | setup.circle.currStep -= setup.circle.rotation; 31 | for (let i = setup.circle.n; i > -1; --i) { // makes the circle 32 | let d = document.getElementById("iemsg" + i).style; 33 | d.top = Math.round(setup.circle.y[i] + setup.circle.a * Math.sin((setup.circle.currStep + i) / setup.circle.letter_spacing) * setup.circle.circleY - 15) + "px"; 34 | d.left = Math.round(setup.circle.x[i] + setup.circle.a * Math.cos((setup.circle.currStep + i) / setup.circle.letter_spacing) * setup.circle.circleX) + "px"; 35 | } 36 | }, 37 | circleDrag: function () { // makes the resistance 38 | setup.circle.y[0] = setup.circle.Y[0] += (setup.circle.ymouse - setup.circle.Y[0]) * setup.circle.speed; 39 | setup.circle.x[0] = setup.circle.X[0] += (setup.circle.xmouse - 20 - setup.circle.X[0]) * setup.circle.speed; 40 | for (let i = setup.circle.n; i > 0; --i) { 41 | setup.circle.y[i] = setup.circle.Y[i] += (setup.circle.y[i - 1] - setup.circle.Y[i]) * setup.circle.speed; 42 | setup.circle.x[i] = setup.circle.X[i] += (setup.circle.x[i - 1] - setup.circle.X[i]) * setup.circle.speed; 43 | } 44 | setup.circle.makeCircle(); 45 | }, 46 | circleInit: function () { // appends message divs, & sets initial values for positioning arrays 47 | setup.circle.n = setup.circle.msg.length - 1; 48 | setup.circle.a = Math.round(setup.circle.size * setup.circle.diameter * 0.20); 49 | setup.circle.ymouse = setup.circle.a * setup.circle.circleY + 20; 50 | setup.circle.xmouse = setup.circle.a * setup.circle.circleX + 20; 51 | setup.circle.circleFirstDiv.id = "outerCircleText"; 52 | setup.circle.circleFirstDiv.style.fontSize = setup.circle.size + "px"; 53 | if (!isNaN(window.pageYOffset)) { 54 | setup.circle.ymouse += window.pageYOffset; 55 | setup.circle.xmouse += window.pageXOffset; 56 | } 57 | else 58 | setup.circle.circleInitnopy = true; 59 | for (let i = setup.circle.n; i > -1; --i) { 60 | let d = document.createElement("div"); 61 | d.id = "iemsg" + i; 62 | d.style.height = setup.circle.a + "px"; 63 | d.style.width = setup.circle.a + "px"; 64 | d.appendChild(document.createTextNode(setup.circle.msg[i])); 65 | setup.circle.circleSecondDiv.appendChild(d); setup.circle.y[i] = setup.circle.x[i] = setup.circle.Y[i] = setup.circle.X[i] = 0; 66 | } 67 | setup.circle.circleFirstDiv.appendChild(setup.circle.circleSecondDiv); 68 | document.body.appendChild(setup.circle.circleFirstDiv); 69 | setInterval(setup.circle.circleDrag, 25); 70 | }, 71 | circleAscroll: function () { 72 | setup.circle.ymouse += window.pageYOffset; 73 | setup.circle.xmouse += window.pageXOffset; 74 | window.removeEventListener("scroll", setup.circle.circleAscroll, false); 75 | } 76 | }; 77 | 78 | // MACRO 79 | 80 | Macro.add("wordwheel", { 81 | tags: ["fontSize", "fontsize", "circleSize", "circlesize"], 82 | handler: function () { 83 | let errorArray = []; 84 | if (!window.addEventListener && !window.attachEvent && !document.createElement) {errorArray.push("Unable to either add EvenListener, AttachEvent or createElement")}; 85 | if(this.args.length <= 0) 86 | return this.error("First argument cannot be skipped, please insert a string via <>."); 87 | if(this.args[0] === "") 88 | return this.error("First argument cannot be an empty string."); 89 | if(this.args[0].length > 31) 90 | return this.error("Word wheel doesn't accept more than 50 characters."); 91 | 92 | setup.circle.msg = this.args[0].split(""); 93 | 94 | 95 | for (const pay of this.payload) { 96 | switch (pay.name.toLowerCase()) { 97 | case "fontsize": 98 | if (pay.args[0] > 30) 99 | errorArray.push("Font size cannot be bigger than 30."); 100 | if (pay.args[0] < 4) 101 | errorArray.push("Font size cannot be negative nor below 4."); 102 | 103 | setup.circle.size = pay.args[0]; 104 | break; 105 | case "circlesize": 106 | if (pay.args[0] > 70) 107 | errorArray.push("Circle size cannot be above 70."); 108 | if (pay.args[0] < 20) 109 | errorArray.push("Circle size cannot be below 20"); 110 | 111 | setup.circle.diameter = pay.args[0]; 112 | break; 113 | } 114 | } 115 | 116 | if (errorArray.length > 0) 117 | return this.error(errorArray.join("\n")); 118 | 119 | else if (window.addEventListener) { 120 | setup.circle.circleInit(); 121 | document.addEventListener("mouseover", setup.circle.circleMouse, false); 122 | document.addEventListener("mousemove", setup.circle.circleMouse, false); 123 | if (/Apple/.test(navigator.vendor)) 124 | window.addEventListener("scroll", setup.circle.circleAscroll, false); 125 | } 126 | else if (window.attachEvent) { 127 | // window.attachEvent("onload", setup.circle.circleInit); 128 | document.attachEvent("onmousemove", setup.circle.circleMouse); 129 | } 130 | } 131 | }); -------------------------------------------------------------------------------- /Notification/Version 2.36 or below/flash.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --success-icon-content: '\e803'; 3 | --warning-icon-content: '\e80d'; 4 | --error-icon-content: '\e80c'; 5 | --info-icon-content: '\e80a'; 6 | --bug-icon-content: '\e838'; 7 | --disabled-icon-content: '\e80f'; 8 | } 9 | 10 | /* FLASH JS THEME VARIABLES */ 11 | .flash-container { 12 | z-index: 1000; 13 | max-width: 25%; 14 | position: fixed; 15 | } 16 | .top-left-flash-layout { 17 | top: 75px; 18 | left: 15px; 19 | } 20 | .top-right-flash-layout { 21 | top: 75px; 22 | right: 15px; 23 | } 24 | .middle-right-flash-layout { 25 | top: 50%; 26 | right: 15px; 27 | transform: translateY(-50%); 28 | } 29 | .bottom-right-flash-layout { 30 | bottom: 75px; 31 | right: 15px; 32 | } 33 | .middle-bottom-flash-layout { 34 | bottom: 75px; 35 | left: 50%; 36 | transform: translate(-50%, -50%); 37 | } 38 | .bottom-left-flash-layout { 39 | bottom: 75px; 40 | left: 15px; 41 | } 42 | .middle-left-flash-layout { 43 | top: 50%; 44 | left: 15px; 45 | transform: translateY(-50%); 46 | } 47 | .middle-top-flash-layout { 48 | top: 75px; 49 | left: 50%; 50 | transform: translate(-50%, -50%); 51 | } 52 | 53 | /* FLASHJS - DEFAULT THEME */ 54 | .flash-container .flash-message { 55 | position: relative; 56 | opacity: 0; 57 | transform: translateX(-20px); 58 | transition: all .5s; 59 | background-color: #fff; 60 | color: #2C3433; 61 | border-radius: 0; 62 | border-top-right-radius: 4px; 63 | border-bottom-right-radius: 4px; 64 | margin-bottom: 10px; 65 | padding: 5px 35px 5px 20px; 66 | box-shadow: 2px 2px 33px 8px rgba(0, 0, 0, 0.1); 67 | line-height: 1.4; 68 | cursor: pointer; 69 | } 70 | .flash-container .flash-message .flash-text { 71 | font-size: 1em; 72 | } 73 | .flash-container .flash-not-interactive { 74 | cursor: default; 75 | } 76 | .flash-container .flash-message .flash-progress { 77 | position: absolute; 78 | right: 0; 79 | top: auto; 80 | bottom: 0; 81 | left: 0; 82 | width: 0; 83 | height: 3px; 84 | opacity: 1; 85 | background-color: rgba(0, 0, 0, 0.15); 86 | transition: opacity .1s; 87 | } 88 | .flash-container .flash-message .flash-progress.flash-is-hidden { 89 | opacity: 0; 90 | } 91 | .flash-container .flash-message .flash-progress.flash-progress-top { 92 | top: 0; 93 | bottom: auto; 94 | } 95 | .flash-container .flash-message:before { 96 | position: absolute; 97 | content: ''; 98 | width: 7px; 99 | height: 100%; 100 | top: 0; 101 | bottom: 0; 102 | left: -7px; 103 | background-color: transparent; 104 | border-top-left-radius: 4px; 105 | border-bottom-left-radius: 4px; 106 | } 107 | .flash-container .flash-message:after { 108 | position: absolute; 109 | content: ''; 110 | font-family: 'tme-fa-icons'; 111 | top: 5px; 112 | right: 8px; 113 | text-align: center; 114 | vertical-align: middle; 115 | color: #9e9e9e; 116 | } 117 | .flash-container .flash-message.flash-is-visible { 118 | opacity: 1; 119 | transform: translateX(0); 120 | } 121 | /* SUCCESS */ 122 | .flash-container .flash-message.flash-success .flash-progress { 123 | background-color: rgba(76, 175, 80, 0.15); 124 | } 125 | .flash-container .flash-message.flash-success:before { 126 | background-color: #4CAF50; 127 | } 128 | .flash-container .flash-message.flash-success:after { 129 | color: rgba(76, 175, 80, 0.5); 130 | content: var(--success-icon-content); 131 | } 132 | /* WARNING */ 133 | .flash-container .flash-message.flash-warning .flash-progress { 134 | background-color: rgba(255, 133, 27, 0.15); 135 | } 136 | .flash-container .flash-message.flash-warning:before { 137 | background-color: #FF851B; 138 | } 139 | .flash-container .flash-message.flash-warning:after { 140 | color: rgba(255, 133, 27, 0.5); 141 | content: var(--warning-icon-content); 142 | } 143 | /* ERROR */ 144 | .flash-container .flash-message.flash-error .flash-progress { 145 | background-color: rgba(255, 65, 54, 0.15); 146 | } 147 | .flash-container .flash-message.flash-error:before { 148 | background-color: #FF4136; 149 | } 150 | .flash-container .flash-message.flash-error:after { 151 | color: rgba(255, 65, 54, 0.5); 152 | content: var(--error-icon-content); 153 | } 154 | /* INFO */ 155 | .flash-container .flash-message.flash-info .flash-progress { 156 | background-color: rgba(0, 116, 217, 0.15); 157 | } 158 | .flash-container .flash-message.flash-info:before { 159 | background-color: #0074D9; 160 | } 161 | .flash-container .flash-message.flash-info:after { 162 | color: rgba(0, 116, 217, 0.5); 163 | content: var(--info-icon-content); 164 | } 165 | /* BUG */ 166 | .flash-container .flash-message.flash-bug .flash-progress { 167 | background-color: rgba(138, 43, 226, 0.15); 168 | } 169 | .flash-container .flash-message.flash-bug:before { 170 | background-color: #8A2BE2; 171 | } 172 | .flash-container .flash-message.flash-bug:after { 173 | color: rgba(138, 43, 226, 0.5); 174 | content: var(--bug-icon-content); 175 | } 176 | /* DISABLED */ 177 | .flash-container .flash-message.flash-disabled .flash-progress { 178 | background-color: rgba(170, 170, 170, 0.15); 179 | } 180 | .flash-container .flash-message.flash-disabled:before { 181 | background-color: #aaa; 182 | } 183 | .flash-container .flash-message.flash-disabled:after { 184 | color: rgba(170, 170, 170, 0.5); 185 | content: var(--disabled-icon-content); 186 | } 187 | /* DEFAULT */ 188 | .flash-container .flash-message.flash-default { 189 | padding-right: 20px; 190 | border-top-left-radius: 4px; 191 | border-bottom-left-radius: 4px; 192 | } 193 | /* MEDIA */ 194 | @media all and (max-width: 1280px) { 195 | .flash-container { 196 | max-width: 33.33%; 197 | } 198 | } 199 | @media all and (max-width: 768px) { 200 | .flash-container { 201 | max-width: 50%; 202 | } 203 | } 204 | @media all and (max-width: 480px) { 205 | .flash-container { 206 | right: 10px; 207 | left: 10px; 208 | max-width: 100%; 209 | } 210 | } 211 | /* FLASH JS DARK THEME */ 212 | .flash-container .flash-message.dark-theme { 213 | background-color: #2C3433; 214 | color: #fff; 215 | } 216 | .flash-container .flash-message.dark-theme .flash-progress { 217 | background-color: rgba(255, 255, 255, 0.5); 218 | } -------------------------------------------------------------------------------- /Notification/Version 2.37 or above/flash.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --success-icon-content: '\f00c'; 3 | --warning-icon-content: '\f071'; 4 | --error-icon-content: '\f06a'; 5 | --info-icon-content: '\f05a'; 6 | --bug-icon-content: '\f188'; 7 | --disabled-icon-content: '\f09c'; 8 | } 9 | 10 | /* FLASH JS THEME VARIABLES */ 11 | .flash-container { 12 | z-index: 1000; 13 | max-width: 25%; 14 | position: fixed; 15 | } 16 | .top-left-flash-layout { 17 | top: 75px; 18 | left: 15px; 19 | } 20 | .top-right-flash-layout { 21 | top: 75px; 22 | right: 15px; 23 | } 24 | .middle-right-flash-layout { 25 | top: 50%; 26 | right: 15px; 27 | transform: translateY(-50%); 28 | } 29 | .bottom-right-flash-layout { 30 | bottom: 75px; 31 | right: 15px; 32 | } 33 | .middle-bottom-flash-layout { 34 | bottom: 75px; 35 | left: 50%; 36 | transform: translate(-50%, -50%); 37 | } 38 | .bottom-left-flash-layout { 39 | bottom: 75px; 40 | left: 15px; 41 | } 42 | .middle-left-flash-layout { 43 | top: 50%; 44 | left: 15px; 45 | transform: translateY(-50%); 46 | } 47 | .middle-top-flash-layout { 48 | top: 75px; 49 | left: 50%; 50 | transform: translate(-50%, -50%); 51 | } 52 | 53 | /* FLASHJS - DEFAULT THEME */ 54 | .flash-container .flash-message { 55 | position: relative; 56 | opacity: 0; 57 | transform: translateX(-20px); 58 | transition: all .5s; 59 | background-color: #fff; 60 | color: #2C3433; 61 | border-radius: 0; 62 | border-top-right-radius: 4px; 63 | border-bottom-right-radius: 4px; 64 | margin-bottom: 10px; 65 | padding: 5px 35px 5px 20px; 66 | box-shadow: 2px 2px 33px 8px rgba(0, 0, 0, 0.1); 67 | line-height: 1.4; 68 | cursor: pointer; 69 | } 70 | .flash-container .flash-message .flash-text { 71 | font-size: 1em; 72 | } 73 | .flash-container .flash-not-interactive { 74 | cursor: default; 75 | } 76 | .flash-container .flash-message .flash-progress { 77 | position: absolute; 78 | right: 0; 79 | top: auto; 80 | bottom: 0; 81 | left: 0; 82 | width: 0; 83 | height: 3px; 84 | opacity: 1; 85 | background-color: rgba(0, 0, 0, 0.15); 86 | transition: opacity .1s; 87 | } 88 | .flash-container .flash-message .flash-progress.flash-is-hidden { 89 | opacity: 0; 90 | } 91 | .flash-container .flash-message .flash-progress.flash-progress-top { 92 | top: 0; 93 | bottom: auto; 94 | } 95 | .flash-container .flash-message:before { 96 | position: absolute; 97 | content: ''; 98 | width: 7px; 99 | height: 100%; 100 | top: 0; 101 | bottom: 0; 102 | left: -7px; 103 | background-color: transparent; 104 | border-top-left-radius: 4px; 105 | border-bottom-left-radius: 4px; 106 | } 107 | .flash-container .flash-message:after { 108 | position: absolute; 109 | content: ''; 110 | top: 5px; 111 | right: 8px; 112 | text-align: center; 113 | vertical-align: middle; 114 | color: #9e9e9e; 115 | font-family: sc-icons !important; 116 | font-style: normal; 117 | font-weight: normal; 118 | font-variant: normal; 119 | speak: never; 120 | text-rendering: auto; 121 | text-transform: none; 122 | -webkit-font-smoothing: antialiased; 123 | -moz-osx-font-smoothing: grayscale; 124 | } 125 | .flash-container .flash-message.flash-is-visible { 126 | opacity: 1; 127 | transform: translateX(0); 128 | } 129 | /* SUCCESS */ 130 | .flash-container .flash-message.flash-success .flash-progress { 131 | background-color: rgba(76, 175, 80, 0.15); 132 | } 133 | .flash-container .flash-message.flash-success:before { 134 | background-color: #4CAF50; 135 | } 136 | .flash-container .flash-message.flash-success:after { 137 | color: rgba(76, 175, 80, 0.5); 138 | content: var(--success-icon-content); 139 | } 140 | /* WARNING */ 141 | .flash-container .flash-message.flash-warning .flash-progress { 142 | background-color: rgba(255, 133, 27, 0.15); 143 | } 144 | .flash-container .flash-message.flash-warning:before { 145 | background-color: #FF851B; 146 | } 147 | .flash-container .flash-message.flash-warning:after { 148 | color: rgba(255, 133, 27, 0.5); 149 | content: var(--warning-icon-content); 150 | } 151 | /* ERROR */ 152 | .flash-container .flash-message.flash-error .flash-progress { 153 | background-color: rgba(255, 65, 54, 0.15); 154 | } 155 | .flash-container .flash-message.flash-error:before { 156 | background-color: #FF4136; 157 | } 158 | .flash-container .flash-message.flash-error:after { 159 | color: rgba(255, 65, 54, 0.5); 160 | content: var(--error-icon-content); 161 | } 162 | /* INFO */ 163 | .flash-container .flash-message.flash-info .flash-progress { 164 | background-color: rgba(0, 116, 217, 0.15); 165 | } 166 | .flash-container .flash-message.flash-info:before { 167 | background-color: #0074D9; 168 | } 169 | .flash-container .flash-message.flash-info:after { 170 | color: rgba(0, 116, 217, 0.5); 171 | content: var(--info-icon-content); 172 | } 173 | /* BUG */ 174 | .flash-container .flash-message.flash-bug .flash-progress { 175 | background-color: rgba(138, 43, 226, 0.15); 176 | } 177 | .flash-container .flash-message.flash-bug:before { 178 | background-color: #8A2BE2; 179 | } 180 | .flash-container .flash-message.flash-bug:after { 181 | color: rgba(138, 43, 226, 0.5); 182 | content: var(--bug-icon-content); 183 | } 184 | /* DISABLED */ 185 | .flash-container .flash-message.flash-disabled .flash-progress { 186 | background-color: rgba(170, 170, 170, 0.15); 187 | } 188 | .flash-container .flash-message.flash-disabled:before { 189 | background-color: #aaa; 190 | } 191 | .flash-container .flash-message.flash-disabled:after { 192 | color: rgba(170, 170, 170, 0.5); 193 | content: var(--disabled-icon-content); 194 | } 195 | /* DEFAULT */ 196 | .flash-container .flash-message.flash-default { 197 | padding-right: 20px; 198 | border-top-left-radius: 4px; 199 | border-bottom-left-radius: 4px; 200 | } 201 | /* MEDIA */ 202 | @media all and (max-width: 1280px) { 203 | .flash-container { 204 | max-width: 33.33%; 205 | } 206 | } 207 | @media all and (max-width: 768px) { 208 | .flash-container { 209 | max-width: 50%; 210 | } 211 | } 212 | @media all and (max-width: 480px) { 213 | .flash-container { 214 | right: 10px; 215 | left: 10px; 216 | max-width: 100%; 217 | } 218 | } 219 | /* FLASH JS DARK THEME */ 220 | .flash-container .flash-message.dark-theme { 221 | background-color: #2C3433; 222 | color: #fff; 223 | } 224 | .flash-container .flash-message.dark-theme .flash-progress { 225 | background-color: rgba(255, 255, 255, 0.5); 226 | } -------------------------------------------------------------------------------- /Notification/Version 2.36 or below/flash.min.js: -------------------------------------------------------------------------------- 1 | class FlashMessageManager{static DEFAULT_OPTIONS={limit:0,debug:!1};static bag=[];static displayed=[];static _displayQueue=0;constructor(s,e){this.options=FlashMessageManager.DEFAULT_OPTIONS,this.singleMessage=this.formatMessage(s,e),this.checkLimit()}genNewID(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(s=>(s^crypto.getRandomValues(new Uint8Array(1))[0]&15>>s/4).toString(16)))}formatMessage(s,e){if(s||e){return{message:s,flashOptions:e,id:this.genNewID()}}return null}checkLimit(){if(null!==this.singleMessage&&FlashMessageManager.bag.push(this),0===FlashMessageManager.bag.length)return void this.garbageCollection();if(FlashMessageManager._displayQueue>=this.options.limit&&this.options.limit>0)return void(this.options.debug&&console.log("Display limit ("+this.options.limit+") reached, queueing message, bag is holding: "+(FlashMessageManager.bag.length-this.options.limit)+" messages."));const s=FlashMessageManager.bag.find((s=>!FlashMessageManager.displayed.includes(s.singleMessage.id)));s?(this.options.debug&&console.log("Displaying message:",s.singleMessage),FlashMessageManager.create(s.singleMessage),FlashMessageManager.displayed.push(s.singleMessage.id)):this.options.debug&&console.log("Can not find more messages, the bag is probably empty, waiting for confirmation.")}garbageCollection(){this.options.debug&&console.log("Confirmation, bag is empty, garbage collection started."),FlashMessageManager.bag=[],FlashMessageManager.displayed=[],FlashMessageManager._displayQueue=0}static create(s){FlashMessageManager._displayQueue++,new FlashMessage(s.message,s.flashOptions,s.id)}}class FlashMessage{static get DEFAULT_OPTIONS(){return{type:"default",thumb:null,progress:!1,interactive:!0,timeout:8e3,appear_delay:200,remove_delay:600,container:".flash-container",classes:{container:"flash-container",visible:"flash-is-visible",flash:"flash-message",progress:"flash-progress",progress_hidden:"flash-is-hidden"},theme:"default",layout:"top-right",onShow:null,onClick:null,onClose:null}}constructor(s,e,t){this.$_element=null,this.$_message=null,this.interval=null,this.progress_bar=null,this.options={},this.message=s,this.setOptions(e),this.$_container=document.querySelector(this.options.container)||null,this._c_timeout=null,this.$_progress=null,this._progress_value=0,this.$_id=t,this.createContainer(),this.createMessage()}setOptions(s={}){return this.options=Object.assign({},FlashMessage.DEFAULT_OPTIONS,s),this.options}createContainer(){null!==this.$_container&&document.body.contains(this.$_container)||(this.$_container=document.createElement("div"),this.$_container.classList.add(this.options.classes.container),document.body.firstChild?document.body.insertBefore(this.$_container,document.body.firstChild):document.body.appendChild(this.$_container),this.$_container.setAttribute("aria-label","Notification list"),this.$_container.setAttribute("aria-live","polite"),this.$_container.tabIndex=0,this.$_container.classList.add(`${this.options.layout}-flash-layout`))}createMessage(){if(this.$_element=document.createElement("div"),this.$_element.classList.add(this.options.classes.flash,"flash-"+this.options.type),this.$_element.setAttribute("data-reference",this.$_id),this.$_message=document.createElement("span"),this.$_message.classList.add("flash-text"),this.$_message.innerHTML=this.message,this.$_element.appendChild(this.$_message),this.options.thumb){let s=document.createElement("img");s.classList.add("flash-thumb"),s.src=this.options.thumb,this.$_element.classList.add("flash-message-has-thumb"),this.$_element.appendChild(s)}this.$_element.classList.add(`${this.options.theme}-theme`),window.setTimeout((()=>{this.$_element.classList.add(this.options.classes.visible),this.run()}),this.options.appear_delay),this.isInteractive()&&this.bindEvents(),this.$_element.setAttribute("aria-live","polite"),this.$_element.setAttribute("aria-label",this.message),this.$_element.tabIndex=1,this.$_container.appendChild(this.$_element)}run(){this.startProgress(),this.hasProgress()&&(this._c_timeout=window.setTimeout((()=>this.close()),this.options.timeout))}stop(){null!==this._c_timeout&&(window.clearTimeout(this._c_timeout),this.stopProgress(),this._c_timeout=null)}close(){this.$_element.remove();const s=$(this.options.container);0===s.children().length&&s.remove(),FlashMessageManager._displayQueue--,FlashMessageManager.bag=FlashMessageManager.bag.filter((s=>s.singleMessage.id!==this.$_id)),new FlashMessageManager}bindEvents(){this.bindEvent("mouseover",(s=>this.stop())),this.bindEvent("mouseleave",(s=>this.run())),this.bindEvent("click",(s=>this.close()))}unbindEvents(){this.unbindEvent("mouseover",(s=>this.stop())),this.unbindEvent("mouseleave",(s=>this.run())),this.unbindEvent("click",(s=>this.close()))}bindEvent(s,e){this.$_element.addEventListener(s,e,!1)}unbindEvent(s,e){this.$_element.removeEventListener(s,e,!1)}isInteractive(){return this.options.interactive||this.$_element.classList.add("flash-not-interactive"),this.options.interactive}hasProgress(){return Boolean(this.options.progress)}progressBar(){this.$_progress=document.createElement("div"),this.$_progress.classList.add(this.options.classes.progress),this.$_progress.setAttribute("role","progressbar"),this.$_progress.setAttribute("aria-valuemin",0),this.$_progress.setAttribute("aria-valuemax",100),this.$_element.appendChild(this.$_progress)}setProgress(){const s=Date.now()-this._progress_starttime,e=Math.min(1,s/this.options.timeout),t=(100*e).toFixed(2);this.$_progress.setAttribute("aria-valuenow",t),this.$_progress.style.width=t+"%",this._progress_value=t,e>=1?this.stopProgress():requestAnimationFrame(this.setProgress.bind(this))}startProgress(){this.hasProgress()&&(this.$_progress||this.progressBar(),this.stopProgress(),this._progress_starttime=Date.now(),this.$_progress.classList.remove(this.options.classes.progress_hidden),this.setProgress())}stopProgress(){this.hasProgress()&&this.$_progress&&(this.$_progress.classList.add("flash-is-hidden"),this._progress_value=0)}}window.FlashMessageManager=FlashMessageManager,Macro.add("flash",{tags:["progress","Progress","interactive","Interactive","timeout","Timeout","delay","Delay","container","Container","theme","Theme","thumb","Thumb","classContainer","classcontainer","classFlash","classflash","classVisible","classvisible","classProgress","classprogress","classHidden","classhidden","flashtype","flashType","layout","Layout","transition","Transition"],handler:function(){const s=["top-right","middle-right","bottom-right","middle-bottom","bottom-left","middle-left","top-left","middle-top"],e=[],t={type:"default",thumb:null,progress:!0,interactive:!0,timeout:8e3,appear_delay:200,container:".flash-container",theme:"default",layout:"top-right",classes:{container:"flash-container",flash:"flash-message",visible:"flash-is-visible",progress:"flash-progress",progress_hidden:"flash-is-hidden"}};if(this.args.length<=0)return this.error('First argument cannot be skipped, please insert a string via <>.');if(""===this.args[0])return this.error("First argument cannot be an empty string.");for(const a of this.payload)switch(a.name.toLowerCase()){case"flashtype":"string"!=typeof a.args[0]&&e.push("Type must be a string."),t.type=a.args[0];break;case"thumb":"string"!=typeof a.args[0]&&e.push("Thumb must be a string."),t.thumb=a.args[0];break;case"layout":"string"!=typeof a.args[0]&&e.push("Layout must be a string."),s.includes(a.args[0])||e.push("Layout does not include: "+a.args[0]),t.layout=a.args[0];break;case"progress":"boolean"!=typeof a.args[0]&&e.push("Progress must be true or false."),t.progress=a.args[0];break;case"interactive":"boolean"!=typeof a.args[0]&&e.push("Interactive must be true or false."),t.interactive=a.args[0];break;case"timeout":"number"!=typeof a.args[0]&&e.push("Timeout must be a number."),a.args[0]<500&&e.push("Timeout cannot be lower than 500ms."),a.args[0]>1e5&&e.push("Timeout cannot be higher than 100s."),t.timeout=a.args[0];break;case"delay":"number"!=typeof a.args[0]&&e.push("Delay must be a number."),a.args[0]<50&&e.push("Delay cannot be lower than 50ms."),a.args[0]>1e5&&e.push("Delay cannot be higher than 100s."),t.appear_delay=a.args[0];break;case"container":$("."+a.args[0]).length||e.push("Container not found."),t.container=a.args[0];break;case"theme":"string"!=typeof a.args[0]&&e.push("Theme must be a string."),"dark"!==a.args[0]&&e.push('The only theme option is "dark".'),t.theme=a.args[0];break;case"classcontainer":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.container=a.args[0];break;case"classflash":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.flash=a.args[0];break;case"classvisible":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.visible=a.args[0];break;case"classprogress":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.progress=a.args[0];break;case"classhidden":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.progress_hidden=a.args[0];break;case"transition":"boolean"!=typeof a.args[0]&&e.push("Transition must be true or false."),a.args[0]&&$(document).one(":passagestart",(function(s){$(t.container).remove()}))}if(e.length>0)return this.error(e.join("\n"));new FlashMessageManager(this.args[0],{type:t.type,thumb:t.thumb,progress:t.progress,interactive:t.interactive,timeout:t.timeout,appear_delay:t.appear_delay,container:t.container,theme:t.theme,layout:t.layout,classes:{container:t.classes.container,flash:t.classes.flash,visible:t.classes.visible,progress:t.classes.progress,progress_hidden:t.classes.progress_hidden}})}}); -------------------------------------------------------------------------------- /Notification/Version 2.37 or above/flash.min.js: -------------------------------------------------------------------------------- 1 | class FlashMessageManager{static DEFAULT_OPTIONS={limit:0,debug:!1};static bag=[];static displayed=[];static _displayQueue=0;constructor(s,e){this.options=FlashMessageManager.DEFAULT_OPTIONS,this.singleMessage=this.formatMessage(s,e),this.checkLimit()}genNewID(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(s=>(s^crypto.getRandomValues(new Uint8Array(1))[0]&15>>s/4).toString(16)))}formatMessage(s,e){if(s||e){return{message:s,flashOptions:e,id:this.genNewID()}}return null}checkLimit(){if(null!==this.singleMessage&&FlashMessageManager.bag.push(this),0===FlashMessageManager.bag.length)return void this.garbageCollection();if(FlashMessageManager._displayQueue>=this.options.limit&&this.options.limit>0)return void(this.options.debug&&console.log("Display limit ("+this.options.limit+") reached, queueing message, bag is holding: "+(FlashMessageManager.bag.length-this.options.limit)+" messages."));const s=FlashMessageManager.bag.find((s=>!FlashMessageManager.displayed.includes(s.singleMessage.id)));s?(this.options.debug&&console.log("Displaying message:",s.singleMessage),FlashMessageManager.create(s.singleMessage),FlashMessageManager.displayed.push(s.singleMessage.id)):this.options.debug&&console.log("Can not find more messages, the bag is probably empty, waiting for confirmation.")}garbageCollection(){this.options.debug&&console.log("Confirmation, bag is empty, garbage collection started."),FlashMessageManager.bag=[],FlashMessageManager.displayed=[],FlashMessageManager._displayQueue=0}static create(s){FlashMessageManager._displayQueue++,new FlashMessage(s.message,s.flashOptions,s.id)}}class FlashMessage{static get DEFAULT_OPTIONS(){return{type:"default",thumb:null,progress:!1,interactive:!0,timeout:8e3,appear_delay:200,remove_delay:600,container:".flash-container",classes:{container:"flash-container",visible:"flash-is-visible",flash:"flash-message",progress:"flash-progress",progress_hidden:"flash-is-hidden"},theme:"default",layout:"top-right",onShow:null,onClick:null,onClose:null}}constructor(s,e,t){this.$_element=null,this.$_message=null,this.interval=null,this.progress_bar=null,this.options={},this.message=s,this.setOptions(e),this.$_container=document.querySelector(this.options.container)||null,this._c_timeout=null,this.$_progress=null,this._progress_value=0,this.$_id=t,this.createContainer(),this.createMessage()}setOptions(s={}){return this.options=Object.assign({},FlashMessage.DEFAULT_OPTIONS,s),this.options}createContainer(){null!==this.$_container&&document.body.contains(this.$_container)||(this.$_container=document.createElement("div"),this.$_container.classList.add(this.options.classes.container),document.body.firstChild?document.body.insertBefore(this.$_container,document.body.firstChild):document.body.appendChild(this.$_container),this.$_container.setAttribute("aria-label","Notification list"),this.$_container.setAttribute("aria-live","polite"),this.$_container.tabIndex=0,this.$_container.classList.add(`${this.options.layout}-flash-layout`))}createMessage(){if(this.$_element=document.createElement("div"),this.$_element.classList.add(this.options.classes.flash,"flash-"+this.options.type),this.$_element.setAttribute("data-reference",this.$_id),this.$_message=document.createElement("span"),this.$_message.classList.add("flash-text"),this.$_message.innerHTML=this.message,this.$_element.appendChild(this.$_message),this.options.thumb){let s=document.createElement("img");s.classList.add("flash-thumb"),s.src=this.options.thumb,this.$_element.classList.add("flash-message-has-thumb"),this.$_element.appendChild(s)}this.$_element.classList.add(`${this.options.theme}-theme`),window.setTimeout((()=>{this.$_element.classList.add(this.options.classes.visible),this.run()}),this.options.appear_delay),this.isInteractive()&&this.bindEvents(),this.$_element.setAttribute("aria-live","polite"),this.$_element.setAttribute("aria-label",this.message),this.$_element.tabIndex=1,this.$_container.appendChild(this.$_element)}run(){this.startProgress(),this.hasProgress()&&(this._c_timeout=window.setTimeout((()=>this.close()),this.options.timeout))}stop(){null!==this._c_timeout&&(window.clearTimeout(this._c_timeout),this.stopProgress(),this._c_timeout=null)}close(){this.$_element.remove();const s=$(this.options.container);0===s.children().length&&s.remove(),FlashMessageManager._displayQueue--,FlashMessageManager.bag=FlashMessageManager.bag.filter((s=>s.singleMessage.id!==this.$_id)),new FlashMessageManager}bindEvents(){this.bindEvent("mouseover",(s=>this.stop())),this.bindEvent("mouseleave",(s=>this.run())),this.bindEvent("click",(s=>this.close()))}unbindEvents(){this.unbindEvent("mouseover",(s=>this.stop())),this.unbindEvent("mouseleave",(s=>this.run())),this.unbindEvent("click",(s=>this.close()))}bindEvent(s,e){this.$_element.addEventListener(s,e,!1)}unbindEvent(s,e){this.$_element.removeEventListener(s,e,!1)}isInteractive(){return this.options.interactive||this.$_element.classList.add("flash-not-interactive"),this.options.interactive}hasProgress(){return Boolean(this.options.progress)}progressBar(){this.$_progress=document.createElement("div"),this.$_progress.classList.add(this.options.classes.progress),this.$_progress.setAttribute("role","progressbar"),this.$_progress.setAttribute("aria-valuemin",0),this.$_progress.setAttribute("aria-valuemax",100),this.$_element.appendChild(this.$_progress)}setProgress(){const s=Date.now()-this._progress_starttime,e=Math.min(1,s/this.options.timeout),t=(100*e).toFixed(2);this.$_progress.setAttribute("aria-valuenow",t),this.$_progress.style.width=t+"%",this._progress_value=t,e>=1?this.stopProgress():requestAnimationFrame(this.setProgress.bind(this))}startProgress(){this.hasProgress()&&(this.$_progress||this.progressBar(),this.stopProgress(),this._progress_starttime=Date.now(),this.$_progress.classList.remove(this.options.classes.progress_hidden),this.setProgress())}stopProgress(){this.hasProgress()&&this.$_progress&&(this.$_progress.classList.add("flash-is-hidden"),this._progress_value=0)}}window.FlashMessageManager=FlashMessageManager,Macro.add("flash",{tags:["progress","Progress","interactive","Interactive","timeout","Timeout","delay","Delay","container","Container","theme","Theme","thumb","Thumb","classContainer","classcontainer","classFlash","classflash","classVisible","classvisible","classProgress","classprogress","classHidden","classhidden","flashtype","flashType","layout","Layout","transition","Transition"],handler:function(){const s=["top-right","middle-right","bottom-right","middle-bottom","bottom-left","middle-left","top-left","middle-top"],e=[],t={type:"default",thumb:null,progress:!0,interactive:!0,timeout:8e3,appear_delay:200,container:".flash-container",theme:"default",layout:"top-right",classes:{container:"flash-container",flash:"flash-message",visible:"flash-is-visible",progress:"flash-progress",progress_hidden:"flash-is-hidden"}};if(this.args.length<=0)return this.error('First argument cannot be skipped, please insert a string via <>.');if(""===this.args[0])return this.error("First argument cannot be an empty string.");for(const a of this.payload)switch(a.name.toLowerCase()){case"flashtype":"string"!=typeof a.args[0]&&e.push("Type must be a string."),t.type=a.args[0];break;case"thumb":"string"!=typeof a.args[0]&&e.push("Thumb must be a string."),t.thumb=a.args[0];break;case"layout":"string"!=typeof a.args[0]&&e.push("Layout must be a string."),s.includes(a.args[0])||e.push("Layout does not include: "+a.args[0]),t.layout=a.args[0];break;case"progress":"boolean"!=typeof a.args[0]&&e.push("Progress must be true or false."),t.progress=a.args[0];break;case"interactive":"boolean"!=typeof a.args[0]&&e.push("Interactive must be true or false."),t.interactive=a.args[0];break;case"timeout":"number"!=typeof a.args[0]&&e.push("Timeout must be a number."),a.args[0]<500&&e.push("Timeout cannot be lower than 500ms."),a.args[0]>1e5&&e.push("Timeout cannot be higher than 100s."),t.timeout=a.args[0];break;case"delay":"number"!=typeof a.args[0]&&e.push("Delay must be a number."),a.args[0]<50&&e.push("Delay cannot be lower than 50ms."),a.args[0]>1e5&&e.push("Delay cannot be higher than 100s."),t.appear_delay=a.args[0];break;case"container":$("."+a.args[0]).length||e.push("Container not found."),t.container=a.args[0];break;case"theme":"string"!=typeof a.args[0]&&e.push("Theme must be a string."),"dark"!==a.args[0]&&e.push('The only theme option is "dark".'),t.theme=a.args[0];break;case"classcontainer":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.container=a.args[0];break;case"classflash":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.flash=a.args[0];break;case"classvisible":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.visible=a.args[0];break;case"classprogress":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.progress=a.args[0];break;case"classhidden":"string"!=typeof a.args[0]&&e.push("Class must be a string."),t.classes.progress_hidden=a.args[0];break;case"transition":"boolean"!=typeof a.args[0]&&e.push("Transition must be true or false."),a.args[0]&&$(document).one(":passagestart",(function(s){$(t.container).remove()}))}if(e.length>0)return this.error(e.join("\n"));new FlashMessageManager(this.args[0],{type:t.type,thumb:t.thumb,progress:t.progress,interactive:t.interactive,timeout:t.timeout,appear_delay:t.appear_delay,container:t.container,theme:t.theme,layout:t.layout,classes:{container:t.classes.container,flash:t.classes.flash,visible:t.classes.visible,progress:t.classes.progress,progress_hidden:t.classes.progress_hidden}})}}); -------------------------------------------------------------------------------- /Notification/README.md: -------------------------------------------------------------------------------- 1 | # Flash 2 | 3 | Advanced notification system with 16 optional settings and 2 global settings. 4 | 5 | ## Installation 6 | 7 | Please check your SugarCube version and use the appropriate folder for the next steps! 8 | 9 | If using the Twine desktop/web app, copy contents of `flash.js` to the `Story JavaScript` section, and copy contents of `flash.css` to the `Story Stylesheet` section. 10 | 11 | If using a compiler like Tweego, drop `flash.js` and `flash.css` to your source folder. 12 | 13 | The `.js` and `.css` are also available in a `.min` form, so it takes up less space. 14 | 15 | ## Example Usage 16 | 17 | The following example uses the default values of flash. 18 | 19 | ```html 20 | <> 21 | <> 22 | ``` 23 | ![Flash example](../Resources/Gif/example4.gif) 24 | --- 25 | 26 | ## Usage - Flash arguments 27 | 28 | 1. 'Flash' has sixteen (yes 16) arguments which allow things to be customized for your desire. I recommend staying away from 7, and 10 to and including 14, unless you know what you're doing. 29 | - [Input text](#Text), there is no default, this must be user provided. (Argument 1) 30 | - [Type](#Type), default is `default`, (Argument 2) 31 | - [Progress](#Progress), default is `true` (Argument 3) 32 | - [Interactive](#Interactive), default is `true`. (Argument 4) 33 | - [Timeout](#Timeout), default is `8000`ms. (Argument 5) 34 | - [Delay](#Delay), default is `200`ms. (Argument 6) 35 | - [Container](#Container), default is `.flash-container`. (Argument 7) 36 | - [Theme](#Theme), default is `default`. (Argument 8) 37 | - [Layout](#Layout), default is `top-right`. (Argument 9) 38 | - [Container Class](#ContainerClass), default is `flash-container`. (Argument 10) 39 | - [Flash Class](#FlashClass), default is `flash-message`. (Argument 11) 40 | - [Visible Class](#VisibleClass), default is `is-visible`. (Argument 12) 41 | - [Progress Class](#ProgressClass), default is `flash-progress`. (Argument 13) 42 | - [Hidden Class](#HiddenClass), default is `is-hidden`. (Argument 14) 43 | - [Transition](#Transition), default is `false`. (Argument 15) 44 | - [Thumb](#Thumb), default is `empty`. (Argument 16) 45 | - [Styling example](#Styling), an example of styling of the `css` itself, this is not the recommended way of doing this. 46 | 2. 'Flash' Can accept custom types (before you had to insert a custom type, this is no longer the case). 47 | - [Inserting new type](#TypeInsert), No longer supported, merged automatically with Argument 2. 48 | - [Using new Type](#Type), refer to Argument 2 for usage. 49 | - [Important styling](#StylingType), if you want to have custom styling, please copy and edit the `css` found here. Take a look at the example. 50 | 3. 'Flash' can also be called directly through javascript 51 | - [Javascript methods](#Javascript), here you can find a way to fully control and customize everything. **NOT** recommended for authors without knowledge of JavaScript. 52 | 4. 'Flash' has two global settings, a maximum allowed flashes at the same time and a debug mode for console messages. 53 | - [Limitation](#Limitation), here you can find how to limit the maximum amount of flashes at the same time. 54 | - [Debug](#Debug), debugging messages in the console to check what is happening while flashes are generated. 55 | 56 | 57 | **Argument placement:** 58 | (You can cherry pick which arguments to use) 59 | ```html 60 | <> 61 | <> 62 | <> 63 | <> 64 | <> 65 | <> 66 | <> 67 | <> 68 | <> 69 | <> 70 | <> 71 | <> 72 | <> 73 | <> 74 | <> 75 | <> 76 | <> 77 | ``` 78 | 79 | **Example:** 80 | 81 | ```html 82 | <> 83 | <> 84 | <> 85 | <> 86 | <> 87 | <> 88 | ``` 89 | ![flash example](../Resources/Gif/example4a.gif) 90 | --- 91 | 92 | ## Text 93 | 94 | - `Argument1`: *(string)* text. 95 | 96 | 1. 'Flash' has no default, user must submit a string with text. 97 | - Only UTF-8 is accepted. 98 | - Must be user supplied. 99 | - Flash accepts `$var` and math, for example, `$x + $y`. 100 | 101 | ## Type 102 | 103 | - `Argument2`: *(string)* flash type. 104 | 105 | 1. 'flash' has a default flash type set to `default`. 106 | - Only string are accepted. 107 | - The following default strings are accepted: `success`, `warning`, `error`, `info`, `bug`, `disabled`, `default`. 108 | - If inserting a custom type, for example `wizardry` be sure to check [Important styling](#StylingType). 109 | 110 | ## Progress 111 | 112 | - `Argument3`: *(boolean)* progress bar and whether the notification dissappears on its own. 113 | 114 | 1. 'flash' has a default progress set to `true`. 115 | - Only booleans are accepted. 116 | - Setting this to `false` will cause the notification to permanently stay unless click if `interactive = true`. 117 | - If combined with [a global limitation](#Limitation), please note that mixing `true` and `false` flashes could cause queued flashes to never display, thus bugging out. 118 | 119 | ## Interactive 120 | 121 | - `Argument4`: *(boolean)* interaction. 122 | 123 | 1. 'flash' has a default interaction set to `true`. 124 | - Only booleans are accepted. 125 | - Setting this to `false` will block people from dismissing the notification through clicking. 126 | 127 | ## Timeout 128 | 129 | - `Argument5`: *(integer)* time-out in milliseconds. 130 | 131 | 1. 'flash' has a default time-out set to `8000`ms or `8`s. 132 | - Only integers are accepted. 133 | - Minimum will be `500` or an error will be thrown. 134 | - Maximum will be `100000` or an error will be thrown. 135 | 136 | ## Delay 137 | 138 | - `Argument6`: *(integer)* delay in milliseconds. 139 | 140 | 1. 'flash' has a default delay set to `200`ms or `0.2`s. 141 | - Only integers are accepted. 142 | - Minimum will be `50` or an error will be thrown. 143 | - Maximum will be `100000` or an error will be thrown. 144 | 145 | ## Container 146 | 147 | - `Argument7`: *(string)* flash container. 148 | 149 | 1. 'flash' has a default container set to `.flash-container`. 150 | - Only strings are accepted. 151 | 152 | **NOTE:** The element must exist before the macro is called. 153 | 154 | ## Theme 155 | 156 | - `Argument8`: *(string)* theme. 157 | 158 | 1. 'flash' has a default theme set to `default`. 159 | - Only string are accepted. 160 | - The following strings are accepted: `dark`, `default`. 161 | 162 | ## Layout 163 | 164 | - `Argument9`: *(string)* layout. 165 | 166 | 1. 'flash' has a default layout set to `top-right`. 167 | - Only string are accepted. 168 | - The following strings are accepted: `top-right`, `middle-right`, `bottom-right`, `middle-bottom`, `bottom-left`, `middle-left`, `top-left`, `middle-top`. 169 | - When a container is made, this can not be changed dynamically, wait for the container to be destroyed to change to a new layout. 170 | 171 | ## ContainerClass 172 | 173 | - `Argument10`: *(string)* flash container class. 174 | 175 | 1. 'flash' has a default flash container class set to `.flash-container`. 176 | - Only string are accepted. 177 | - Please place a `.` or `#` in the argument to imply whether the custom container is selected through an ID or Class. 178 | - The container is automatically removed after usage. This is expected behaviour because the container should also be dynamically made right before the macro call. This is because the element **must** exist. I recommend pairing it with a widget to create the container, and right after call the macro. 179 | 180 | **NOTE:** This breaks ALL existing `css` rules. Thus, the user must supply their own. 181 | 182 | ## FlashClass 183 | 184 | - `Argument11`: *(string)* flash class. 185 | 186 | 1. 'flash' has a default flash class set to `flash-message`. 187 | - Only string are accepted. 188 | 189 | **NOTE:** This breaks ALL existing `css` rules. Thus, the user must supply their own. 190 | 191 | ## VisibleClass 192 | 193 | - `Argument12`: *(string)* flash visible class. 194 | 195 | 1. 'flash' has a default flash visible class set to `is-visible`. 196 | - Only string are accepted. 197 | 198 | **NOTE:** This breaks ALL existing `css` rules. Thus, the user must supply their own. 199 | 200 | ## ProgressClass 201 | 202 | - `Argument13`: *(string)* flash progress class. 203 | 204 | 1. 'flash' has a default flash progress class set to `flash-progress`. 205 | - Only string are accepted. 206 | 207 | **NOTE:** This breaks ALL existing `css` rules. Thus, the user must supply their own. 208 | 209 | ## HiddenClass 210 | 211 | - `Argument14`: *(string)* flash progress hidden class. 212 | 213 | 1. 'flash' has a default flash progress hidden class set to `is-hidden`. 214 | - Only string are accepted. 215 | 216 | **NOTE:** This breaks ALL existing `css` rules. Thus, the user must supply their own. 217 | 218 | ## Transition 219 | 220 | - `Argument15`: *(boolean)* transition container removal. 221 | 222 | 1. 'flash' has a default transition container removal set to `false`. 223 | - Only booleans are accepted. 224 | - If set to `true` flash removes its container upon passage transition. To explain it more simple: when a player clicks a link, the flash message stays until its timer runs out or until it's clicked. If this argument is set to `true`, the messages get removed when a player clicks a link. 225 | 226 | ## Thumb 227 | 228 | - `Argument16`: *(string)* creates an image. 229 | 230 | 1. 'flash' can create `` within the flash body. 231 | - Only strings are accepted. 232 | - Can be styled with the class `.flash-thumb`. 233 | - Parent (the container) can be styled with the class `.flash-message-has-thumb`. 234 | - ` [img[url]]` is not accepted (maybe some day). 235 | 236 | ## TypeInsert 237 | 238 | 1. `NO LONGER SUPPORTED`: custom types are now directly useable from argument 2. 239 | 240 | ## StylingType 241 | 242 | 1. `Story Stylesheet`: *(strings)*'s to make sure your newly added type actually does something we **must** give it custom styling. Please edit 'Wizardry' with your own custom name(s). 243 | 244 | - ```css 245 | /* Place the css below in your story stylesheet PLEASE PICK THE CORRECT VERSION FOR THE CONTENT!*/ 246 | .flash-container .flash-message.flash-wizardry .flash-progress { 247 | /* Progress bar color */ 248 | background-color: rgba(255, 153, 0, 0.15); 249 | } 250 | .flash-container .flash-message.flash-wizardry:before { 251 | /* Left line color */ 252 | background-color: #ffcc00; 253 | } 254 | .flash-container .flash-message.flash-wizardry:after { 255 | /* Icon color */ 256 | color: rgba(255, 179, 0, 0.5); 257 | /* Icon from tme-fa-icons VERSION 2.36 */ 258 | content: '\e83a'; 259 | /* Icon from tme-fa-icons VERSION 2.37 */ 260 | content: '\f0d0'; 261 | /* Change this if you want to use font-awesome icons or something else */ 262 | font-family: "tme-fa-icons"; 263 | } 264 | ``` 265 | - ```html 266 | /* Place the macro in the passage you want to use it in */ 267 | <> 268 | <> 269 | <> 270 | ``` 271 | 272 | ![flash styling example 2](../Resources/Gif/example4d.gif) 273 | 274 | ## Styling 275 | 276 | 1. 'flash' uses a nested `
` as output. 277 | - `
`: *(class)*. 278 | 279 | **Example:** 280 | 281 | ```css 282 | .flash-container { 283 | z-index: 1000; 284 | max-width: 50%; 285 | position: fixed; 286 | } 287 | .flash-message { 288 | border: double 10px; 289 | color: red; 290 | } 291 | ``` 292 | **NOTE:** It's not recommended to touch anything out of the VARIABLE theme settings. 293 | 294 | ![flash styling example](../Resources/Gif/example4b.gif) 295 | 296 | ## Javascript 297 | 298 | 1. This is how to use JavaScript to create flash messages with the default parameters. 299 | - ```js 300 | new FlashMessageManager( 301 | /* Text to be displayed. */ 302 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 303 | /* Other Settings */ 304 | { 305 | type: bug, 306 | progress: true, 307 | interactive: true, 308 | timeout: 8000, 309 | appear_delay: 200, 310 | container: '.flash-container', 311 | theme: 'default', 312 | layout: 'top-right', 313 | classes: { 314 | container: 'flash-container', 315 | flash: 'flash-message', 316 | visible: 'flash-is-visible', 317 | progress: 'flash-progress', 318 | progress_hidden: 'flash-is-hidden' 319 | } 320 | }); 321 | ``` 322 | 323 | 2. To quickly test in your SugarCube project, insert: 324 | - ```html 325 | <> 348 | ``` 349 | 350 | ## Limitation 351 | 352 | 1. `StoryInit`: *(integer)* to set a limit on the messages to display. Flash will queue messages when the limit has been reached. Place the following in `StoryInit`: 353 | - `<>` -> `x` is the maximum, the `default` is `0`, which means unlimited. 354 | - Theoretically this setting can be changed everywhere within a story, but, if doing this, there is a high chance of weirdness. 355 | 356 | ## Debug 357 | 358 | 1. `StoryInit`: *(boolean)* to activate debugging mode, so you can see what is happening. Place the following in `StoryInit`: 359 | - `<>` -> `x` is either `true` to display them in console, or `false` (default) to not generate them. 360 | - Theoretically this setting can be changed everywhere within a story, but, if doing this, there is a high chance of weirdness. -------------------------------------------------------------------------------- /Notification/Version 2.36 or below/flash.js: -------------------------------------------------------------------------------- 1 | // FlashMessage macro by SjoerdHekking 2 | // FireFox progressbar bug fixed by Goctionni 3 | class FlashMessageManager { 4 | static DEFAULT_OPTIONS = { 5 | limit: 0, 6 | debug: false 7 | }; 8 | 9 | static bag = []; 10 | static displayed = []; 11 | static _displayQueue = 0; 12 | 13 | constructor(message, flashOptions) { 14 | this.options = FlashMessageManager.DEFAULT_OPTIONS; 15 | this.singleMessage = this.formatMessage(message, flashOptions); 16 | this.checkLimit(); 17 | } 18 | 19 | /** 20 | * Sets a random ID 21 | * @returns {String} UUID4 format. 22 | */ 23 | genNewID() { 24 | return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); 25 | } 26 | 27 | /** 28 | * Formats a message to a bag-able object with an unique ID. 29 | * @param {string} message The text to format 30 | * @param {Object} flashOptions The options to pass down 31 | * @returns Formatted message 32 | */ 33 | formatMessage(message, flashOptions) { 34 | if (!message && !flashOptions) { 35 | return null; 36 | } else { 37 | const tempMessage = { 38 | message: message, 39 | flashOptions: flashOptions, 40 | id: this.genNewID() 41 | }; 42 | return tempMessage; 43 | } 44 | } 45 | 46 | /** 47 | * Check if a display limit has been set, if so, queue message, else pass them down. 48 | * @returns Notification creation or queueing 49 | */ 50 | checkLimit() { 51 | if (this.singleMessage !== null) { 52 | FlashMessageManager.bag.push(this); 53 | } 54 | 55 | if (FlashMessageManager.bag.length === 0 ) { 56 | this.garbageCollection(); 57 | return; 58 | } 59 | 60 | if ((FlashMessageManager._displayQueue >= this.options.limit) && (this.options.limit > 0)) { 61 | // FlashMessageManager.bag.length > this.options.limit 62 | if (this.options.debug) { 63 | console.log("Display limit ("+this.options.limit+") reached, queueing message, bag is holding: "+(FlashMessageManager.bag.length - this.options.limit)+" messages."); 64 | } 65 | return; 66 | } 67 | 68 | const nextMessage = FlashMessageManager.bag.find( 69 | (instance) => !FlashMessageManager.displayed.includes(instance.singleMessage.id) 70 | ); 71 | 72 | if (!nextMessage) { 73 | if (this.options.debug) { 74 | console.log("Can not find more messages, the bag is probably empty, waiting for confirmation."); 75 | } 76 | return; 77 | } 78 | 79 | if (this.options.debug) { 80 | console.log("Displaying message:", nextMessage.singleMessage); 81 | } 82 | 83 | FlashMessageManager.create(nextMessage.singleMessage); 84 | FlashMessageManager.displayed.push(nextMessage.singleMessage.id); 85 | } 86 | 87 | /** 88 | * If the bag has been emptied, be sure to clean the history even if it is cleaned. 89 | */ 90 | garbageCollection() { 91 | if (this.options.debug) { 92 | console.log("Confirmation, bag is empty, garbage collection started."); 93 | } 94 | FlashMessageManager.bag = []; 95 | FlashMessageManager.displayed = []; 96 | FlashMessageManager._displayQueue = 0; 97 | } 98 | 99 | /** 100 | * Forward the message formatted and checked to the FlashMessage class. 101 | * @param {Object} singleMessage Holding the message itself (String), the options (Object), and lastly the unique ID. 102 | */ 103 | static create(singleMessage) { 104 | FlashMessageManager._displayQueue++; 105 | new FlashMessage(singleMessage.message, singleMessage.flashOptions, singleMessage.id); 106 | } 107 | } 108 | 109 | class FlashMessage { 110 | static get DEFAULT_OPTIONS() { 111 | return { 112 | type: "default", 113 | thumb: null, 114 | progress: false, 115 | interactive: true, 116 | timeout: 8000, 117 | appear_delay: 200, 118 | remove_delay: 600, 119 | container: ".flash-container", 120 | classes: { 121 | container: "flash-container", 122 | visible: "flash-is-visible", 123 | flash: "flash-message", 124 | progress: "flash-progress", 125 | progress_hidden: "flash-is-hidden" 126 | }, 127 | theme: "default", 128 | layout: "top-right", 129 | onShow: null, 130 | onClick: null, 131 | onClose: null 132 | }; 133 | } 134 | 135 | constructor(message, options, id) { 136 | this.$_element = null; 137 | this.$_message = null; 138 | this.interval = null; 139 | this.progress_bar = null; 140 | this.options = {}; 141 | this.message = message; 142 | this.setOptions(options); 143 | this.$_container = document.querySelector(this.options.container) || null; 144 | this._c_timeout = null; 145 | this.$_progress = null; 146 | this._progress_value = 0; 147 | this.$_id = id; 148 | this.createContainer(); 149 | this.createMessage(); 150 | } 151 | 152 | /** 153 | * An empty object is used a base, merge defaults into it, then user options. 154 | * @param {Object} options - FlashMessage options. 155 | * @returns {Object} - Modified FlashMessage options. 156 | */ 157 | setOptions(options = {}) { 158 | this.options = Object.assign({}, FlashMessage.DEFAULT_OPTIONS, options); 159 | return this.options; 160 | } 161 | 162 | /** 163 | * Create the container for the flash messages. 164 | * @returns - (If container exists do not create a new one, method returns.) 165 | */ 166 | createContainer() { 167 | if (this.$_container !== null && document.body.contains(this.$_container)) { 168 | return; 169 | } 170 | 171 | // Create the parent div with class 172 | this.$_container = document.createElement("div"); 173 | this.$_container.classList.add(this.options.classes.container); 174 | 175 | // check if body has children, ensure div always generates on top 176 | if (document.body.firstChild) { 177 | document.body.insertBefore(this.$_container, document.body.firstChild); 178 | } else { 179 | document.body.appendChild(this.$_container); 180 | } 181 | 182 | // Accessibility enforcing 183 | this.$_container.setAttribute("aria-label", "Notification list"); 184 | this.$_container.setAttribute("aria-live", "polite"); 185 | this.$_container.tabIndex = 0; 186 | 187 | // prepare layout 188 | this.$_container.classList.add(`${this.options.layout}-flash-layout`); 189 | } 190 | 191 | /** 192 | * Initialize the message, e.g. the child of the container. 193 | */ 194 | createMessage() { 195 | // Create child container that holds the message 196 | this.$_element = document.createElement("div"); 197 | this.$_element.classList.add(this.options.classes.flash, "flash-" + this.options.type); 198 | this.$_element.setAttribute("data-reference", this.$_id); 199 | 200 | // create message 201 | this.$_message = document.createElement("span"); 202 | this.$_message.classList.add("flash-text"); 203 | this.$_message.innerHTML = this.message; 204 | this.$_element.appendChild(this.$_message); 205 | 206 | // if a custom image is needed it can be added 207 | if (this.options.thumb) { 208 | let imgElement = document.createElement("img"); 209 | imgElement.classList.add("flash-thumb"); 210 | imgElement.src = this.options.thumb; 211 | this.$_element.classList.add("flash-message-has-thumb"); 212 | this.$_element.appendChild(imgElement); 213 | } 214 | 215 | // set the theme 216 | this.$_element.classList.add(`${this.options.theme}-theme`); 217 | 218 | // create progress bar 219 | 220 | // create start of the progress 221 | window.setTimeout( 222 | () => { 223 | this.$_element.classList.add(this.options.classes.visible); 224 | this.run(); 225 | }, this.options.appear_delay 226 | ); 227 | 228 | // check if interactive, if so, bind events. 229 | if (this.isInteractive()) { 230 | this.bindEvents(); 231 | } 232 | 233 | // accessibility add 234 | this.$_element.setAttribute("aria-live", "polite"); 235 | this.$_element.setAttribute("aria-label", this.message); 236 | this.$_element.tabIndex = 1; 237 | 238 | // append to container 239 | this.$_container.appendChild(this.$_element); 240 | } 241 | 242 | /** 243 | * Initialize the progress and make a timed event. 244 | */ 245 | run() { 246 | this.startProgress() 247 | if (this.hasProgress()) { 248 | this._c_timeout = window.setTimeout(() => this.close(), this.options.timeout) 249 | } 250 | } 251 | 252 | /** 253 | * Stop the progress and reset the timeout. 254 | */ 255 | stop() { 256 | if (this._c_timeout !== null) { 257 | window.clearTimeout(this._c_timeout); 258 | this.stopProgress(); 259 | this._c_timeout = null; 260 | } 261 | } 262 | 263 | /** 264 | * Remove the element from the parent. If the parent is empty, remove the parent. 265 | */ 266 | close() { 267 | this.$_element.remove(); 268 | const container = $(this.options.container); 269 | if (container.children().length === 0 ) { 270 | container.remove(); 271 | } 272 | 273 | FlashMessageManager._displayQueue--; 274 | FlashMessageManager.bag = FlashMessageManager.bag.filter((item) => item.singleMessage.id !== this.$_id); 275 | new FlashMessageManager(); 276 | } 277 | 278 | /** 279 | * Bind mouseover, mouseleave and click events 280 | */ 281 | bindEvents() { 282 | this.bindEvent('mouseover', _ => this.stop()) 283 | this.bindEvent('mouseleave', _ => this.run()) 284 | this.bindEvent('click', _ => this.close()) 285 | } 286 | 287 | /** 288 | * Unbind mouseover, mouseleave and click events 289 | */ 290 | unbindEvents() { 291 | this.unbindEvent('mouseover', _ => this.stop()) 292 | this.unbindEvent('mouseleave', _ => this.run()) 293 | this.unbindEvent('click', _ => this.close()) 294 | } 295 | 296 | /** 297 | * Initialize the events and callback methods. 298 | * @param {String} eventName - mouseover/mouseleave/click 299 | * @param {String} eventHandler - what method to call 300 | */ 301 | bindEvent(eventName, eventHandler) { 302 | this.$_element.addEventListener(eventName, eventHandler, false); 303 | } 304 | 305 | /** 306 | * Unbind the events and callback methods. 307 | * @param {String} eventName - mouseover/mouseleave/click 308 | * @param {String} eventHandler - what method to call 309 | */ 310 | unbindEvent(eventName, eventHandler) { 311 | this.$_element.removeEventListener(eventName, eventHandler, false); 312 | } 313 | 314 | /** 315 | * Checks if interactivity is allowed, if not, add a class. 316 | * @returns {Boolean} true / false 317 | */ 318 | isInteractive() { 319 | if (!this.options.interactive) { 320 | this.$_element.classList.add("flash-not-interactive"); 321 | } 322 | return this.options.interactive; 323 | } 324 | 325 | /** 326 | * Check if progress is true. 327 | * @returns {Boolean} true / false 328 | */ 329 | hasProgress() { 330 | return Boolean(this.options.progress); 331 | } 332 | 333 | /** 334 | * Creates the progress div. 335 | */ 336 | progressBar() { 337 | this.$_progress = document.createElement("div"); 338 | this.$_progress.classList.add(this.options.classes.progress); 339 | this.$_progress.setAttribute("role", "progressbar"); 340 | this.$_progress.setAttribute("aria-valuemin", 0); 341 | this.$_progress.setAttribute("aria-valuemax", 100); 342 | this.$_element.appendChild(this.$_progress); 343 | } 344 | 345 | /** 346 | * Set and update the progress. 347 | */ 348 | setProgress() { 349 | const elapsed = Date.now() - this._progress_starttime; 350 | const pct = Math.min(1, elapsed / this.options.timeout); 351 | const width = (pct * 100).toFixed(2); 352 | this.$_progress.setAttribute("aria-valuenow", width); 353 | this.$_progress.style.width = width + "%"; 354 | this._progress_value = width; 355 | if (pct >= 1) { 356 | this.stopProgress(); 357 | } else { 358 | requestAnimationFrame(this.setProgress.bind(this)); 359 | } 360 | } 361 | 362 | /** 363 | * Initialize the progress. 364 | */ 365 | startProgress() { 366 | if (this.hasProgress()) { 367 | if (!this.$_progress) { 368 | this.progressBar(); 369 | } 370 | this.stopProgress(); 371 | this._progress_starttime = Date.now(); 372 | this.$_progress.classList.remove(this.options.classes.progress_hidden); 373 | this.setProgress(); 374 | } 375 | } 376 | 377 | /** 378 | * Stop progress. 379 | */ 380 | stopProgress() { 381 | if (this.hasProgress() && this.$_progress) { 382 | this.$_progress.classList.add("flash-is-hidden"); 383 | this._progress_value = 0; 384 | } 385 | } 386 | } 387 | 388 | window.FlashMessageManager = FlashMessageManager; 389 | 390 | Macro.add("flash", { 391 | tags: ["progress", "Progress", "interactive", "Interactive", "timeout", "Timeout", "delay", "Delay", "container", "Container", "theme", "Theme", "thumb", "Thumb", "classContainer", "classcontainer", "classFlash", "classflash", "classVisible", "classvisible", "classProgress", "classprogress", "classHidden", "classhidden", "flashtype", "flashType", "layout", "Layout", "transition", "Transition"], 392 | handler: function () { 393 | const layoutArray = ["top-right", "middle-right", "bottom-right", "middle-bottom", "bottom-left", "middle-left", "top-left", "middle-top"]; 394 | const errorArray = []; 395 | const defaultOptions = { 396 | type: "default", 397 | thumb: null, 398 | progress: true, 399 | interactive: true, 400 | timeout: 8000, 401 | appear_delay: 200, 402 | container: '.flash-container', 403 | theme: 'default', 404 | layout: 'top-right', 405 | classes: { 406 | container: 'flash-container', 407 | flash: 'flash-message', 408 | visible: 'flash-is-visible', 409 | progress: 'flash-progress', 410 | progress_hidden: 'flash-is-hidden' 411 | } 412 | }; 413 | 414 | if(this.args.length <= 0) 415 | return this.error("First argument cannot be skipped, please insert a string via <>."); 416 | if(this.args[0] === "") 417 | return this.error("First argument cannot be an empty string."); 418 | 419 | for (const pay of this.payload) { 420 | switch (pay.name.toLowerCase()) { 421 | case "flashtype": 422 | if (!(typeof pay.args[0] == "string")) 423 | errorArray.push("Type must be a string."); 424 | defaultOptions.type = pay.args[0]; 425 | break; 426 | case "thumb": 427 | if (!(typeof pay.args[0] == "string")) 428 | errorArray.push("Thumb must be a string."); 429 | defaultOptions.thumb = pay.args[0]; 430 | break; 431 | case "layout": 432 | if (!(typeof pay.args[0] == "string")) 433 | errorArray.push("Layout must be a string."); 434 | if (!(layoutArray.includes(pay.args[0]))) 435 | errorArray.push("Layout does not include: "+pay.args[0]); 436 | defaultOptions.layout = pay.args[0]; 437 | break; 438 | case "progress": 439 | if (!(typeof pay.args[0] == "boolean")) 440 | errorArray.push("Progress must be true or false."); 441 | defaultOptions.progress = pay.args[0]; 442 | break; 443 | case "interactive": 444 | if (!(typeof pay.args[0] == "boolean")) 445 | errorArray.push("Interactive must be true or false."); 446 | defaultOptions.interactive = pay.args[0]; 447 | break; 448 | case "timeout": 449 | if (!(typeof pay.args[0] == "number")) 450 | errorArray.push("Timeout must be a number."); 451 | if (pay.args[0] < 500) 452 | errorArray.push("Timeout cannot be lower than 500ms."); 453 | if (pay.args[0] > 100000) 454 | errorArray.push("Timeout cannot be higher than 100s."); 455 | defaultOptions.timeout = pay.args[0]; 456 | break; 457 | case "delay": 458 | if (!(typeof pay.args[0] == "number")) 459 | errorArray.push("Delay must be a number."); 460 | if (pay.args[0] < 50) 461 | errorArray.push("Delay cannot be lower than 50ms."); 462 | if (pay.args[0] > 100000) 463 | errorArray.push("Delay cannot be higher than 100s."); 464 | defaultOptions.appear_delay = pay.args[0]; 465 | break; 466 | case "container": 467 | if (!($("."+pay.args[0]).length)) 468 | errorArray.push("Container not found."); 469 | defaultOptions.container = pay.args[0]; 470 | break; 471 | case "theme": 472 | if (!(typeof pay.args[0] == "string")) 473 | errorArray.push("Theme must be a string."); 474 | if (!(pay.args[0] === "dark")) 475 | errorArray.push("The only theme option is \"dark\"."); 476 | defaultOptions.theme = pay.args[0]; 477 | break; 478 | case "classcontainer": 479 | if (!(typeof pay.args[0] == "string")) 480 | errorArray.push("Class must be a string."); 481 | defaultOptions.classes.container = pay.args[0]; 482 | break; 483 | case "classflash": 484 | if (!(typeof pay.args[0] == "string")) 485 | errorArray.push("Class must be a string."); 486 | defaultOptions.classes.flash = pay.args[0]; 487 | break; 488 | case "classvisible": 489 | if (!(typeof pay.args[0] == "string")) 490 | errorArray.push("Class must be a string."); 491 | defaultOptions.classes.visible = pay.args[0]; 492 | break; 493 | case "classprogress": 494 | if (!(typeof pay.args[0] == "string")) 495 | errorArray.push("Class must be a string."); 496 | defaultOptions.classes.progress = pay.args[0]; 497 | break; 498 | case "classhidden": 499 | if (!(typeof pay.args[0] == "string")) 500 | errorArray.push("Class must be a string."); 501 | defaultOptions.classes.progress_hidden = pay.args[0]; 502 | break; 503 | case "transition": 504 | if (!(typeof pay.args[0] == "boolean")) 505 | errorArray.push("Transition must be true or false."); 506 | if (pay.args[0]) { 507 | $(document).one(':passagestart', function (ev) { 508 | $(defaultOptions.container).remove() 509 | }); 510 | } 511 | break; 512 | } 513 | } 514 | 515 | if (errorArray.length > 0) 516 | return this.error(errorArray.join("\n")); 517 | else { 518 | new FlashMessageManager(this.args[0], { 519 | type: defaultOptions.type, 520 | thumb: defaultOptions.thumb, 521 | progress: defaultOptions.progress, 522 | interactive: defaultOptions.interactive, 523 | timeout: defaultOptions.timeout, 524 | appear_delay: defaultOptions.appear_delay, 525 | container: defaultOptions.container, 526 | theme: defaultOptions.theme, 527 | layout: defaultOptions.layout, 528 | classes: { 529 | container: defaultOptions.classes.container, 530 | flash: defaultOptions.classes.flash, 531 | visible: defaultOptions.classes.visible, 532 | progress: defaultOptions.classes.progress, 533 | progress_hidden: defaultOptions.classes.progress_hidden 534 | } 535 | }); 536 | } 537 | } 538 | }); -------------------------------------------------------------------------------- /Notification/Version 2.37 or above/flash.js: -------------------------------------------------------------------------------- 1 | // FlashMessage macro by SjoerdHekking 2 | // FireFox progressbar bug fixed by Goctionni 3 | class FlashMessageManager { 4 | static DEFAULT_OPTIONS = { 5 | limit: 0, 6 | debug: false 7 | }; 8 | 9 | static bag = []; 10 | static displayed = []; 11 | static _displayQueue = 0; 12 | 13 | constructor(message, flashOptions) { 14 | this.options = FlashMessageManager.DEFAULT_OPTIONS; 15 | this.singleMessage = this.formatMessage(message, flashOptions); 16 | this.checkLimit(); 17 | } 18 | 19 | /** 20 | * Sets a random ID 21 | * @returns {String} UUID4 format. 22 | */ 23 | genNewID() { 24 | return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); 25 | } 26 | 27 | /** 28 | * Formats a message to a bag-able object with an unique ID. 29 | * @param {string} message The text to format 30 | * @param {Object} flashOptions The options to pass down 31 | * @returns Formatted message 32 | */ 33 | formatMessage(message, flashOptions) { 34 | if (!message && !flashOptions) { 35 | return null; 36 | } else { 37 | const tempMessage = { 38 | message: message, 39 | flashOptions: flashOptions, 40 | id: this.genNewID() 41 | }; 42 | return tempMessage; 43 | } 44 | } 45 | 46 | /** 47 | * Check if a display limit has been set, if so, queue message, else pass them down. 48 | * @returns Notification creation or queueing 49 | */ 50 | checkLimit() { 51 | if (this.singleMessage !== null) { 52 | FlashMessageManager.bag.push(this); 53 | } 54 | 55 | if (FlashMessageManager.bag.length === 0 ) { 56 | this.garbageCollection(); 57 | return; 58 | } 59 | 60 | if ((FlashMessageManager._displayQueue >= this.options.limit) && (this.options.limit > 0)) { 61 | // FlashMessageManager.bag.length > this.options.limit 62 | if (this.options.debug) { 63 | console.log("Display limit ("+this.options.limit+") reached, queueing message, bag is holding: "+(FlashMessageManager.bag.length - this.options.limit)+" messages."); 64 | } 65 | return; 66 | } 67 | 68 | const nextMessage = FlashMessageManager.bag.find( 69 | (instance) => !FlashMessageManager.displayed.includes(instance.singleMessage.id) 70 | ); 71 | 72 | if (!nextMessage) { 73 | if (this.options.debug) { 74 | console.log("Can not find more messages, the bag is probably empty, waiting for confirmation."); 75 | } 76 | return; 77 | } 78 | 79 | if (this.options.debug) { 80 | console.log("Displaying message:", nextMessage.singleMessage); 81 | } 82 | 83 | FlashMessageManager.create(nextMessage.singleMessage); 84 | FlashMessageManager.displayed.push(nextMessage.singleMessage.id); 85 | } 86 | 87 | /** 88 | * If the bag has been emptied, be sure to clean the history even if it is cleaned. 89 | */ 90 | garbageCollection() { 91 | if (this.options.debug) { 92 | console.log("Confirmation, bag is empty, garbage collection started."); 93 | } 94 | FlashMessageManager.bag = []; 95 | FlashMessageManager.displayed = []; 96 | FlashMessageManager._displayQueue = 0; 97 | } 98 | 99 | /** 100 | * Forward the message formatted and checked to the FlashMessage class. 101 | * @param {Object} singleMessage Holding the message itself (String), the options (Object), and lastly the unique ID. 102 | */ 103 | static create(singleMessage) { 104 | FlashMessageManager._displayQueue++; 105 | new FlashMessage(singleMessage.message, singleMessage.flashOptions, singleMessage.id); 106 | } 107 | } 108 | 109 | class FlashMessage { 110 | static get DEFAULT_OPTIONS() { 111 | return { 112 | type: "default", 113 | thumb: null, 114 | progress: false, 115 | interactive: true, 116 | timeout: 8000, 117 | appear_delay: 200, 118 | remove_delay: 600, 119 | container: ".flash-container", 120 | classes: { 121 | container: "flash-container", 122 | visible: "flash-is-visible", 123 | flash: "flash-message", 124 | progress: "flash-progress", 125 | progress_hidden: "flash-is-hidden" 126 | }, 127 | theme: "default", 128 | layout: "top-right", 129 | onShow: null, 130 | onClick: null, 131 | onClose: null 132 | }; 133 | } 134 | 135 | constructor(message, options, id) { 136 | this.$_element = null; 137 | this.$_message = null; 138 | this.interval = null; 139 | this.progress_bar = null; 140 | this.options = {}; 141 | this.message = message; 142 | this.setOptions(options); 143 | this.$_container = document.querySelector(this.options.container) || null; 144 | this._c_timeout = null; 145 | this.$_progress = null; 146 | this._progress_value = 0; 147 | this.$_id = id; 148 | this.createContainer(); 149 | this.createMessage(); 150 | } 151 | 152 | /** 153 | * An empty object is used a base, merge defaults into it, then user options. 154 | * @param {Object} options - FlashMessage options. 155 | * @returns {Object} - Modified FlashMessage options. 156 | */ 157 | setOptions(options = {}) { 158 | this.options = Object.assign({}, FlashMessage.DEFAULT_OPTIONS, options); 159 | return this.options; 160 | } 161 | 162 | /** 163 | * Create the container for the flash messages. 164 | * @returns - (If container exists do not create a new one, method returns.) 165 | */ 166 | createContainer() { 167 | if (this.$_container !== null && document.body.contains(this.$_container)) { 168 | return; 169 | } 170 | 171 | // Create the parent div with class 172 | this.$_container = document.createElement("div"); 173 | this.$_container.classList.add(this.options.classes.container); 174 | 175 | // check if body has children, ensure div always generates on top 176 | if (document.body.firstChild) { 177 | document.body.insertBefore(this.$_container, document.body.firstChild); 178 | } else { 179 | document.body.appendChild(this.$_container); 180 | } 181 | 182 | // Accessibility enforcing 183 | this.$_container.setAttribute("aria-label", "Notification list"); 184 | this.$_container.setAttribute("aria-live", "polite"); 185 | this.$_container.tabIndex = 0; 186 | 187 | // prepare layout 188 | this.$_container.classList.add(`${this.options.layout}-flash-layout`); 189 | } 190 | 191 | /** 192 | * Initialize the message, e.g. the child of the container. 193 | */ 194 | createMessage() { 195 | // Create child container that holds the message 196 | this.$_element = document.createElement("div"); 197 | this.$_element.classList.add(this.options.classes.flash, "flash-" + this.options.type); 198 | this.$_element.setAttribute("data-reference", this.$_id); 199 | 200 | // create message 201 | this.$_message = document.createElement("span"); 202 | this.$_message.classList.add("flash-text"); 203 | this.$_message.innerHTML = this.message; 204 | this.$_element.appendChild(this.$_message); 205 | 206 | // if a custom image is needed it can be added 207 | if (this.options.thumb) { 208 | let imgElement = document.createElement("img"); 209 | imgElement.classList.add("flash-thumb"); 210 | imgElement.src = this.options.thumb; 211 | this.$_element.classList.add("flash-message-has-thumb"); 212 | this.$_element.appendChild(imgElement); 213 | } 214 | 215 | // set the theme 216 | this.$_element.classList.add(`${this.options.theme}-theme`); 217 | 218 | // create progress bar 219 | 220 | // create start of the progress 221 | window.setTimeout( 222 | () => { 223 | this.$_element.classList.add(this.options.classes.visible); 224 | this.run(); 225 | }, this.options.appear_delay 226 | ); 227 | 228 | // check if interactive, if so, bind events. 229 | if (this.isInteractive()) { 230 | this.bindEvents(); 231 | } 232 | 233 | // accessibility add 234 | this.$_element.setAttribute("aria-live", "polite"); 235 | this.$_element.setAttribute("aria-label", this.message); 236 | this.$_element.tabIndex = 1; 237 | 238 | // append to container 239 | this.$_container.appendChild(this.$_element); 240 | } 241 | 242 | /** 243 | * Initialize the progress and make a timed event. 244 | */ 245 | run() { 246 | this.startProgress() 247 | if (this.hasProgress()) { 248 | this._c_timeout = window.setTimeout(() => this.close(), this.options.timeout) 249 | } 250 | } 251 | 252 | /** 253 | * Stop the progress and reset the timeout. 254 | */ 255 | stop() { 256 | if (this._c_timeout !== null) { 257 | window.clearTimeout(this._c_timeout); 258 | this.stopProgress(); 259 | this._c_timeout = null; 260 | } 261 | } 262 | 263 | /** 264 | * Remove the element from the parent. If the parent is empty, remove the parent. 265 | */ 266 | close() { 267 | this.$_element.remove(); 268 | const container = $(this.options.container); 269 | if (container.children().length === 0 ) { 270 | container.remove(); 271 | } 272 | 273 | FlashMessageManager._displayQueue--; 274 | FlashMessageManager.bag = FlashMessageManager.bag.filter((item) => item.singleMessage.id !== this.$_id); 275 | new FlashMessageManager(); 276 | } 277 | 278 | /** 279 | * Bind mouseover, mouseleave and click events 280 | */ 281 | bindEvents() { 282 | this.bindEvent('mouseover', _ => this.stop()) 283 | this.bindEvent('mouseleave', _ => this.run()) 284 | this.bindEvent('click', _ => this.close()) 285 | } 286 | 287 | /** 288 | * Unbind mouseover, mouseleave and click events 289 | */ 290 | unbindEvents() { 291 | this.unbindEvent('mouseover', _ => this.stop()) 292 | this.unbindEvent('mouseleave', _ => this.run()) 293 | this.unbindEvent('click', _ => this.close()) 294 | } 295 | 296 | /** 297 | * Initialize the events and callback methods. 298 | * @param {String} eventName - mouseover/mouseleave/click 299 | * @param {String} eventHandler - what method to call 300 | */ 301 | bindEvent(eventName, eventHandler) { 302 | this.$_element.addEventListener(eventName, eventHandler, false); 303 | } 304 | 305 | /** 306 | * Unbind the events and callback methods. 307 | * @param {String} eventName - mouseover/mouseleave/click 308 | * @param {String} eventHandler - what method to call 309 | */ 310 | unbindEvent(eventName, eventHandler) { 311 | this.$_element.removeEventListener(eventName, eventHandler, false); 312 | } 313 | 314 | /** 315 | * Checks if interactivity is allowed, if not, add a class. 316 | * @returns {Boolean} true / false 317 | */ 318 | isInteractive() { 319 | if (!this.options.interactive) { 320 | this.$_element.classList.add("flash-not-interactive"); 321 | } 322 | return this.options.interactive; 323 | } 324 | 325 | /** 326 | * Check if progress is true. 327 | * @returns {Boolean} true / false 328 | */ 329 | hasProgress() { 330 | return Boolean(this.options.progress); 331 | } 332 | 333 | /** 334 | * Creates the progress div. 335 | */ 336 | progressBar() { 337 | this.$_progress = document.createElement("div"); 338 | this.$_progress.classList.add(this.options.classes.progress); 339 | this.$_progress.setAttribute("role", "progressbar"); 340 | this.$_progress.setAttribute("aria-valuemin", 0); 341 | this.$_progress.setAttribute("aria-valuemax", 100); 342 | this.$_element.appendChild(this.$_progress); 343 | } 344 | 345 | /** 346 | * Set and update the progress. 347 | */ 348 | setProgress() { 349 | const elapsed = Date.now() - this._progress_starttime; 350 | const pct = Math.min(1, elapsed / this.options.timeout); 351 | const width = (pct * 100).toFixed(2); 352 | this.$_progress.setAttribute("aria-valuenow", width); 353 | this.$_progress.style.width = width + "%"; 354 | this._progress_value = width; 355 | if (pct >= 1) { 356 | this.stopProgress(); 357 | } else { 358 | requestAnimationFrame(this.setProgress.bind(this)); 359 | } 360 | } 361 | 362 | /** 363 | * Initialize the progress. 364 | */ 365 | startProgress() { 366 | if (this.hasProgress()) { 367 | if (!this.$_progress) { 368 | this.progressBar(); 369 | } 370 | this.stopProgress(); 371 | this._progress_starttime = Date.now(); 372 | this.$_progress.classList.remove(this.options.classes.progress_hidden); 373 | this.setProgress(); 374 | } 375 | } 376 | 377 | /** 378 | * Stop progress. 379 | */ 380 | stopProgress() { 381 | if (this.hasProgress() && this.$_progress) { 382 | this.$_progress.classList.add("flash-is-hidden"); 383 | this._progress_value = 0; 384 | } 385 | } 386 | } 387 | 388 | window.FlashMessageManager = FlashMessageManager; 389 | 390 | Macro.add("flash", { 391 | tags: ["progress", "Progress", "interactive", "Interactive", "timeout", "Timeout", "delay", "Delay", "container", "Container", "theme", "Theme", "thumb", "Thumb", "classContainer", "classcontainer", "classFlash", "classflash", "classVisible", "classvisible", "classProgress", "classprogress", "classHidden", "classhidden", "flashtype", "flashType", "layout", "Layout", "transition", "Transition"], 392 | handler: function () { 393 | const layoutArray = ["top-right", "middle-right", "bottom-right", "middle-bottom", "bottom-left", "middle-left", "top-left", "middle-top"]; 394 | const errorArray = []; 395 | const defaultOptions = { 396 | type: "default", 397 | thumb: null, 398 | progress: true, 399 | interactive: true, 400 | timeout: 8000, 401 | appear_delay: 200, 402 | container: '.flash-container', 403 | theme: 'default', 404 | layout: 'top-right', 405 | classes: { 406 | container: 'flash-container', 407 | flash: 'flash-message', 408 | visible: 'flash-is-visible', 409 | progress: 'flash-progress', 410 | progress_hidden: 'flash-is-hidden' 411 | } 412 | }; 413 | 414 | if(this.args.length <= 0) 415 | return this.error("First argument cannot be skipped, please insert a string via <>."); 416 | if(this.args[0] === "") 417 | return this.error("First argument cannot be an empty string."); 418 | 419 | for (const pay of this.payload) { 420 | switch (pay.name.toLowerCase()) { 421 | case "flashtype": 422 | if (!(typeof pay.args[0] == "string")) 423 | errorArray.push("Type must be a string."); 424 | defaultOptions.type = pay.args[0]; 425 | break; 426 | case "thumb": 427 | if (!(typeof pay.args[0] == "string")) 428 | errorArray.push("Thumb must be a string."); 429 | defaultOptions.thumb = pay.args[0]; 430 | break; 431 | case "layout": 432 | if (!(typeof pay.args[0] == "string")) 433 | errorArray.push("Layout must be a string."); 434 | if (!(layoutArray.includes(pay.args[0]))) 435 | errorArray.push("Layout does not include: "+pay.args[0]); 436 | defaultOptions.layout = pay.args[0]; 437 | break; 438 | case "progress": 439 | if (!(typeof pay.args[0] == "boolean")) 440 | errorArray.push("Progress must be true or false."); 441 | defaultOptions.progress = pay.args[0]; 442 | break; 443 | case "interactive": 444 | if (!(typeof pay.args[0] == "boolean")) 445 | errorArray.push("Interactive must be true or false."); 446 | defaultOptions.interactive = pay.args[0]; 447 | break; 448 | case "timeout": 449 | if (!(typeof pay.args[0] == "number")) 450 | errorArray.push("Timeout must be a number."); 451 | if (pay.args[0] < 500) 452 | errorArray.push("Timeout cannot be lower than 500ms."); 453 | if (pay.args[0] > 100000) 454 | errorArray.push("Timeout cannot be higher than 100s."); 455 | defaultOptions.timeout = pay.args[0]; 456 | break; 457 | case "delay": 458 | if (!(typeof pay.args[0] == "number")) 459 | errorArray.push("Delay must be a number."); 460 | if (pay.args[0] < 50) 461 | errorArray.push("Delay cannot be lower than 50ms."); 462 | if (pay.args[0] > 100000) 463 | errorArray.push("Delay cannot be higher than 100s."); 464 | defaultOptions.appear_delay = pay.args[0]; 465 | break; 466 | case "container": 467 | if (!($("."+pay.args[0]).length)) 468 | errorArray.push("Container not found."); 469 | defaultOptions.container = pay.args[0]; 470 | break; 471 | case "theme": 472 | if (!(typeof pay.args[0] == "string")) 473 | errorArray.push("Theme must be a string."); 474 | if (!(pay.args[0] === "dark")) 475 | errorArray.push("The only theme option is \"dark\"."); 476 | defaultOptions.theme = pay.args[0]; 477 | break; 478 | case "classcontainer": 479 | if (!(typeof pay.args[0] == "string")) 480 | errorArray.push("Class must be a string."); 481 | defaultOptions.classes.container = pay.args[0]; 482 | break; 483 | case "classflash": 484 | if (!(typeof pay.args[0] == "string")) 485 | errorArray.push("Class must be a string."); 486 | defaultOptions.classes.flash = pay.args[0]; 487 | break; 488 | case "classvisible": 489 | if (!(typeof pay.args[0] == "string")) 490 | errorArray.push("Class must be a string."); 491 | defaultOptions.classes.visible = pay.args[0]; 492 | break; 493 | case "classprogress": 494 | if (!(typeof pay.args[0] == "string")) 495 | errorArray.push("Class must be a string."); 496 | defaultOptions.classes.progress = pay.args[0]; 497 | break; 498 | case "classhidden": 499 | if (!(typeof pay.args[0] == "string")) 500 | errorArray.push("Class must be a string."); 501 | defaultOptions.classes.progress_hidden = pay.args[0]; 502 | break; 503 | case "transition": 504 | if (!(typeof pay.args[0] == "boolean")) 505 | errorArray.push("Transition must be true or false."); 506 | if (pay.args[0]) { 507 | $(document).one(':passagestart', function (ev) { 508 | $(defaultOptions.container).remove() 509 | }); 510 | } 511 | break; 512 | } 513 | } 514 | 515 | if (errorArray.length > 0) 516 | return this.error(errorArray.join("\n")); 517 | else { 518 | new FlashMessageManager(this.args[0], { 519 | type: defaultOptions.type, 520 | thumb: defaultOptions.thumb, 521 | progress: defaultOptions.progress, 522 | interactive: defaultOptions.interactive, 523 | timeout: defaultOptions.timeout, 524 | appear_delay: defaultOptions.appear_delay, 525 | container: defaultOptions.container, 526 | theme: defaultOptions.theme, 527 | layout: defaultOptions.layout, 528 | classes: { 529 | container: defaultOptions.classes.container, 530 | flash: defaultOptions.classes.flash, 531 | visible: defaultOptions.classes.visible, 532 | progress: defaultOptions.classes.progress, 533 | progress_hidden: defaultOptions.classes.progress_hidden 534 | } 535 | }); 536 | } 537 | } 538 | }); --------------------------------------------------------------------------------