From 25f1b20929637c1d901668055722234da8bf41a6 Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Sat, 16 Oct 2021 19:32:25 +0300 Subject: [PATCH 01/21] Black reformatted. Added footer keyword to generate_embeds Removed build from docs --- discordSuperUtils/birthday.py | 2 +- discordSuperUtils/music/music.py | 6 +- discordSuperUtils/music/queue.py | 2 +- discordSuperUtils/paginator.py | 11 +- docs/Makefile | 20 - docs/_build/doctrees/environment.pickle | Bin 283865 -> 0 bytes docs/_build/doctrees/index.doctree | Bin 5332 -> 0 bytes .../doctrees/source/discordSuperUtils.doctree | Bin 1098543 -> 0 bytes docs/_build/doctrees/source/modules.doctree | Bin 3769 -> 0 bytes docs/_build/html/.buildinfo | 4 - docs/_build/html/.nojekyll | 0 .../_modules/discordSuperUtils/Antispam.html | 236 - .../html/_modules/discordSuperUtils/Ban.html | 261 - .../html/_modules/discordSuperUtils/Base.html | 466 - .../_modules/discordSuperUtils/Birthday.html | 294 - .../discordSuperUtils/CommandHinter.html | 186 - .../discordSuperUtils/Convertors.html | 149 - .../_modules/discordSuperUtils/Database.html | 454 - .../_modules/discordSuperUtils/Economy.html | 187 - .../_modules/discordSuperUtils/FiveM.html | 167 - .../_modules/discordSuperUtils/Imaging.html | 312 - .../discordSuperUtils/Infractions.html | 224 - .../discordSuperUtils/InviteTracker.html | 218 - .../html/_modules/discordSuperUtils/Kick.html | 132 - .../_modules/discordSuperUtils/Leveling.html | 338 - .../discordSuperUtils/MessageFilter.html | 232 - .../_modules/discordSuperUtils/Music.html | 852 -- .../html/_modules/discordSuperUtils/Mute.html | 344 - .../_modules/discordSuperUtils/Paginator.html | 292 - .../_modules/discordSuperUtils/Prefix.html | 146 - .../discordSuperUtils/Punishments.html | 161 - .../discordSuperUtils/ReactionRoles.html | 191 - .../_modules/discordSuperUtils/Spotify.html | 199 - .../_modules/discordSuperUtils/Template.html | 663 - docs/_build/html/_modules/index.html | 103 - docs/_build/html/_sources/index.rst.txt | 12 - .../_build/html/_sources/installation.rst.txt | 23 - .../_sources/source/discordSuperUtils.rst.txt | 197 - .../html/_sources/source/modules.rst.txt | 7 - docs/_build/html/_static/alabaster.css | 701 - docs/_build/html/_static/basic.css | 904 -- docs/_build/html/_static/custom.css | 1 - docs/_build/html/_static/doctools.js | 323 - .../html/_static/documentation_options.js | 12 - docs/_build/html/_static/file.png | Bin 286 -> 0 bytes docs/_build/html/_static/jquery-3.5.1.js | 10872 ---------------- docs/_build/html/_static/jquery.js | 2 - docs/_build/html/_static/language_data.js | 297 - docs/_build/html/_static/minus.png | Bin 90 -> 0 bytes docs/_build/html/_static/plus.png | Bin 90 -> 0 bytes docs/_build/html/_static/pygments.css | 74 - docs/_build/html/_static/searchtools.js | 528 - docs/_build/html/_static/underscore-1.13.1.js | 2042 --- docs/_build/html/_static/underscore.js | 6 - docs/_build/html/genindex.html | 1261 -- docs/_build/html/index.html | 95 - docs/_build/html/installation.html | 104 - docs/_build/html/objects.inv | Bin 3524 -> 0 bytes docs/_build/html/py-modindex.html | 215 - docs/_build/html/search.html | 104 - docs/_build/html/searchindex.js | 1 - .../_build/html/source/discordSuperUtils.html | 2941 ----- docs/_build/html/source/modules.html | 121 - docs/index.rst | 12 - docs/installation.rst | 23 - docs/make.bat | 35 - docs/source/discordSuperUtils.rst | 197 - docs/source/modules.rst | 7 - 68 files changed, 16 insertions(+), 27953 deletions(-) delete mode 100644 docs/_build/doctrees/environment.pickle delete mode 100644 docs/_build/doctrees/index.doctree delete mode 100644 docs/_build/doctrees/source/discordSuperUtils.doctree delete mode 100644 docs/_build/doctrees/source/modules.doctree delete mode 100644 docs/_build/html/.buildinfo delete mode 100644 docs/_build/html/.nojekyll delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Antispam.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Ban.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Base.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Birthday.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/CommandHinter.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Convertors.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Database.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Economy.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/FiveM.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Imaging.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Infractions.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/InviteTracker.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Kick.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Leveling.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/MessageFilter.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Music.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Mute.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Paginator.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Prefix.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Punishments.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/ReactionRoles.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Spotify.html delete mode 100644 docs/_build/html/_modules/discordSuperUtils/Template.html delete mode 100644 docs/_build/html/_modules/index.html delete mode 100644 docs/_build/html/_sources/index.rst.txt delete mode 100644 docs/_build/html/_sources/installation.rst.txt delete mode 100644 docs/_build/html/_sources/source/discordSuperUtils.rst.txt delete mode 100644 docs/_build/html/_sources/source/modules.rst.txt delete mode 100644 docs/_build/html/_static/alabaster.css delete mode 100644 docs/_build/html/_static/basic.css delete mode 100644 docs/_build/html/_static/custom.css delete mode 100644 docs/_build/html/_static/doctools.js delete mode 100644 docs/_build/html/_static/documentation_options.js delete mode 100644 docs/_build/html/_static/file.png delete mode 100644 docs/_build/html/_static/jquery-3.5.1.js delete mode 100644 docs/_build/html/_static/jquery.js delete mode 100644 docs/_build/html/_static/language_data.js delete mode 100644 docs/_build/html/_static/minus.png delete mode 100644 docs/_build/html/_static/plus.png delete mode 100644 docs/_build/html/_static/pygments.css delete mode 100644 docs/_build/html/_static/searchtools.js delete mode 100644 docs/_build/html/_static/underscore-1.13.1.js delete mode 100644 docs/_build/html/_static/underscore.js delete mode 100644 docs/_build/html/genindex.html delete mode 100644 docs/_build/html/index.html delete mode 100644 docs/_build/html/installation.html delete mode 100644 docs/_build/html/objects.inv delete mode 100644 docs/_build/html/py-modindex.html delete mode 100644 docs/_build/html/search.html delete mode 100644 docs/_build/html/searchindex.js delete mode 100644 docs/_build/html/source/discordSuperUtils.html delete mode 100644 docs/_build/html/source/modules.html delete mode 100644 docs/source/modules.rst diff --git a/discordSuperUtils/birthday.py b/discordSuperUtils/birthday.py index 9d7f837..afc7ac3 100644 --- a/discordSuperUtils/birthday.py +++ b/discordSuperUtils/birthday.py @@ -269,7 +269,7 @@ async def get_upcoming(self, guild: discord.Guild) -> List[BirthdayMember]: self._check_database() member_data = await self.database.select( - self.tables["birthdays"], [],{"guild": guild.id}, fetchall=True + self.tables["birthdays"], [], {"guild": guild.id}, fetchall=True ) birthdays = {} diff --git a/discordSuperUtils/music/music.py b/discordSuperUtils/music/music.py index 73ab4c4..b1c6094 100644 --- a/discordSuperUtils/music/music.py +++ b/discordSuperUtils/music/music.py @@ -888,9 +888,11 @@ async def shuffle(self, ctx: commands.Context) -> Optional[bool]: if queue.shuffle: queue.original_queue = queue.queue - play_queue = queue.queue[queue.pos + 1:] + play_queue = queue.queue[queue.pos + 1 :] shuffled_queue = random.sample(play_queue, len(play_queue)) - queue.queue = queue.queue[:queue.pos] + [queue.now_playing] + shuffled_queue + queue.queue = ( + queue.queue[: queue.pos] + [queue.now_playing] + shuffled_queue + ) return queue.shuffle diff --git a/discordSuperUtils/music/queue.py b/discordSuperUtils/music/queue.py index 806c462..3692803 100644 --- a/discordSuperUtils/music/queue.py +++ b/discordSuperUtils/music/queue.py @@ -28,7 +28,7 @@ class QueueManager: "vote_skips", "played_history", "queue_loop_start", - "original_queue" + "original_queue", ) def __init__(self, volume: float, queue: List[Player]): diff --git a/discordSuperUtils/paginator.py b/discordSuperUtils/paginator.py index ae1744e..0daceee 100644 --- a/discordSuperUtils/paginator.py +++ b/discordSuperUtils/paginator.py @@ -7,7 +7,13 @@ def generate_embeds( - list_to_generate, title, description, fields=25, color=0xFF0000, string_format="{}" + list_to_generate, + title, + description, + fields=25, + color=0xFF0000, + string_format="{}", + footer: str = None, ): num_of_embeds = ceil((len(list_to_generate) + 1) / fields) @@ -33,6 +39,9 @@ def generate_embeds( if (index + 1) % fields == 0: embed_index += 1 + for embed in embeds: + embed.set_footer(text=footer) + return embeds diff --git a/docs/Makefile b/docs/Makefile index d4bb2cb..e69de29 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle deleted file mode 100644 index df4c34e3d8053363a44a87dd075300ab505c1377..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283865 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
  • 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 CFIxNn diff --git a/docs/_build/doctrees/source/modules.doctree b/docs/_build/doctrees/source/modules.doctree deleted file mode 100644 index d0a34cb9b2308fb277044e014e56d26d8222e617..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3769 zcmbtXTW=f36}EMuNQt5(JC1|YPK6dl9iyUxBu-nQO&{`7L@<%0NuLxfhPy+~RJ%Lt znOTtvoT7b5VY^eHK&G$#34IEXU(^56@9d3BQte9#5ZFC)=G@PB&i-lpAO9NMDt_+3 zWJ0F%A&a6^McjDTjZ~N?JhlE?|Ms8#*Zz^~X*M$|*CF@r7C1s7<4E(=e~H5Z@mm?2 zVI|9LU~w%sp>!{g_n((3uK=2^@_es*^Jxsc{~lBXeYALE&o zJP?%fAeQOn^I+)34z!A%+xnc($wqhghI{@SUziS6Hy*oA#^8YUqMwf;tf%>eg8&a} zuW|LLFtM$^&<_4CPcp%bG;Q|1$K+AL{*Bn{k+ewSxle?fRtx7gA8Nz?t}k1*po6^3{+5+B@{kalr>1@>9V?tc5x z*~J%zYjeROmZTS-T!(_`|eT$kWmEWf9dwC+YO_y4*`6v;ti7wF;YiZE0w z=zY}6qIz#Utgw$H=dHpe3pd)L>mH~x(15LYMR#wbH*!sbC1^mnUsFmjc3s0Q2HF&( z_NW=16RlFqQ5UZBdlu(NM>B@L8oAz-r(83uw8u-tFfJ@hGQ8}SYRWzwx!vkD*D((F z5ygPU1obb}ZBq)ay}L6}%BITl*5UbWw@(d;90v1=(g~m%#f4JJfHDc~0|p|0P*Qlz z3raAPRs{91g2w7XHN8fzkLj+o4Pt)9V}HKkb_I)@S4>zwmxz^RHa9iXw%d;QIG;k@ zV8(QcSb;-xmVnZ;8p{GXu}7{Gs&oP$5e^w)45EYQUUb}mc$oC##qBwSHLl!clH^*FXlVLg`IPKx!3_9Uq2SvE!kMTE6o_Ymcn z1Wji)<_9K2!2?4Nv1(a14}_eG7=M)RR;;M%`zB%Nv1d72+8|7Ki}&(1h*ii+cy|wJ zez6c~FIx>UE&_<#mw5&iw8|H_T|odq9u%2QPysb2+#!^}hQG{r$N~Kk+VmQ_1jnlK z(%lPV#V!M$Fc}BH@8Uv=_o6HnDFzw=BW1tdm`TR&c6RG%K ze3!c(QSEgGhiH#}_CkC=%Ee=PPK6NPiCOOU+u1B+?S~RjM@0=AN(!}X%~$%|rj&eG z%8~tKtm39X3f9+Fhz)mdNn)?oT3oubBp=YLEn)q=Raw;uMmYc_#L_}}a(khQV`K?F zV501NRMEdh6u$AFp(*fPg*L+%ivgCE3L9KmBJVZiBL92UI)Q?n(r3?ZM9^2EM7h}y F{{zUB%~AjW diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo deleted file mode 100644 index 10fbe09..0000000 --- a/docs/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: d61d14f83416216facf096aae5126376 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_build/html/.nojekyll b/docs/_build/html/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/_build/html/_modules/discordSuperUtils/Antispam.html b/docs/_build/html/_modules/discordSuperUtils/Antispam.html deleted file mode 100644 index a46f75e..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Antispam.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - - - discordSuperUtils.Antispam — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Antispam

    -from __future__ import annotations
    -
    -import asyncio
    -from abc import ABC, abstractmethod
    -from datetime import timedelta
    -from difflib import SequenceMatcher
    -from typing import (
    -    TYPE_CHECKING,
    -    List,
    -    Union,
    -    Any,
    -    Iterable
    -)
    -
    -import discord
    -
    -from .Base import EventManager, get_generator_response
    -from .Punishments import get_relevant_punishment
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    from .Punishments import Punishment
    -
    -__all__ = ("SpamManager", "SpamDetectionGenerator", "DefaultSpamDetectionGenerator")
    -
    -
    -
    [docs]class SpamDetectionGenerator(ABC): - """ - Represents a SpamManager that filters messages to find spam. - """ - - __slots__ = () - -
    [docs] @abstractmethod - def generate(self, last_messages: List[discord.Message]) -> Union[bool, Any]: - """ - This function is an abstract method. - The generate function of the generator. - - :param last_messages: The last messages sent (5 is max). - :type last_messages: List[discord.Message] - :return: A boolean representing if the message is spam. - :rtype: Union[bool, Any] - """ - - pass
    - - -
    [docs]class DefaultSpamDetectionGenerator(SpamDetectionGenerator): -
    [docs] def generate(self, last_messages: List[discord.Message]) -> Union[bool, Any]: - member = last_messages[0].author - if member.guild_permissions.administrator: - return False - - return SpamManager.get_messages_similarity([message.content for message in last_messages]) > 0.70
    - - -
    [docs]class SpamManager(EventManager): - """ - Represents a SpamManager which detects spam. - """ - - __slots__ = ("bot", "generator", "punishments", "_spam_cache", "_last_messages") - - def __init__(self, - bot: commands.Bot, - generator: SpamDetectionGenerator = None, - wipe_cache_delay: timedelta = timedelta(minutes=5)): - super().__init__() - self.bot = bot - self.generator = generator if generator is not None else DefaultSpamDetectionGenerator - self.wipe_cache_delay = wipe_cache_delay - self.punishments = [] - self._spam_cache = {} - self._last_messages = {} - - self.bot.loop.create_task(self.__wipe_cache()) - self.bot.add_listener(self.__handle_messages, "on_message") - self.bot.add_listener(self.__handle_messages, "on_message_edit") - - async def __wipe_cache(self): - """ - This function is responsible for wiping the member cache. - - :return: - """ - - while not self.bot.is_closed(): - await asyncio.sleep(self.wipe_cache_delay.total_seconds()) - - self._spam_cache = {} - -
    [docs] @staticmethod - def get_messages_similarity(messages: Iterable[str]) -> float: - """ - Gets the similarity between messages. - - :param messages: Messages to compare. - :type messages: Iterable[str] - :rtype: float - :return: The similarity between messages (0-1) - """ - - results = [] - - for i, message in enumerate(messages): - for second_index, second_message in enumerate(messages): - if i != second_index: - results.append(SequenceMatcher(None, message, second_message).ratio()) - - return sum(results) / len(results) if results else 0
    - -
    [docs] def add_punishments(self, punishments: List[Punishment]) -> None: - self.punishments = punishments
    - - async def __handle_messages(self, message, edited_message=None): - message = edited_message or message - - if not message.guild or message.author.bot: - return - - member_last_messages = self._last_messages.setdefault(message.guild.id, {}).get(message.author.id, []) - - member_last_messages.append(message) - member_last_messages = member_last_messages[-5:] - - self._last_messages[message.guild.id][message.author.id] = member_last_messages - - if len(member_last_messages) <= 3 or not get_generator_response(self.generator, SpamDetectionGenerator, member_last_messages): - return - - # member_warnings are the number of times the member has spammed. - member_warnings = self._spam_cache.setdefault(message.guild.id, {}).get(message.author.id, 0) + 1 - self._spam_cache[message.guild.id][message.author.id] = member_warnings - - await self.call_event("on_message_spam", member_last_messages, member_warnings) - - if punishment := get_relevant_punishment(self.punishments, member_warnings): - await punishment.punishment_manager.punish(message, message.author, punishment)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Ban.html b/docs/_build/html/_modules/discordSuperUtils/Ban.html deleted file mode 100644 index 90debf0..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Ban.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - discordSuperUtils.Ban — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Ban

    -from __future__ import annotations
    -
    -import asyncio
    -from datetime import datetime
    -from typing import (
    -    TYPE_CHECKING,
    -    Union,
    -    Optional,
    -    List,
    -    Dict,
    -    Any
    -)
    -
    -import discord
    -
    -from .Base import DatabaseChecker
    -from .Punishments import Punisher
    -
    -if TYPE_CHECKING:
    -    from .Punishments import Punishment
    -    from discord.ext import commands
    -
    -
    -__all__ = ("UnbanFailure", "BanManager")
    -
    -
    -
    [docs]class UnbanFailure(Exception): - """Raises an exception when the user tries to unban a discord.User without passing the guild."""
    - - -
    [docs]class BanManager(DatabaseChecker, Punisher): - """ - A BanManager that manages guild bans. - """ - - __slots__ = ("bot",) - - def __init__(self, bot: commands.Bot): - super().__init__([{'guild': "snowflake", 'member': "snowflake", 'reason': "string", 'timestamp': "snowflake"}], - ['bans']) - self.bot = bot - - self.add_event(self.on_database_connect) - -
    [docs] async def on_database_connect(self): - self.bot.loop.create_task(self.__check_bans())
    - -
    [docs] async def get_banned_members(self) -> List[Dict[str, Any]]: - """ - |coro| - - This function returns all the members that are supposed to be unbanned but are banned. - - :return: The list of unbanned members. - :rtype: List[Dict[str, Any]] - """ - - return [x for x in await self.database.select(self.tables['bans'], [], fetchall=True) - if x["timestamp"] <= datetime.utcnow().timestamp()]
    - - async def __check_bans(self) -> None: - """ - |coro| - - A loop that ensures that members are unbanned when they need to. - - :return: None - :rtype: None - """ - - await self.bot.wait_until_ready() - - while not self.bot.is_closed(): - for banned_member in await self.get_banned_members(): - guild = self.bot.get_guild(banned_member['guild']) - - if guild is None: - continue - - user = await self.bot.fetch_user(banned_member['member']) - - if await self.unban(user, guild): - await self.call_event("on_unban", user, banned_member['reason']) - - await asyncio.sleep(300) - -
    [docs] async def punish(self, ctx: commands.Context, member: discord.Member, punishment: Punishment) -> None: - try: - self.bot.loop.create_task( - self.ban(member, punishment.punishment_reason, punishment.punishment_time.total_seconds()) - ) - except discord.errors.Forbidden as e: - raise e - else: - await self.call_event("on_punishment", ctx, member, punishment)
    - -
    [docs] @staticmethod - async def get_ban(member: Union[discord.Member, discord.User], guild: discord.Guild) -> Optional[discord.User]: - """ - |coro| - - This function returns the user object of the member if he is banned from the guild. - - :param member: The banned member. - :type member: discord.Member - :param guild: The guild. - :type guild: discord.Guild - :return: The user object if found. - :rtype: Optional[discord.User] - """ - - banned = await guild.bans() - for x in banned: - if x.user.id == member.id: - return x.user
    - -
    [docs] async def unban(self, member: Union[discord.Member, discord.User], guild: discord.Guild = None) -> bool: - self._check_database() - - if isinstance(member, discord.User) and not guild: - raise UnbanFailure("Cannot unban a discord.User without a guild.") - - guild = guild if guild is not None else member.guild - await self.database.delete(self.tables['bans'], {'guild': guild.id, 'member': member.id}) - - if user := await self.get_ban(member, guild): - await guild.unban(user) - return True
    - -
    [docs] async def ban(self, - member: discord.Member, - reason: str = "No reason provided.", - time_of_ban: Union[int, float] = 0) -> None: - """ - |coro| - - Bans the member from the guild. - - :param member: The member to ban. - :type member: discord.Member - :param reason: The reason of the ban. - :type reason: str - :param time_of_ban: The time of ban. - :type time_of_ban: Union[int, float] - :return: None - :rtype: None - """ - - self._check_database() - - await member.ban(reason=reason) - - if time_of_ban <= 0: - return - - await self.database.insert(self.tables['bans'], {'guild': member.guild.id, - 'member': member.id, - 'reason': reason, - 'timestamp': datetime.utcnow().timestamp() + time_of_ban}) - - await asyncio.sleep(time_of_ban) - - if await self.unban(member): - await self.call_event("on_unban", member, reason)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Base.html b/docs/_build/html/_modules/discordSuperUtils/Base.html deleted file mode 100644 index 5d6de86..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Base.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - - - discordSuperUtils.Base — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Base

    -from __future__ import annotations
    -
    -import asyncio
    -import inspect
    -from typing import List, Any, Iterable, Optional, TYPE_CHECKING, Union, Tuple, Callable, Dict, Awaitable
    -
    -import aiomysql
    -import aiopg
    -import aiosqlite
    -import discord
    -from motor import motor_asyncio
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    from .Database import Database
    -
    -
    -__all__ = (
    -    "COLUMN_TYPES",
    -    "DatabaseNotConnected",
    -    "InvalidGenerator",
    -    "get_generator_response",
    -    "maybe_coroutine",
    -    "generate_column_types",
    -    "questionnaire",
    -    "EventManager",
    -    "CogManager",
    -    "DatabaseChecker"
    -)
    -
    -
    -COLUMN_TYPES = {
    -    motor_asyncio.AsyncIOMotorDatabase: None,  # mongo does not require any columns
    -    aiosqlite.core.Connection: {"snowflake": "INTEGER", "string": 'TEXT', "number": "INTEGER", "smallnumber": "INTEGER"},
    -    aiopg.pool.Pool: {"snowflake": "bigint", "string": 'character varying', "number": "integer", "smallnumber": "smallint"},
    -    aiomysql.pool.Pool: {"snowflake": "BIGINT", "string": 'TEXT', "number": "INT", "smallnumber": "SMALLINT"}
    -}
    -
    -
    -
    [docs]class DatabaseNotConnected(Exception): - """Raises an error when the user tries to use a method of a manager without a database connected to it."""
    - - -
    [docs]class InvalidGenerator(Exception): - """ - Raises an exception when the user passes an invalid generator. - """ - - __slots__ = ("generator",) - - def __init__(self, generator): - self.generator = generator - super().__init__(f"Generator of type {type(self.generator)!r} is not supported.")
    - - -
    [docs]async def maybe_coroutine(function: Callable, *args, **kwargs) -> Any: - """ - |coro| - - Returns the coroutine version of the function. - - :param function: The function to convert. - :type function: Union[Awaitable, Callable] - :param args: The arguments. - :param kwargs: The key arguments: - :return: The coroutine version of the function. - :rtype: Awaitable - """ - - value = function(*args, **kwargs) - - if inspect.isawaitable(value): - return await value - - return value
    - - -
    [docs]def get_generator_response(generator: Any, generator_type: Any, *args, **kwargs) -> Any: - """ - Returns the generator response with the arguments. - - :param generator: The generator to get the response from. - :type generator: Any - :param generator_type: The generator type. (Should be same as the generator type. - :type generator_type: Any - :param args: The arguments of the generator. - :param kwargs: The key arguments of the generator - :return: The generator response. - :rtype: Any - """ - - if inspect.isclass(generator) and issubclass(generator, generator_type): - if inspect.ismethod(generator.generate): - return generator.generate(*args, **kwargs) - - return generator().generate(*args, **kwargs) - - if isinstance(generator, generator_type): - return generator.generate(*args, **kwargs) - - raise InvalidGenerator(generator)
    - - -
    [docs]def generate_column_types(types: Iterable[str], database_type: Any) -> Optional[List[str]]: - """ - Generates the column type names that are suitable for the database type. - - :param types: The column types. - :type types: Iterable[str] - :param database_type: The database type. - :type database_type: Any - :return: The suitable column types for the database types. - :rtype: Optional[List[str]] - """ - - database_type_configuration = COLUMN_TYPES.get(database_type) - - if database_type_configuration is None: - return - - return [database_type_configuration[x] for x in types]
    - - -
    [docs]async def questionnaire(ctx: commands.Context, - questions: Iterable[Union[str, discord.Embed]], - public: bool = False, - timeout: Union[float, int] = 30, - member: discord.Member = None) -> Tuple[List[str], bool]: - """ - |coro| - - Questions the member using a "quiz" and returns the answers. - The questionnaire can be used without a specific member and be public. - If no member was passed and the questionnaire public argument is true, a ValueError will be raised. - - :raises: ValueError: The questionnaire is private and no member was provided. - :param ctx: The context (where the questionnaire will ask the questions). - :type ctx: commands.Context - :param questions: The questions the questionnaire will ask. - :type questions: Iterable[Union[str, discord.Embed]] - :param public: A bool indicating if the questionnaire is public. - :type public: bool - :param timeout: The number of seconds until the questionnaire will stop and time out. - :type timeout: Union[float, int] - :param member: The member the questionnaire will get the answers from. - :type member: discord.Member - :return: The answers and a boolean indicating if the questionnaire timed out. - :rtype: Tuple[List[str], bool] - """ - - answers = [] - timed_out = False - - if not public and not member: - raise ValueError("The questionnaire is private and no member was provided.") - - def checks(msg): - return msg.channel == ctx.channel if public else msg.channel == ctx.channel and msg.author == member - - for question in questions: - if isinstance(question, str): - await ctx.send(question) - elif isinstance(question, discord.Embed): - await ctx.send(embed=question) - else: - raise TypeError("Question must be of type 'str' or 'discord.Embed'.") - - try: - message = await ctx.bot.wait_for('message', check=checks, timeout=timeout) - except asyncio.TimeoutError: - timed_out = True - break - - answers.append(message.content) - - return answers, timed_out
    - - -
    [docs]class EventManager: - """ - An event manager that manages events for managers. - """ - - def __init__(self): - self.events = {} - -
    [docs] async def call_event(self, name: str, *args, **kwargs) -> None: - """ - Calls the event name with the arguments - - :param name: The event name. - :type name: str - :param args: The arguments. - :param kwargs: The key arguments. - :return: None - :rtype: None - """ - - if name in self.events: - for event in self.events[name]: - await event(*args, **kwargs)
    - -
    [docs] def event(self, name: str = None) -> Callable: - """ - A decorator which adds an event listener. - - :param name: The event name. - :type name: str - :return: The inner function. - :rtype: Callable - """ - - def inner(func): - self.add_event(func, name) - return func - - return inner
    - -
    [docs] def add_event(self, func: Callable, name: str = None) -> None: - """ - Adds an event to the event dictionary. - - :param func: The event callback. - :type func: Callable - :param name: The event name. - :type name: str - :return: None - :rtype: None - :raises: TypeError: The listener isn't async. - """ - - name = func.__name__ if not name else name - - if not asyncio.iscoroutinefunction(func): - raise TypeError('Listeners must be async.') - - if name in self.events: - self.events[name].append(func) - else: - self.events[name] = [func]
    - -
    [docs] def remove_event(self, func: Callable, name: str = None) -> None: - """ - Removes an event from the event dictionary. - - :param func: The event callback. - :type func: Callable - :param name: The event name. - :type name: str - :return: None - :rtype: None - """ - - name = func.__name__ if not name else name - - if name in self.events: - self.events[name].remove(func)
    - - -
    [docs]class CogManager: - """ - A CogManager which helps the user use the managers inside discord cogs. - """ - -
    [docs] class Cog: - """ - The internal Cog class. - """ - - def __init__(self, managers: List = None): - listeners = {} - managers = [] if managers is None else managers - - attribute_objects = [getattr(self, attr) for attr in dir(self)] - - for attr in attribute_objects: - listener_type = getattr(attr, "_listener_type", None) - if listener_type: - if listener_type in listeners: - listeners[listener_type].append(attr) - else: - listeners[listener_type] = [attr] - - managers = managers or [attr for attr in attribute_objects if type(attr) in listeners] - for event_type in listeners: - for manager in managers: - for event in listeners[event_type]: - manager.add_event(event)
    - -
    [docs] @staticmethod - def event(manager_type: Any) -> Callable: - """ - Adds an event to the Cog event list. - - :param manager_type: The manager type of the event. - :type manager_type: Any - :rtype: Callable - :return: The inner function. - :raises: TypeError: The listener isn't async. - """ - - def decorator(func): - if not inspect.iscoroutinefunction(func): - raise TypeError('Listeners must be async.') - - func._listener_type = manager_type - - return func - - return decorator
    - - -
    [docs]class DatabaseChecker(EventManager): - """ - A database checker which makes sure the database is connected to a manager and handles the table creation. - """ - - def __init__(self, - tables_column_data: List[Dict[str, str]], - table_identifiers: List[str]): - super().__init__() - - self.database = None - self.table_identifiers = table_identifiers - self.tables = {} - self.tables_column_data = tables_column_data - - def _check_database(self, raise_error: bool = True) -> bool: - """ - A function which checks if the database is connected. - - :param raise_error: A bool indicating if the function should raise an error if the database is not connected. - :type raise_error: bool - :rtype: bool - :return: If the database is connected. - :raises: DatabaseNotConnected: The database is not connected. - """ - - if not self.database: - if raise_error: - raise DatabaseNotConnected(f"Database not connected." - f" Connect this manager to a database using 'connect_to_database'") - - return False - - return True - -
    [docs] async def connect_to_database(self, database: Database, tables: List[str]) -> None: - """ - Connects to the database. - Calls on_database_connect when connected. - - :param database: The database to connect to. - :type database: Database - :param tables: The tables to create (incase they do not exist). - :type tables: List[str] - :rtype: None - :return: None - """ - - for table, table_data, identifier in zip(tables, self.tables_column_data, self.table_identifiers): - types = generate_column_types(table_data.values(), type(database.database)) - - await database.create_table(table, dict(zip(list(table_data), types)) if types else None, True) - - self.database = database - self.tables[identifier] = table - - await self.call_event("on_database_connect")
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Birthday.html b/docs/_build/html/_modules/discordSuperUtils/Birthday.html deleted file mode 100644 index eaac70f..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Birthday.html +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - - - discordSuperUtils.Birthday — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Birthday

    -from __future__ import annotations
    -
    -import asyncio
    -from datetime import datetime, timedelta
    -from typing import (
    -    Dict,
    -    List,
    -    Optional,
    -    Any
    -)
    -
    -import discord
    -import pytz
    -from discord.ext import commands
    -
    -from .Base import DatabaseChecker
    -
    -
    -
    [docs]class PartialBirthdayMember: - def __init__(self, member: discord.Member, birthday_date: datetime, timezone: str): - self.member = member - self.birthday_date = birthday_date - self.timezone = timezone
    - - -
    [docs]class BirthdayMember: - def __init__(self, birthday_manager: BirthdayManager, member: discord.Member): - self.birthday_manager = birthday_manager - self.member = member - self.table = self.birthday_manager.tables['birthdays'] - - @property - def __checks(self) -> Dict[str, int]: - return {'guild': self.member.guild.id, 'member': self.member.id} - -
    [docs] async def birthday_date(self) -> datetime: - birthday_data = await self.birthday_manager.database.select(self.table, ["utc_birthday"], self.__checks) - return datetime.utcfromtimestamp(birthday_data["utc_birthday"])
    - -
    [docs] async def timezone(self) -> str: - timezone_data = await self.birthday_manager.database.select(self.table, ["timezone"], self.__checks) - return timezone_data["timezone"]
    - -
    [docs] async def delete(self) -> PartialBirthdayMember: - partial = PartialBirthdayMember(self.member, await self.birthday_date(), await self.timezone()) - await self.birthday_manager.database.delete(self.table, self.__checks) - return partial
    - -
    [docs] async def set_birthday_date(self, timestamp: float) -> None: - await self.birthday_manager.database.update(self.table, {"utc_birthday": timestamp}, self.__checks)
    - -
    [docs] async def set_timezone(self, timezone: str) -> None: - await self.birthday_manager.database.update(self.table, {"timezone": timezone}, self.__checks)
    - -
    [docs] async def age(self) -> int: - current_birthday = await self.birthday_date() - current_datetime = datetime.now() - - return current_datetime.year - current_birthday.year - (datetime(year=current_datetime.year, - month=current_birthday.month, - day=current_birthday.day) >= current_datetime)
    - - -
    [docs]class BirthdayManager(DatabaseChecker): - def __init__(self, bot: commands.Bot): - super().__init__([ - {'guild': 'snowflake', 'member': 'snowflake', 'utc_birthday': 'snowflake', 'timezone': 'string'} - ], ['birthdays']) - self.bot = bot - self.add_event(self.on_database_connect) - -
    [docs] async def on_database_connect(self): - self.bot.loop.create_task(self.__detect_birthdays())
    - -
    [docs] async def create_birthday(self, - member: discord.Member, - member_birthday: float, - timezone: str = "UTC") -> None: - self._check_database() - - await self.database.insertifnotexists(self.tables['birthdays'], - dict(zip(self.tables_column_data[0], - [member.guild.id, member.id, member_birthday, timezone])), - {'guild': member.guild.id, 'member': member.id})
    - -
    [docs] async def get_birthday(self, member: discord.Member) -> Optional[BirthdayMember]: - self._check_database() - - member_data = await self.database.select(self.tables['birthdays'], - [], - {'guild': member.guild.id, 'member': member.id}, True) - - if member_data: - return BirthdayMember(self, member) - - return None
    - -
    [docs] async def get_upcoming(self, guild: discord.Guild) -> List[BirthdayMember]: - self._check_database() - - member_data = await self.database.select(self.tables['birthdays'], [], fetchall=True) - - member_data_formatted = [] - for member in member_data: - current_datetime = datetime.now(pytz.timezone(member["timezone"])) - member["utc_birthday"] = datetime.utcfromtimestamp(member["utc_birthday"]) - - new_date = member["utc_birthday"].replace(year=current_datetime.year) - if new_date.timestamp() - current_datetime.timestamp() < 0: - new_date = new_date.replace(year=current_datetime.year + 1) - - member["utc_birthday"] = new_date - - member_data_formatted.append(member) - - birthdays = [] - for birthday_member in sorted(member_data_formatted, key=lambda x: x["utc_birthday"]): - member = guild.get_member(birthday_member['member']) - - if member: - birthdays.append(BirthdayMember(self, member)) - - return birthdays
    - -
    [docs] @staticmethod - def get_midnight_timezones() -> List[str]: - """ - This method returns a list of timezones where the current time is 12 am. - :return: - """ - - current_utc_time = datetime.utcnow() - utc_offset = -(current_utc_time.hour % 24) - - minutes = 30 if current_utc_time.minute > 5 else 0 - if minutes == 30: - utc_offset -= 1 - - checks = ( - timedelta(hours=utc_offset, minutes=minutes), - timedelta(hours=24 - -utc_offset if current_utc_time.hour != 0 else 0, minutes=minutes) - ) - - return [tz.zone for tz in map(pytz.timezone, pytz.all_timezones_set) - if current_utc_time.astimezone(tz).utcoffset() in checks]
    - -
    [docs] async def get_members_with_birthday(self, timezones: List[str]) -> List[Dict[str, Any]]: - """ - This function receives a list of timezones and returns a list of members that have birthdays in that date - and timezone. - - :param timezones: - :return: - """ - - result_members = [] - registered_members = await self.database.select(self.tables['birthdays'], [], fetchall=True) - - birthday_members = [x for x in registered_members if x["timezone"] in timezones] - for birthday_member in birthday_members: - timezone_time = datetime.now(pytz.timezone(birthday_member["timezone"])) - date_of_birth = datetime.fromtimestamp(birthday_member["utc_birthday"]) - - if date_of_birth.month == timezone_time.month and date_of_birth.day == timezone_time.day: - result_members.append(birthday_member) - - return result_members
    - -
    [docs] @staticmethod - def round_to_nearest(timedelta_to_round): - """ - This function receives a timedelta to round to and gets the amount of seconds before that timestamp. - - :param timedelta_to_round: - :return: - """ - - now = datetime.now() - nearest = now + (datetime.min - now) % timedelta_to_round - return nearest.timestamp() - now.timestamp()
    - - async def __detect_birthdays(self) -> None: - await self.bot.wait_until_ready() - - while not self.bot.is_closed(): - await asyncio.sleep(self.round_to_nearest(timedelta(minutes=30))) - - for birthday_member in await self.get_members_with_birthday(self.get_midnight_timezones()): - guild = self.bot.get_guild(birthday_member["guild"]) - - if guild: - member = guild.get_member(birthday_member["member"]) - - if member: - await self.call_event("on_member_birthday", BirthdayMember( - self, member - ))
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/CommandHinter.html b/docs/_build/html/_modules/discordSuperUtils/CommandHinter.html deleted file mode 100644 index e8600ad..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/CommandHinter.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - discordSuperUtils.CommandHinter — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.CommandHinter

    -from abc import ABC, abstractmethod
    -from difflib import SequenceMatcher
    -from typing import (
    -    List,
    -    Union
    -)
    -
    -import discord
    -from discord.ext import commands
    -
    -from .Base import get_generator_response
    -
    -__all__ = ("CommandResponseGenerator", "DefaultResponseGenerator", "CommandHinter")
    -
    -
    -
    [docs]class CommandResponseGenerator(ABC): - __slots__ = () - -
    [docs] @abstractmethod - def generate(self, invalid_command: str, suggestions: List[str]) -> Union[str, discord.Embed]: - pass
    - - -
    [docs]class DefaultResponseGenerator(CommandResponseGenerator): - __slots__ = () - -
    [docs] def generate(self, invalid_command: str, suggestions: List[str]) -> discord.Embed: - embed = discord.Embed( - title="Invalid command!", - description=f"**`{invalid_command}`** is invalid. Did you mean:", - color=0x00ff00 - ) - - for index, suggestion in enumerate(suggestions[:3]): - embed.add_field(name=f"**{index + 1}.**", value=f"**`{suggestion}`**", inline=False) - - return embed
    - - -
    [docs]class CommandHinter: - __slots__ = ("bot", "generator") - - def __init__(self, - bot: commands.Bot, - generator=None): - self.bot = bot - self.generator = DefaultResponseGenerator if generator is None else generator - - self.bot.add_listener(self.__handle_hinter, "on_command_error") - - @property - def command_names(self) -> List[str]: - names = [] - - for command in self.bot.commands: - if isinstance(command, commands.Group): - names += [command.name] + command.aliases - for inner_command in command.commands: - names += [inner_command.name] + inner_command.aliases - - else: - names += [command.name] + command.aliases - - return names - - async def __handle_hinter(self, ctx: commands.Context, error) -> None: - if isinstance(error, commands.CommandNotFound): - command_similarity = {} - command_used = ctx.message.content.lstrip(ctx.prefix)[:max([len(c) for c in self.command_names])] - - for command in self.command_names: - command_similarity[SequenceMatcher(None, command, command_used).ratio()] = command - - generated_message = get_generator_response( - self.generator, - CommandResponseGenerator, - command_used, - [x[1] for x in sorted(command_similarity.items(), reverse=True)] - ) - - if isinstance(generated_message, discord.Embed): - await ctx.send(embed=generated_message) - elif isinstance(generated_message, str): - await ctx.send(generated_message) - else: - raise TypeError("The generated message must be of type 'discord.Embed' or 'str'.") - - else: - raise error
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Convertors.html b/docs/_build/html/_modules/discordSuperUtils/Convertors.html deleted file mode 100644 index e1b4dd4..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Convertors.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - discordSuperUtils.Convertors — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Convertors

    -from typing import (
    -    Optional,
    -    Union
    -)
    -
    -from discord.ext import commands
    -
    -
    -
    [docs]def isfloat(string: str) -> bool: - """ - This function receives a string and returns if it is a float or not - :param string: - :return: - """ - - try: - float(string) - return True - except (ValueError, TypeError): - return False
    - - -
    [docs]class TimeConvertor(commands.Converter): - """ - Converts a given argument to an int that represents time in seconds. - - Examples - ---------- - 7d: 604800 (7 days in seconds) - - 1m: 60 (1 minute in seconds) - - heyh: BadArgument ('hey' is not an int) - - 100j: BadArgument ('j' is not a valid time multiplier) - """ - -
    [docs] async def convert(self, ctx: commands.Context, argument: str) -> Optional[Union[int, float]]: - time_multipliers = {"s": 1, - "m": 60, - "h": 60 * 60, - "d": 60 * 60 * 24, - "w": 60 * 60 * 24 * 7} - - permanent = ["permanent", "perm", "0"] - if argument.lower() in permanent: - return 0 - - if not isfloat(argument[:-1]) or argument[-1] not in time_multipliers.keys(): - raise commands.BadArgument(f"Invalid time argument provided, cannot convert '{argument}' to time.") - - return float(argument[:-1]) * time_multipliers[argument[-1]]
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Database.html b/docs/_build/html/_modules/discordSuperUtils/Database.html deleted file mode 100644 index 0c4c6c9..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Database.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - discordSuperUtils.Database — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Database

    -import asyncio
    -import sys
    -from abc import ABC, abstractmethod
    -from typing import (
    -    Dict,
    -    Any,
    -    Optional,
    -    List,
    -    Union
    -)
    -
    -import aiomysql
    -import aiopg
    -import aiosqlite
    -from motor import motor_asyncio
    -
    -if sys.version_info >= (3, 8) and sys.platform.lower().startswith("win"):
    -    # Aiopg requires the event loop policy to be WindowsSelectorEventLoop, if it is not, aiopg raises an error.
    -
    -    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    -
    -
    -
    [docs]async def create_mysql(host, port, user, password, dbname): - # Created this function to make sure the user has autocommit enabled. - # we must make sure autocommit is enabled because manual commits are not working on aiomysql :) - return await aiomysql.create_pool(host=host, port=port, user=user, password=password, db=dbname, autocommit=True)
    - - -
    [docs]class UnsupportedDatabase(Exception): - """Raises error when the user tries to use an unsupported database."""
    - - -
    [docs]class Database(ABC): - def __init__(self, database): - self.database = database - -
    [docs] @abstractmethod - async def close(self): - pass
    - -
    [docs] @abstractmethod - async def insertifnotexists(self, table_name: str, data: Dict[str, Any], checks: Dict[str, Any]): - pass
    - -
    [docs] @abstractmethod - async def insert(self, table_name: str, data: Dict[str, Any]): - pass
    - -
    [docs] @abstractmethod - async def create_table(self, - table_name: str, - columns: Optional[Dict[str, str]] = None, - exists: Optional[bool] = False): - pass
    - -
    [docs] @abstractmethod - async def update(self, table_name: str, data: Dict[str, Any], checks: Dict[str, Any]): - pass
    - -
    [docs] @abstractmethod - async def updateorinsert(self, - table_name: str, - data: Dict[str, Any], - checks: Dict[str, Any], - insert_data: Dict[str, Any]): - pass
    - -
    [docs] @abstractmethod - async def delete(self, table_name: str, checks: Dict[str, Any]): - pass
    - -
    [docs] @abstractmethod - async def select(self, - table_name: str, - keys: List[str], - checks: Optional[Dict[str, Any]] = None, - fetchall: Optional[bool] = False): - pass
    - -
    [docs] @abstractmethod - async def execute(self, - sql_query: str, - values: List[Any], - fetchall: bool = True) -> Union[List[Dict[str, Any]], Dict[str, Any]]: - pass
    - - -class _MongoDatabase(Database): - def __str__(self): - return f"<{self.__class__.__name__} '{self.name}'>" - - @property - def name(self): - return self.database.name - - async def close(self): - self.database.client.close() - - async def insertifnotexists(self, table_name, data, checks): - response = await self.select(table_name, [], checks, True) - - if not response: - return await self.insert(table_name, data) - - async def insert(self, table_name, data): - return await self.database[table_name].insert_one(data) - - async def create_table(self, table_name, _=None, exists=False): - # create_table has an unused positional parameter to make the methods consistent between database types. - - if exists and table_name in await self.database.list_collection_names(): - return - - return await self.database.create_collection(table_name) - - async def update(self, table_name, data, checks): - return await self.database[table_name].update_one(checks, {"$set": data}) - - async def updateorinsert(self, table_name, data, checks, insert_data): - response = await self.select(table_name, [], checks, True) - - if len(response) == 1: - return await self.update(table_name, data, checks) - - return await self.insert(table_name, insert_data) - - async def delete(self, table_name, checks=None): - return await self.database[table_name].delete_one({} if checks is None else checks) - - async def select(self, table_name, keys, checks=None, fetchall=False): - checks = {} if checks is None else checks - - if fetchall: - fetch = self.database[table_name].find(checks) - result = [] - - async for doc in fetch: - current_doc = {} - - for key, value in doc.items(): - if not keys or key in keys: - current_doc[key] = value - - result.append(current_doc) - else: - fetch = await self.database[table_name].find_one(checks) - result = {} - - if fetch is not None: - for key, value in fetch.items(): - if not keys or key in keys: - result[key] = value - else: - result = None - - return result - - async def execute(self, - sql_query: str, - values: List[Any], - fetchall: bool = True) -> Union[List[Dict[str, Any]], Dict[str, Any]]: - raise NotImplementedError("NoSQL databases cannot execute sql queries.") - - -class _SqlDatabase(Database): - def __str__(self): - return f"<{self.__class__.__name__}>" - - def with_commit(func): - async def inner(self, *args, **kwargs): - resp = await func(self, *args, **kwargs) - if self.commit_needed: - await self.commit() - - return resp - - return inner - - def with_cursor(func): - async def inner(self, *args, **kwargs): - database = await self.database.acquire() if self.pool else self.database - - if self.cursor_context: - async with database.cursor() as cursor: - resp = await func(self, cursor, *args, **kwargs) - - else: - cursor = await database.cursor() - resp = await func(self, cursor, *args, **kwargs) - await cursor.close() - - if self.pool: - self.database.release(database) - - return resp - - return inner - - def __init__(self, database): - super().__init__(database) - self.place_holder = DATABASE_TYPES[type(database)]["placeholder"] - self.cursor_context = DATABASE_TYPES[type(database)]['cursorcontext'] - self.commit_needed = DATABASE_TYPES[type(database)]['commit'] - self.quote = DATABASE_TYPES[type(database)]['quotes'] - self.pool = DATABASE_TYPES[type(database)]['pool'] - - async def commit(self): - if not self.pool: - await self.database.commit() - - async def close(self): - await self.database.close() - - async def insertifnotexists(self, table_name, data, checks): - response = await self.select(table_name, [], checks, True) - - if not response: - return await self.insert(table_name, data) - - @with_cursor - @with_commit - async def insert(self, cursor, table_name, data): - query = f"INSERT INTO {table_name} ({', '.join(data.keys())}) VALUES ({', '.join([self.place_holder] * len(data.values()))})" - await cursor.execute(query, list(data.values())) - - @with_cursor - @with_commit - async def create_table(self, cursor, table_name, columns=None, exists=False): - query = f'CREATE TABLE {"IF NOT EXISTS" if exists else ""} {self.quote}{table_name}{self.quote} (' - - for column in [] if columns is None else columns: - query += f"\n{self.quote}{column}{self.quote} {columns[column]}," - query = query[:-1] - - query += "\n);" - await cursor.execute(query) - - @with_cursor - @with_commit - async def update(self, cursor, table_name, data, checks): - query = f"UPDATE {table_name} SET " - - if data: - for key in data: - query += f"{key} = {self.place_holder}, " - query = query[:-2] - - if checks: - query += " WHERE " - for check in checks: - query += f"{check} = {self.place_holder} AND " - - query = query[:-4] - - await cursor.execute(query, list(data.values()) + list(checks.values())) - - async def updateorinsert(self, table_name, data, checks, insert_data): - response = await self.select(table_name, [], checks, True) - - if len(response) == 1: - return await self.update(table_name, data, checks) - - return await self.insert(table_name, insert_data) - - @with_cursor - @with_commit - async def delete(self, cursor, table_name, checks=None): - checks = {} if checks is None else checks - - query = f"DELETE FROM {table_name} " - - if checks: - query += "WHERE " - for check in checks: - query += f"{check} = {self.place_holder} AND " - - query = query[:-4] - - await cursor.execute(query, list(checks.values())) - - @with_cursor - async def select(self, cursor, table_name, keys, checks=None, fetchall=False): - checks = {} if checks is None else checks - - keys = '*' if not keys else keys - query = f"SELECT {','.join(keys)} FROM {table_name} " - - if checks: - query += "WHERE " - for check in checks: - query += f"{check} = {self.place_holder} AND " - - query = query[:-4] - - await cursor.execute(query, list(checks.values())) - columns = [x[0] for x in cursor.description] - - result = await cursor.fetchall() if fetchall else await cursor.fetchone() - if not result: - return result - - return [dict(zip(columns, x)) for x in result] if fetchall else dict(zip(columns, result)) - - @with_cursor - @with_commit - async def execute(self, - cursor, - sql_query: str, - values: List[Any] = None, - fetchall: bool = True) -> Union[List[Dict[str, Any]], Dict[str, Any]]: - await cursor.execute(sql_query, values if values is not None else []) - - result = await cursor.fetchall() if fetchall else await cursor.fetchone() - columns = [x[0] for x in cursor.description] - if not result: - return result - - return [dict(zip(columns, x)) for x in result] if fetchall else dict(zip(columns, result)) - - -DATABASE_TYPES: Dict[Any, Dict[str, Any]] = { - motor_asyncio.AsyncIOMotorDatabase: {"class": _MongoDatabase, - "placeholder": None}, - - aiosqlite.core.Connection: {"class": _SqlDatabase, - "placeholder": '?', - 'cursorcontext': True, - 'commit': True, - 'quotes': '"', - 'pool': False}, - - aiopg.pool.Pool: {"class": _SqlDatabase, - "placeholder": '%s', - 'cursorcontext': True, - 'commit': True, - 'quotes': '"', - 'pool': True}, - - aiomysql.pool.Pool: {"class": _SqlDatabase, - "placeholder": '%s', - 'cursorcontext': True, - 'commit': False, - 'quotes': '`', - 'pool': True} -} - - -DATABASES: List = [_SqlDatabase, _MongoDatabase] - - -
    [docs]class DatabaseManager: -
    [docs] @staticmethod - def connect(database): - if type(database) not in DATABASE_TYPES: - raise UnsupportedDatabase(f"Database of type {type(database)} is not supported by the database manager.") - - return DATABASE_TYPES[type(database)]["class"](database)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Economy.html b/docs/_build/html/_modules/discordSuperUtils/Economy.html deleted file mode 100644 index 42216f3..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Economy.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - discordSuperUtils.Economy — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Economy

    -import discord
    -from typing import (
    -    List,
    -    Optional
    -)
    -
    -from .Base import DatabaseChecker
    -
    -
    -
    [docs]class EconomyAccount: - def __init__(self, guild: int, member: int, database, table): - self.guild = guild - self.member = member - self.database = database - self.table = table - - def __str__(self): - return f"<Account MEMBER={self.member}, GUILD={self.guild}>" - - @property - def __checks(self): - return EconomyManager.generate_checks(self.guild, self.member) - -
    [docs] async def currency(self): - currency_data = await self.database.select(self.table, ['currency'], self.__checks) - return currency_data["currency"]
    - -
    [docs] async def bank(self): - bank_data = await self.database.select(self.table, ['bank'], self.__checks) - return bank_data["bank"]
    - -
    [docs] async def net(self): - return await self.bank() + await self.currency()
    - -
    [docs] async def change_currency(self, amount: int): - currency = await self.currency() - await self.database.update(self.table, {'currency': currency + amount}, self.__checks)
    - -
    [docs] async def change_bank(self, amount: int): - bank_amount = await self.bank() - await self.database.update(self.table, {'bank': bank_amount + amount}, self.__checks)
    - - -
    [docs]class EconomyManager(DatabaseChecker): - def __init__(self, bot): - super().__init__([{'guild': 'snowflake', 'member': 'snowflake', 'currency': 'snowflake', 'bank': 'snowflake'}], - ['economy']) - self.bot = bot - -
    [docs] @staticmethod - def generate_checks(guild: int, member: int): - return {'guild': guild, 'member': member}
    - -
    [docs] async def create_account(self, member: discord.Member) -> None: - self._check_database() - - await self.database.insertifnotexists(self.tables['economy'], - {"guild": member.guild.id, - "member": member.id, - "currency": 0, - "bank": 0 - }, - self.generate_checks(member.guild.id, member.id))
    - -
    [docs] async def get_account(self, member: discord.Member) -> Optional[EconomyAccount]: - self._check_database() - - member_data = await self.database.select(self.tables['economy'], - [], - self.generate_checks(member.guild.id, member.id), - True) - - if member_data: - return EconomyAccount(member.guild.id, member.id, self.database, self.tables['economy']) - - return None
    - -
    [docs] async def get_leaderboard(self, guild) -> List[EconomyAccount]: - self._check_database() - - guild_info = await self.database.select(self.tables['economy'], [], {'guild': guild.id}, True) - members = [EconomyAccount(member_info['guild'], - member_info['member'], - database=self.database, - table=self.tables['economy']) for member_info in sorted(guild_info, - key=lambda x: x["bank"] + x[ - "currency"], - reverse=True)] - - return members
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/FiveM.html b/docs/_build/html/_modules/discordSuperUtils/FiveM.html deleted file mode 100644 index 8c345f8..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/FiveM.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - discordSuperUtils.FiveM — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.FiveM

    -import aiohttp
    -import aiohttp.client_exceptions
    -
    -
    -
    [docs]class ServerNotFound(Exception): - """Raises an error when a server is invalid or offline."""
    - - -
    [docs]class FiveMPlayer: - def __init__(self, player_id, identifiers, name, ping): - self.player_id = player_id - self.identifiers = identifiers - self.name = name - self.ping = ping - - def __str__(self): - return f"<FiveM Player {self.player_id=}>" - - def __repr__(self): - return f"<FiveM Player {self.name=}, {self.player_id=}, {self.identifiers=}, {self.ping=}>" - -
    [docs] @classmethod - def fetch(cls, player_dict): - identifiers = dict([x.split(':') for x in player_dict['identifiers']]) - return cls(player_dict['id'], identifiers, player_dict['name'], player_dict['ping'])
    - - -
    [docs]class FiveMServer: - def __init__(self, ip, resources, players, name, variables): - self.ip = ip - self.resources = resources - self.players = players - self.name = name - self.variables = variables - - def __str__(self): - return f"<FiveM Server {self.name=}>" - - def __repr__(self): - return f"<FiveM Server {self.ip=}," \ - f" {self.name=}," \ - f" {self.players=}," \ - f" {self.resources=}," \ - f" {self.variables=}>" - -
    [docs] @classmethod - async def fetch(cls, ip): - base_address = "http://" + ip + "/" - - async with aiohttp.ClientSession() as session: - try: - await session.get(base_address) # Server status check - except (aiohttp.client_exceptions.ClientConnectorError, aiohttp.client_exceptions.InvalidURL): - raise ServerNotFound(f"Server '{ip}' is invalid or offline.") - - players_request = await session.get(base_address + "players.json") - info_request = await session.get(base_address + "info.json") - dynamic_info_request = await session.get(base_address + "dynamic.json") - - info = await info_request.json(content_type=None) - players = await players_request.json(content_type=None) - dynamic = await dynamic_info_request.json(content_type=None) - # This is still included in the session because parsing the json outside of it sometimes doesnt work - # and block the program (?) :( - - return cls(ip, - info['resources'], - [FiveMPlayer.fetch(player) for player in players], - dynamic["hostname"], - info['vars'])
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Imaging.html b/docs/_build/html/_modules/discordSuperUtils/Imaging.html deleted file mode 100644 index f3ea5b9..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Imaging.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - discordSuperUtils.Imaging — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Imaging

    -from __future__ import annotations
    -
    -import os
    -import textwrap
    -from enum import Enum
    -from io import BytesIO
    -from typing import (
    -    Optional,
    -    Tuple,
    -    Union,
    -    TYPE_CHECKING
    -)
    -
    -import PIL
    -import PIL.ImageShow
    -import aiohttp
    -import discord
    -from PIL import Image, ImageFont, ImageDraw
    -
    -if TYPE_CHECKING:
    -    from .Leveling import LevelingAccount
    -
    -
    -__all__ = ("ImageManager", "Backgrounds")
    -
    -
    -
    [docs]class ImageManager: - __slots__ = () - -
    [docs] @staticmethod - def load_asset(name: str) -> str: - return os.path.join(os.path.dirname(__file__), 'assets', name)
    - -
    [docs] @staticmethod - async def make_request(url: str) -> Optional[bytes]: - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - return await response.read()
    - -
    [docs] @classmethod - async def convert_image(cls, url: str) -> Image: - return PIL.Image.open(BytesIO(await cls.make_request(url))).convert('RGBA')
    - -
    [docs] @classmethod - def human_format(cls, num): - original_num = num - - num = float('{:.3g}'.format(num)) - magnitude = 0 - matches = ['', 'K', 'M', 'B', 'T', 'Qua', 'Qui'] - while abs(num) >= 1000: - if magnitude >= 5: - break - - magnitude += 1 - num /= 1000.0 - - try: - return '{}{}'.format('{:f}'.format(num).rstrip('0').rstrip('.'), matches[magnitude]) - except IndexError: - return original_num
    - -
    [docs] @staticmethod - def multiline_text(card: ImageDraw, - text: str, - font: ImageFont, - text_color: Tuple[int, int, int], - start_height: Union[int, float], - width: int): - draw = ImageDraw.Draw(card) - image_width, image_height = card.size - - y_text = start_height - lines = textwrap.wrap(text, width=width) - - for line in lines: - line_width, line_height = font.getsize(line) - draw.text(((image_width - line_width) / 2, y_text), line, font=font, fill=text_color) - y_text += line_height
    - -
    [docs] async def draw_profile_picture(self, - card: Image, - member: discord.Member, - location: Tuple[int, int], - size: int = 180, - outline_thickness: int = 5, - status: bool = True, - outline_color: Tuple[int, int, int] = (255, 255, 255)): - blank = Image.new("RGBA", card.size, (255, 255, 255, 0)) - - location = tuple(round(x - size / 2) if i <= 1 else round(x + size / 2) - for i, x in enumerate(location + location)) - - outline_dimensions = tuple(x - outline_thickness if i <= 1 else x + outline_thickness - for i, x in enumerate(location)) - - size_dimensions = (size, size) - status_dimensions = tuple(round(x / 4) for x in size_dimensions) - - mask = Image.new("RGBA", card.size, 0) - ImageDraw.Draw(mask).ellipse(location, fill=(255, 25, 255, 255)) - - avatar = (await self.convert_image(str(member.avatar_url))).resize(size_dimensions) - profile_pic_holder = Image.new("RGBA", card.size, (255, 255, 255, 255)) - - ImageDraw.Draw(card).ellipse(outline_dimensions, fill=outline_color) - - profile_pic_holder.paste(avatar, location) - pre_card = Image.composite(profile_pic_holder, card, mask) - pre_card = pre_card.convert('RGBA') - - if status: - status_picture = Image.open(self.load_asset(f"{member.status.name}.png")) - status_picture = status_picture.convert("RGBA").resize(status_dimensions) - - blank.paste(status_picture, tuple(x - status_dimensions[0] for x in location[2:])) - - return Image.alpha_composite(pre_card, blank)
    - -
    [docs] async def create_welcome_card(self, - member: discord.Member, - background: Backgrounds, - text_color: Tuple[int, int, int], - title: str, - description: str, - font_path: str = None, - outline: int = 5, - transparency: int = 0) -> discord.File: - result_bytes = BytesIO() - - card = Image.open(background.value).resize((1024, 500)) - - font_path = font_path if font_path else self.load_asset("font.ttf") - - big_font = ImageFont.truetype(font_path, 36) - small_font = ImageFont.truetype(font_path, 30) - - draw = ImageDraw.Draw(card, 'RGBA') - draw.rectangle((30, 30, 994, 470), fill=(0, 0, 0, transparency)) - draw.text((512, 360), title, text_color, font=big_font, anchor="ms") - self.multiline_text(card, description, small_font, text_color, 380, 60) - - final_card = await self.draw_profile_picture(card, member, (512, 180), 260, outline_thickness=outline) - - final_card.save(result_bytes, format="PNG") - result_bytes.seek(0) - return discord.File(result_bytes, filename="welcome_card.png")
    - -
    [docs] async def merge_image(self, - foreground: str, - background: str, - blend_level: float = 0.6, - discord_file: bool = True) -> Union[discord.File, Image]: - """Merges two images together""" - foreground = await self.convert_image(foreground) - background = await self.convert_image(background) - result_bytes = BytesIO() - width, height = background.size - - foreground = foreground.resize((width, height), PIL.Image.ANTIALIAS) - result = PIL.Image.blend(background, foreground, alpha=blend_level) - - if discord_file: - result.save(result_bytes, format="PNG") - result_bytes.seek(0) - return discord.File(result_bytes, filename="mergedimage.png") - - return result
    - -
    [docs] async def create_leveling_profile(self, - member: discord.Member, - member_account: LevelingAccount, - background: Backgrounds, - text_color: Tuple[int, int, int], - rank: int, - font_path: str = None, - outline: int = 5) -> discord.File: - result_bytes = BytesIO() - - card = Image.open(background.value).resize((850, 238)) - - font_path = font_path if font_path else self.load_asset("font.ttf") - font_big = ImageFont.truetype(font_path, 36) - font_small = ImageFont.truetype(font_path, 20) - - draw = ImageDraw.Draw(card) - draw.text((245, 90), str(member), text_color, font=font_big, anchor="ls") - draw.text((800, 90), f"Rank #{rank}", text_color, font=font_big, anchor="rs") - draw.text((245, 165), f"Level {await member_account.level()}", text_color, font=font_small, anchor="ls") - draw.text((800, 165), - f"{self.human_format(await member_account.xp())} /" - f" {self.human_format(await member_account.next_level())} XP", - text_color, - font=font_small, - anchor="rs") - - draw.rounded_rectangle((245, 185, 800, 205), fill=(0, 0, 0, 0), outline=text_color, radius=10) - length_of_bar = await member_account.percentage_next_level() * 5.5 + 250 - draw.rounded_rectangle((245, 185, length_of_bar, 205), fill=text_color, radius=10) - - final_card = await self.draw_profile_picture(card, - member, - (109, 119), - outline_thickness=outline, - outline_color=text_color) - - final_card.save(result_bytes, format="PNG") - result_bytes.seek(0) - return discord.File(result_bytes, filename="rankcard.png")
    - - -
    [docs]class Backgrounds(Enum): - GALAXY = ImageManager.load_asset("1.png") - BLANK_GRAY = ImageManager.load_asset("2.png") - GAMING = ImageManager.load_asset("3.png")
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Infractions.html b/docs/_build/html/_modules/discordSuperUtils/Infractions.html deleted file mode 100644 index 76240db..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Infractions.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - discordSuperUtils.Infractions — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Infractions

    -from __future__ import annotations
    -
    -import uuid
    -from datetime import datetime
    -from typing import (
    -    List,
    -    TYPE_CHECKING,
    -    Optional,
    -    Dict,
    -    Union
    -)
    -
    -from .Base import DatabaseChecker
    -from .Punishments import (
    -    Punisher,
    -    get_relevant_punishment
    -)
    -
    -if TYPE_CHECKING:
    -    from .Punishments import Punishment
    -    import discord
    -    from discord.ext import commands
    -
    -
    -__all__ = ("PartialInfraction", "Infraction", "InfractionManager")
    -
    -
    -
    [docs]class PartialInfraction: - """ - A partial infraction. - """ - - def __init__(self, member: discord.Member, infraction_id: str, reason: str, date_of_infraction: datetime): - self.member = member - self.infraction_id = infraction_id - self.reason = reason - self.date_of_infraction = date_of_infraction
    - - -
    [docs]class Infraction: - """ - An infraction object. - """ - - def __init__(self, database, table: str, member: discord.Member, infraction_id: str): - self.member = member - self.database = database - self.table = table - self.infraction_id = infraction_id - - def __str__(self): - return f"<Infraction {self.infraction_id=}>" - - def __repr__(self): - return f"<Infraction {self.member=}, {self.infraction_id=}>" - - @property - def __checks(self) -> Dict[str, int]: - return {'guild': self.member.guild.id, 'member': self.member.id, 'id': self.infraction_id} - -
    [docs] async def datetime(self) -> Optional[datetime]: - timestamp_data = await self.database.select(self.table, ['timestamp'], self.__checks) - if timestamp_data: - return datetime.utcfromtimestamp(timestamp_data["timestamp"])
    - -
    [docs] async def reason(self) -> Optional[str]: - reason_data = await self.database.select(self.table, ['reason'], self.__checks) - if reason_data: - return reason_data["reason"]
    - -
    [docs] async def set_reason(self, new_reason: str) -> None: - await self.database.update(self.table, {'reason': new_reason}, self.__checks)
    - -
    [docs] async def delete(self) -> PartialInfraction: - partial = PartialInfraction(self.member, self.infraction_id, await self.reason(), await self.datetime()) - await self.database.delete(self.table, self.__checks) - return partial
    - - -
    [docs]class InfractionManager(DatabaseChecker, Punisher): - def __init__(self, bot: commands.Bot): - super().__init__([ - {'guild': "snowflake", 'member': "snowflake", 'timestamp': "snowflake", 'id': 'string', 'reason': 'string'} - ], ['infractions']) - self.punishments = [] - self.bot = bot - -
    [docs] def add_punishments(self, punishments: List[Punishment]) -> None: - self.punishments = punishments
    - -
    [docs] async def warn(self, ctx: commands.Context, member: discord.Member, reason: str) -> Infraction: - self._check_database() - - generated_id = str(uuid.uuid4()) - await self.database.insert(self.tables['infractions'], { - 'guild': member.guild.id, - 'member': member.id, - 'timestamp': datetime.utcnow().timestamp(), - 'id': generated_id, - 'reason': reason - }) - - if punishment := get_relevant_punishment(self.punishments, len(await self.get_infractions(member))): - await punishment.punishment_manager.punish(ctx, member, punishment) - - return Infraction(self.database, self.tables['infractions'], member, generated_id)
    - -
    [docs] async def punish(self, ctx: commands.Context, member: discord.Member, punishment: Punishment) -> None: - await self.warn(ctx, member, punishment.punishment_reason) - await self.call_event("on_punishment", ctx, member, punishment)
    - -
    [docs] async def get_infractions(self, - member: discord.Member, - infraction_id: str = None, - from_timestamp: Union[int, float] = 0) -> List[Infraction]: - self._check_database() - - checks = {'guild': member.guild.id, 'member': member.id} - if infraction_id: - checks['id'] = infraction_id - - warnings = await self.database.select(self.tables['infractions'], [], checks, fetchall=True) - - return [Infraction(self.database, - self.tables['infractions'], - member, - infraction['id']) for infraction in warnings if infraction['timestamp'] > from_timestamp]
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/InviteTracker.html b/docs/_build/html/_modules/discordSuperUtils/InviteTracker.html deleted file mode 100644 index b82827a..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/InviteTracker.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - discordSuperUtils.InviteTracker — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.InviteTracker

    -""""
    -If InviteTracker is used in any way that breaks Discord TOS we, (the DiscordSuperUtils team)
    -are not responsible or liable in any way.
    -InviteTracker by DiscordSuperUtils was not intended to violate Discord TOS in any way.
    -In case we are contacted by Discord, we will remove any and all features that violate the Discord ToS.
    -Please feel free to read the Discord Terms of Service https://discord.com/terms.
    -"""
    -
    -from __future__ import annotations
    -
    -from typing import (
    -    TYPE_CHECKING,
    -    Union,
    -    Optional
    -)
    -
    -from .Base import DatabaseChecker
    -
    -if TYPE_CHECKING:
    -    import discord
    -    from discord.ext import commands
    -
    -
    -
    [docs]class InviteAccount: - def __init__(self, invite_tracker: InviteTracker, member: discord.Member): - self.invite_tracker = invite_tracker - self.member = member - - def __str__(self): - return f"<member={self.member.id}>" - -
    [docs] async def get_invited_users(self): - return await self.invite_tracker.get_members_invited(self.member, self.member.guild)
    - - -
    [docs]class InviteTracker(DatabaseChecker): - def __init__(self, bot: commands.Bot): - super().__init__([{'guild': 'snowflake', 'member': 'snowflake', 'members_invited': 'string'}], ['invites']) - self.bot = bot - self.cache = {} - - self.bot.loop.create_task(self.__initialize_cache()) - - self.bot.add_listener(self.__cleanup_guild_cache, 'on_guild_remove') - self.bot.add_listener(self.__update_guild_cache, 'on_guild_add') - self.bot.add_listener(self.__track_invite, 'on_invite_create') - self.bot.add_listener(self.__cleanup_invite, 'on_invite_delete') - -
    [docs] async def get_invite(self, member: discord.Member) -> Optional[discord.Invite]: - for inv in await member.guild.invites(): - for invite in self.cache[member.guild.id]: - if invite.revoked: - self.cache[invite.guild.id].remove(invite) - return - - if invite.code == inv.code and inv.uses - invite.uses == 1: - await self.__update_guild_cache(member.guild) - return inv
    - -
    [docs] async def get_members_invited(self, user: Union[discord.User, discord.Member], guild: discord.Guild): - self._check_database() - - invited_members = await self.database.select(self.tables['invites'], ['members_invited'], { - 'guild': guild.id, - 'member': user.id - }) - - if not invited_members: - return [] - - invited_members = invited_members["members_invited"] - if isinstance(invited_members, str): - return [int(invited_member) for invited_member in invited_members.split('\0') if invited_member]
    - -
    [docs] async def fetch_inviter(self, invite: discord.Invite) -> Union[discord.Member, discord.User]: - inviter = invite.guild.get_member(invite.inviter.id) - return inviter if inviter else await self.bot.get_user(invite.inviter.id)
    - -
    [docs] async def register_invite(self, - invite: discord.Invite, - member: discord.Member, - inviter: Union[discord.Member, discord.User]) -> None: - self._check_database() - - invited_members = await self.get_members_invited(inviter, invite.guild) - if member.id in invited_members: - return - - invited_members.append(member.id) - invited_members_sql = '\0'.join(str(invited_member) for invited_member in invited_members) - - await self.database.updateorinsert( - self.tables['invites'], - {'members_invited': invited_members_sql}, - {'guild': invite.guild.id, 'member': inviter.id}, - {'guild': invite.guild.id, 'member': inviter.id, 'members_invited': invited_members_sql} - )
    - - async def __initialize_cache(self) -> None: - await self.bot.wait_until_ready() - - for guild in self.bot.guilds: - self.cache[guild.id] = await guild.invites() - - async def __update_guild_cache(self, guild: discord.Guild) -> None: - self.cache[guild.id] = await guild.invites() - - async def __track_invite(self, invite: discord.Invite) -> None: - self.cache[invite.guild.id].append(invite) - - async def __cleanup_invite(self, invite: discord.Invite) -> None: - if invite in self.cache[invite.guild.id]: - self.cache[invite.guild.id].remove(invite) - - async def __cleanup_guild_cache(self, guild: discord.Guild) -> None: - self.cache.pop(guild.id) - -
    [docs] def get_user_info(self, member: discord.Member) -> InviteAccount: - self._check_database() - - return InviteAccount(self, member)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Kick.html b/docs/_build/html/_modules/discordSuperUtils/Kick.html deleted file mode 100644 index 31db422..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Kick.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - discordSuperUtils.Kick — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Kick

    -from __future__ import annotations
    -
    -from typing import TYPE_CHECKING
    -
    -import discord
    -
    -from .Base import EventManager
    -from .Punishments import Punisher
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    from .Punishments import Punishment
    -
    -
    -__all__ = ("KickManager",)
    -
    -
    -
    [docs]class KickManager(EventManager, Punisher): - """ - A KickManager that manages kicks for guilds. - """ - - __slots__ = ("bot",) - - def __init__(self, bot: commands.Bot): - super().__init__() - self.bot = bot - -
    [docs] async def punish(self, ctx: commands.Context, member: discord.Member, punishment: Punishment) -> None: - try: - await member.kick(reason=punishment.punishment_reason) - except discord.errors.Forbidden as e: - raise e - else: - await self.call_event("on_punishment", ctx, member, punishment)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Leveling.html b/docs/_build/html/_modules/discordSuperUtils/Leveling.html deleted file mode 100644 index d6d1b03..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Leveling.html +++ /dev/null @@ -1,338 +0,0 @@ - - - - - - - - discordSuperUtils.Leveling — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Leveling

    -from __future__ import annotations
    -
    -import math
    -import time
    -from typing import (
    -    Iterable,
    -    TYPE_CHECKING,
    -    List
    -)
    -
    -from .Base import DatabaseChecker
    -
    -if TYPE_CHECKING:
    -    import discord
    -
    -
    -
    [docs]class LevelingAccount: - def __init__(self, leveling_manager: LevelingManager, member: discord.Member): - self.leveling_manager = leveling_manager - self.table = self.leveling_manager.tables["xp"] - self.member = member - - def __str__(self): - return f"<Account MEMBER={self.member}, GUILD={self.member.guild}>" - - @property - def __checks(self): - return LevelingManager.generate_checks(self.member) - -
    [docs] async def xp(self): - xp_data = await self.leveling_manager.database.select(self.table, ['xp'], self.__checks) - return xp_data["xp"]
    - -
    [docs] async def level(self): - rank_data = await self.leveling_manager.database.select(self.table, ['rank'], self.__checks) - return rank_data["rank"]
    - -
    [docs] async def next_level(self): - level_up_data = await self.leveling_manager.database.select(self.table, ['level_up'], self.__checks) - return level_up_data["level_up"]
    - -
    [docs] async def percentage_next_level(self): - level_up = await self.next_level() - xp = await self.xp() - initial_xp = await self.initial_rank_xp() - - return min(abs(math.floor(abs(xp - initial_xp) / (level_up - initial_xp) * 100)), 100)
    - -
    [docs] async def initial_rank_xp(self): - next_level = await self.next_level() - return 0 if next_level == 50 else next_level / self.leveling_manager.rank_multiplier
    - -
    [docs] async def set_xp(self, value): - await self.leveling_manager.database.update(self.table, {"xp": value}, self.__checks)
    - -
    [docs] async def set_level(self, value): - await self.leveling_manager.database.update(self.table, {"rank": value}, self.__checks)
    - -
    [docs] async def set_next_level(self, value): - await self.leveling_manager.database.update(self.table, {"level_up": value}, self.__checks)
    - - -
    [docs]class LevelingManager(DatabaseChecker): - def __init__(self, - bot, - award_role: bool = False, - default_role_interval: int = 5, - xp_on_message=5, - rank_multiplier=1.5, - xp_cooldown=60): - super().__init__([ - {'guild': 'snowflake', 'member': 'snowflake', 'rank': 'number', 'xp': 'number', 'level_up': 'number'}, - {'guild': 'snowflake', 'interval': 'smallnumber'}, - {'guild': 'snowflake', 'role': 'snowflake'} - ], ['xp', 'roles', 'role_list']) - - self.bot = bot - self.award_role = award_role - self.default_role_interval = default_role_interval - self.xp_on_message = xp_on_message - self.rank_multiplier = rank_multiplier - self.xp_cooldown = xp_cooldown - - self.cooldown_members = {} - self.add_event(self.on_database_connect) - -
    [docs] async def set_interval(self, guild: discord.Guild, interval: int = None) -> None: - """ - Set the role interval of a guild. - - :param interval: The interval to set. - :type interval: int - :param guild: The guild to set the role interval in. - :type guild: discord.Guild - :return: - :rtype: None - """ - - interval = interval if interval is not None else self.default_role_interval - - if 0 >= interval: - raise ValueError("The interval must be greater than 0.") - - sql_insert_data = { - 'guild': guild.id, - 'interval': interval - } - - await self.database.updateorinsert(self.tables["roles"], - sql_insert_data, - {'guild': guild.id}, - sql_insert_data)
    - -
    [docs] async def get_roles(self, guild: discord.Guild) -> List[int]: - """ - Returns the role IDs of the guild. - - :param guild: The guild to get the roles from. - :type guild: discord.Guild - :return: - :rtype: List[int] - """ - - self._check_database() - - return [role["role"] for role in await self.database.select(self.tables["role_list"], - ['role'], - {'guild': guild.id}, - fetchall=True)]
    - -
    [docs] async def set_roles(self, guild: discord.Guild, roles: Iterable[discord.Role]) -> None: - """ - Sets the roles of the guild. - - :param guild: The guild to set the roles in. - :type guild: discord.Guild - :param roles: The roles to set. - :type roles: Iterable[discord.Role] - :return: - :rtype: None - """ - - self._check_database() - - await self.database.delete(self.tables["role_list"], {'guild': guild.id}) - - for role in roles: - await self.database.insert(self.tables["role_list"], {'guild': guild.id, "role": role.id})
    - -
    [docs] async def on_database_connect(self): - self.bot.add_listener(self.__handle_experience, 'on_message')
    - -
    [docs] @staticmethod - def generate_checks(member: discord.Member): - return {'guild': member.guild.id, 'member': member.id}
    - - async def __handle_experience(self, message): - self._check_database() - - if not message.guild or message.author.bot: - return - - member_cooldown = self.cooldown_members.setdefault(message.guild.id, {}).get(message.author.id, 0) - - if (time.time() - member_cooldown) >= self.xp_cooldown: - await self.create_account(message.author) - member_account = await self.get_account(message.author) - - await member_account.set_xp(await member_account.xp() + self.xp_on_message) - self.cooldown_members[message.guild.id][message.author.id] = time.time() - - leveled_up = False - while await member_account.xp() >= await member_account.next_level(): - await member_account.set_next_level(await member_account.next_level() * self.rank_multiplier) - await member_account.set_level(await member_account.level() + 1) - leveled_up = True - - if leveled_up: - roles = [] - if self.award_role: - role_ids = await self.get_roles(message.guild) - interval = await self.database.select(self.tables['roles'], - ['interval'], - {'guild': message.guild.id}) - interval = interval["interval"] if interval else self.default_role_interval - - if role_ids: - member_level = await member_account.level() - - if member_level % interval == 0 and member_level // interval <= len(role_ids): - roles = [message.guild.get_role(role_id) for role_id in - role_ids][:await member_account.level() // interval] - roles.reverse() - roles = [role for role in roles if role] - - await self.call_event('on_level_up', message, member_account, roles) - - if roles: - await message.author.add_roles(*roles) - -
    [docs] async def create_account(self, member): - self._check_database() - - await self.database.insertifnotexists(self.tables["xp"], - dict( - zip( - self.tables_column_data[0], - [member.guild.id, member.id, 1, 0, 50] - ) - ), - self.generate_checks(member))
    - -
    [docs] async def get_account(self, member): - self._check_database() - - member_data = await self.database.select(self.tables["xp"], - [], - self.generate_checks(member), - True) - - if member_data: - return LevelingAccount(self, member) - - return None
    - -
    [docs] async def get_leaderboard(self, guild: discord.Guild): - self._check_database() - - guild_info = sorted( - await self.database.select(self.tables['xp'], [], {'guild': guild.id}, True), - key=lambda x: x["xp"], - reverse=True - ) - - members = [] - for member_info in guild_info: - member = guild.get_member(member_info['member']) - if member: - members.append(LevelingAccount(self, member)) - - return members
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/MessageFilter.html b/docs/_build/html/_modules/discordSuperUtils/MessageFilter.html deleted file mode 100644 index d4d45b7..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/MessageFilter.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - discordSuperUtils.MessageFilter — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.MessageFilter

    -from __future__ import annotations
    -
    -import asyncio
    -import re
    -from abc import ABC, abstractmethod
    -from datetime import timedelta
    -from typing import (
    -    TYPE_CHECKING,
    -    Union,
    -    Any,
    -    List
    -)
    -
    -from .Base import get_generator_response, EventManager
    -from .Punishments import get_relevant_punishment
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    import discord
    -    from .Punishments import Punishment
    -
    -__all__ = (
    -    "MessageFilter",
    -    "MessageResponseGenerator",
    -    "DefaultMessageResponseGenerator"
    -)
    -
    -
    -
    [docs]class MessageResponseGenerator(ABC): - """ - Represents a URL response generator that filters messages and checks if they contain URLs or anything - inappropriate. - """ - - __slots__ = () - -
    [docs] @abstractmethod - def generate(self, message: discord.Message) -> Union[bool, Any]: - """ - This function is an abstract method. - The generate function of the generator. - - :param message: The message to filter. - :type message: discord.Message - :return: A boolean representing if the message contains inappropriate content. - :rtype: Union[bool, Any] - """ - - pass
    - - -
    [docs]class DefaultMessageResponseGenerator(MessageResponseGenerator): - URL_RE = re.compile(r"(https?://(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][" - r"a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?://(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2," - r"}|www\.[a-zA-Z0-9]+\.[^\s]{2,})") - DISCORD_INVITE_RE = re.compile(r"(?:(?:http|https)://)?(?:www.)?(?:disco|discord|discordapp).(" - r"?:com|gg|io|li|me|net|org)(?:/(?:invite))?/([a-z0-9-.]+)") - -
    [docs] def generate(self, message: discord.Message) -> Union[bool, Any]: - if message.author.guild_permissions.administrator: - return False - - return self.URL_RE.match(message.content) or self.DISCORD_INVITE_RE.match(message.content)
    - - -
    [docs]class MessageFilter(EventManager): - """ - Represents a discordSuperUtils message filter that filters messages and finds inappropriate content. - """ - - __slots__ = ("bot", "generator", "_member_cache", "punishments", "wipe_cache_delay") - - def __init__(self, - bot: commands.Bot, - generator: MessageResponseGenerator = None, - delete_message: bool = True, - wipe_cache_delay: timedelta = timedelta(minutes=5)): - super().__init__() - self.bot = bot - self.generator = generator if generator is not None else DefaultMessageResponseGenerator - self.delete_message = delete_message - self.wipe_cache_delay = wipe_cache_delay - self._member_cache = {} - self.punishments = [] - - self.bot.loop.create_task(self.__wipe_cache()) - self.bot.add_listener(self.__handle_messages, 'on_message') - self.bot.add_listener(self.__handle_messages, 'on_message_edit') - - async def __wipe_cache(self): - """ - This function is responsible for wiping the member cache. - - :return: - """ - - while not self.bot.is_closed(): - await asyncio.sleep(self.wipe_cache_delay.total_seconds()) - - self._member_cache = {} - -
    [docs] def add_punishments(self, punishments: List[Punishment]) -> None: - self.punishments = punishments
    - - async def __handle_messages(self, message, edited_message=None): - """ - This function is the main logic of the MessageFilter, - Handled events: on_message, on_message_edit - - :param message: The on_message message passed by the event. - :type message: discord.Message - :param edited_message: The edited messages passed by the on_message_edit event, this function will use this - incase it is not None. - :type edited_message: discord.Message - :return: - """ - - message = edited_message or message - - if not message.guild or message.author.bot: - return - - if not get_generator_response(self.generator, MessageResponseGenerator, message): - return - - if self.delete_message: - await message.delete() - - member_warnings = self._member_cache.setdefault(message.guild.id, {}).get(message.author.id, 0) + 1 - self._member_cache[message.guild.id][message.author.id] = member_warnings - - await self.call_event("on_inappropriate_message", message, member_warnings) - - if punishment := get_relevant_punishment(self.punishments, member_warnings): - await punishment.punishment_manager.punish(message, message.author, punishment)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Music.html b/docs/_build/html/_modules/discordSuperUtils/Music.html deleted file mode 100644 index c03f339..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Music.html +++ /dev/null @@ -1,852 +0,0 @@ - - - - - - - - discordSuperUtils.Music — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Music

    -from __future__ import annotations
    -
    -import asyncio
    -import re
    -import time
    -from enum import Enum
    -from typing import (
    -    Optional,
    -    TYPE_CHECKING,
    -    Iterable,
    -    List,
    -    Union,
    -    Any
    -)
    -
    -import aiohttp
    -import discord
    -import youtube_dl
    -
    -from .Base import EventManager
    -from .Spotify import SpotifyClient
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -
    -
    -FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
    -SPOTIFY_RE = re.compile("^https://open.spotify.com/")
    -YTDL = youtube_dl.YoutubeDL({
    -    'format': 'bestaudio/best',
    -    'restrictfilenames': True,
    -    'noplaylist': False,
    -    'nocheckcertificate': True,
    -    'ignoreerrors': False,
    -    'logtostderr': False,
    -    'quiet': True,
    -    'no_warnings': True,
    -    'default_search': 'auto'
    -})
    -
    -
    -__all__ = (
    -    "NotPlaying",
    -    "NotConnected",
    -    "NotPaused",
    -    "QueueEmpty",
    -    "AlreadyConnected",
    -    "AlreadyPaused",
    -    "QueueError",
    -    "SkipError",
    -    "UserNotConnected",
    -    "InvalidSkipIndex",
    -    "Loops",
    -    "Player",
    -    "QueueManager",
    -    "MusicManager"
    -)
    -
    -
    -
    [docs]class NotPlaying(Exception): - """Raises error when client is not playing"""
    - - -
    [docs]class NotConnected(Exception): - """Raises error when client is not connected to a voice channel"""
    - - -
    [docs]class NotPaused(Exception): - """Raises error when player is not paused"""
    - - -
    [docs]class QueueEmpty(Exception): - """Raises error when queue is empty"""
    - - -
    [docs]class AlreadyConnected(Exception): - """Raises error when client is already connected to voice"""
    - - -
    [docs]class AlreadyPaused(Exception): - """Raises error when player is already paused."""
    - - -
    [docs]class QueueError(Exception): - """Raises error when something is wrong with the queue"""
    - - -
    [docs]class SkipError(Exception): - """Raises error when there is no song to skip to"""
    - - -
    [docs]class UserNotConnected(Exception): - """Raises error when user is not connected to channel"""
    - - -
    [docs]class InvalidSkipIndex(Exception): - """Raises error when the skip index is < 0"""
    - - -
    [docs]class Loops(Enum): - NO_LOOP = 0 - LOOP = 1 - QUEUE_LOOP = 2
    - - -
    [docs]class Player(discord.PCMVolumeTransformer): - """ - Represents a music player. - """ - - __slots__ = ("data", "title", "stream_url", "url", "start_timestamp", "last_pause_timestamp", "duration") - - def __init__(self, source, requester: discord.Member, *, data, volume=0.1): - super().__init__(source, volume) - - self.data = data - self.requester = requester - self.title = data.get('title') - self.stream_url = data.get('url') - self.url = data.get('webpage_url') - - self.start_timestamp = 0 - self.last_pause_timestamp = 0 - - self.duration = data.get('duration') if data.get('duration') != 0 else "LIVE" - - def __str__(self): - return self.title - -
    [docs] @staticmethod - async def make_multiple_players(songs: Iterable[str], requester: discord.Member) -> List[Player]: - """ - |coro| - - Returns a list of players from a iterable of queries. - - :param requester: The requester. - :type requester: discord.Member - :param songs: The queries. - :type songs: Iterable[str] - :return: The list of created players. - :rtype: List[Player] - """ - - tasks = [Player.make_player(song, requester, playlist=False) for song in songs] - return [x[0] for x in await asyncio.gather(*tasks) if x]
    - -
    [docs] @classmethod - async def make_player(cls, query: str, requester: discord.Member, playlist: bool = True) -> List[Player]: - """ - |coro| - - Returns a list of players from the query. - The list will contain the first video incase it is not a playlist. - - :param requester: The requester. - :type requester: discord.Member - :param query: The query. - :type query: str - :param playlist: A bool indicating if the function should fetch playlists or get the first video. - :type playlist: bool - :return: The list of created players. - :rtype: List[Player] - """ - - data = await MusicManager.fetch_data(query) - if data is None: - return [] - - if 'entries' in data: - if not playlist: - data = data['entries'][0] - else: - return [cls( - discord.FFmpegPCMAudio(player['url'], - **FFMPEG_OPTIONS), requester, data=player) for player in data['entries']] - - filename = data['url'] - return [cls(discord.FFmpegPCMAudio(filename, **FFMPEG_OPTIONS), requester, data=data)]
    - - -
    [docs]class QueueManager: - __slots__ = ("queue", "volume", "history", "loop", "now_playing") - - def __init__(self, volume: float, queue: List[Player]): - self.queue = queue - self.volume = volume - self.history = [] - self.loop = Loops.NO_LOOP - self.now_playing = None - -
    [docs] def add(self, player: Player) -> None: - """ - Adds a player to the queue. - - :param player: The player to add. - :type player: Player - :return: None - :rtype: None - """ - - self.queue.append(player)
    - -
    [docs] def clear(self) -> None: - """ - Clears the queue. - - :return: None - :rtype: None - """ - - self.queue.clear()
    - -
    [docs] def remove(self, index: int) -> Union[Player, Any]: - """ - Removes and element from the queue at the specified index, and returns the element's value. - - :param index: The index. - :type index: int - :return: The element's value - :rtype: Union[Player, Any] - """ - - return self.queue.pop(index)
    - - -
    [docs]class MusicManager(EventManager): - """ - Represents a MusicManager. - """ - - __slots__ = ("bot", "client_id", "client_secret", "spotify_support", "inactivity_timeout", "queue", "spotify") - - def __init__(self, bot: commands.Bot, spotify_support: bool = True, inactivity_timeout: int = 60, **kwargs): - super().__init__() - self.bot = bot - - self.client_id = kwargs.get('client_id') - self.client_secret = kwargs.get('client_secret') - self.spotify_support = spotify_support - self.inactivity_timeout = inactivity_timeout - - self.queue = {} - - if spotify_support: - self.spotify = SpotifyClient(client_id=self.client_id, client_secret=self.client_secret) - -
    [docs] async def ensure_activity(self, ctx: commands.Context) -> None: - """ - |coro| - - Waits the inactivity timeout and ensures the voice client in ctx is playing a song. - If no song is playing, it disconnects and calls the on_inactivity_timeout event. - - :param ctx: The context. - :type ctx: commands.Context - :return: None - :rtype: None - """ - - if self.inactivity_timeout is None: - return - - await asyncio.sleep(self.inactivity_timeout) - - if not ctx.voice_client: - return - - if ctx.voice_client.is_connected() and not ctx.voice_client.is_playing(): - await ctx.voice_client.disconnect() - await self.call_event("on_inactivity_disconnect", ctx)
    - - async def __check_connection(self, - ctx: commands.Context, - check_playing: bool = False, - check_queue: bool = False) -> Optional[bool]: - """ - |coro| - - Checks the connection state of the voice client in ctx. - - :param ctx: The context. - :type ctx: commands.Context - :param check_playing: A bool indicating if the function should check if a song is playing. - :type check_playing: bool - :param check_queue: A bool indicating if the function should check if a queue exists. - :type check_queue: bool - :return: True if all the checks passed. - :rtype: bool - """ - - if not ctx.voice_client or not ctx.voice_client.is_connected(): - await self.call_event('on_music_error', ctx, NotConnected("Client is not connected to a voice channel")) - return - - if check_playing and not ctx.voice_client.is_playing(): - await self.call_event('on_music_error', ctx, NotPlaying("Client is not playing anything currently")) - return - - if check_queue and ctx.guild.id not in self.queue: - await self.call_event('on_music_error', ctx, QueueEmpty("Queue is empty")) - return - - return True - - async def __check_queue(self, ctx: commands.Context) -> None: - """ - |coro| - - Plays the next song in the queue, handles looping and queue looping. - - :param ctx: The context of the voice client. - :type ctx: commands.Context - :return: None - :rtype: None - """ - - try: - if not ctx.voice_client or not ctx.voice_client.is_connected(): - return - - if self.queue[ctx.guild.id].loop == Loops.LOOP: - song = self.queue[ctx.guild.id].now_playing - player = (await Player.make_player(song.url, song.requester, playlist=False))[0] - - elif self.queue[ctx.guild.id].loop == Loops.QUEUE_LOOP: - song = self.queue[ctx.guild.id].remove(0) - player = (await Player.make_player(song.url, song.requester, playlist=False))[0] - self.queue[ctx.guild.id].add(player) - - else: - player = self.queue[ctx.guild.id].remove(0) - - self.queue[ctx.guild.id].now_playing = player - - if player is None or not ctx.voice_client: - return - - player.volume = self.queue[ctx.guild.id].volume - ctx.voice_client.play(player, after=lambda x: self.bot.loop.create_task(self.__check_queue(ctx))) - player.start_timestamp = time.time() - - if self.queue[ctx.guild.id].loop == Loops.NO_LOOP: - self.queue[ctx.guild.id].history.append(player) - await self.call_event('on_play', ctx, player) - - except (IndexError, KeyError): - await self.call_event("on_queue_end", ctx) - -
    [docs] async def get_player_played_duration(self, ctx: commands.Context, player: Player) -> Optional[float]: - """ - |coro| - - Returns the played duration of a player. - - :param ctx: The context. - :type ctx: commands.Context - :param player: The player. - :type player: Player - :return: The played duration of the player in seconds. - :rtype: Optional[float] - """ - - if not await self.__check_connection(ctx): - return - - start_timestamp = player.start_timestamp - if ctx.voice_client.is_paused(): - start_timestamp = player.start_timestamp + time.time() - player.last_pause_timestamp - - time_played = time.time() - start_timestamp - return min(time_played, time_played if player.duration == "LIVE" else player.duration)
    - -
    [docs] @staticmethod - async def fetch_data(query: str) -> Optional[dict]: - """ - |coro| - - Fetches the YTDL data of the query. - - :param query: The query. - :type query: str - :return: The YTDL data. - :rtype: Optional[dict] - """ - - try: - loop = asyncio.get_event_loop() - return await loop.run_in_executor(None, lambda: YTDL.extract_info(query, download=False)) - except youtube_dl.utils.DownloadError: - return None
    - -
    [docs] async def create_player(self, query: str, requester: discord.Member) -> List[Player]: - """ - |coro| - - Creates a list of players from the query. - This function supports Spotify and all YTDL supported links. - - :param requester: The requester. - :type requester: discord.Member - :param query: The query. - :type query: str - :return: The list of players. - :rtype: List[Player] - """ - - if SPOTIFY_RE.match(query) and self.spotify_support: - return await Player.make_multiple_players( - [song for song in await self.spotify.get_songs(query)], - requester - ) - - return await Player.make_player(query, requester)
    - -
    [docs] async def queue_add(self, players: List[Player], ctx: commands.Context) -> Optional[bool]: - """ - |coro| - - Adds a list of players to the ctx queue. - If a queue does not exist in ctx, it creates one. - - :param players: The list of players. - :type players: List[Player] - :param ctx: The context. - :type ctx: commands.Context - :return: A bool indicating if it was successful - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx): - return - - if ctx.guild.id in self.queue: - self.queue[ctx.guild.id].queue += players - else: - self.queue[ctx.guild.id] = QueueManager(0.1, players) - - return True
    - -
    [docs] async def queue_remove(self, ctx: commands.Context, index: int) -> None: - """ - |coro| - - Removes a player from the queue in ctx at the specified index. - Calls on_music_error with QueueError if index is invalid. - - :param ctx: The context. - :type ctx: commands.Context - :param index: The index. - :type index: int - :return: None - :rtype: None - """ - - if not await self.__check_connection(ctx, check_queue=True): - return - - try: - self.queue[ctx.guild.id].remove(index) - except IndexError: - await self.call_event('on_music_error', ctx, QueueError("Failure when removing player from queue"))
    - -
    [docs] async def lyrics(self, ctx: commands.Context, query: str = None) -> Optional[str]: - """ - |coro| - - Returns the lyrics from the query or the currently playing song. - - :param ctx: The context. - :type ctx: commands.Context - :param query: The query. - :type query: str - :return: The lyrics. - :rtype: Optional[str] - """ - - query = await self.now_playing(ctx) if query is None else query - url = f"https://some-random-api.ml/lyrics?title={query}" - - async with aiohttp.ClientSession() as session: - request = await session.get(url) - request_json = await request.json() - - return request_json.get('lyrics', None)
    - -
    [docs] async def play(self, ctx: commands.Context, player: Player = None) -> Optional[bool]: - """ - |coro| - - Plays the player or the next song in the queue if the player is not passed. - - :param ctx: The context. - :type ctx: commands.Context - :param player: The player. - :type player: Player - :return: A bool indicating if the play was successful - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx): - return - - if player is not None: - ctx.voice_client.play(player) - return True - - if not ctx.voice_client.is_playing(): - await self.__check_queue(ctx) - return True
    - -
    [docs] async def pause(self, ctx: commands.Context) -> Optional[bool]: - """ - |coro| - - Pauses the currently playing song in ctx. - Calls on_music_error with AlreadyPaused if already paused. - - :param ctx: The context. - :type ctx: commands.Context - :return: A bool indicating if the pause was successful - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx): - return - - if ctx.voice_client.is_paused(): - await self.call_event('on_music_error', ctx, AlreadyPaused("Player is already paused.")) - return - - (await self.now_playing(ctx)).last_pause_timestamp = time.time() - ctx.voice_client.pause() - self.bot.loop.create_task(self.ensure_activity(ctx)) - return True
    - -
    [docs] async def resume(self, ctx: commands.Context) -> Optional[bool]: - """ - |coro| - - Resumes the currently paused song in ctx. - Calls on_music_error with NotPaused if not paused. - - :param ctx: The context. - :type ctx: commands.Context - :return: A bool indicating if the resume was successful - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx): - return - - if not ctx.voice_client.is_paused(): - await self.call_event('on_music_error', ctx, NotPaused("Player is not paused")) - return - - ctx.voice_client.resume() - - now_playing = await self.now_playing(ctx) - now_playing.start_timestamp += time.time() - now_playing.last_pause_timestamp - - return True
    - -
    [docs] async def skip(self, ctx: commands.Context, index: int = None) -> Optional[Player]: - """ - |coro| - - Skips to the index in ctx. - Calls on_music_error with InvalidSkipIndex or SkipError. - - :param index: The index to skip to. - :type index: int - :param ctx: The context. - :type ctx: commands.Context - :return: A bool indicating if the skip was successful - :rtype: Optional[Player] - """ - - if not await self.__check_connection(ctx, True, check_queue=True): - return - - # Created duplicate to make sure InvalidSkipIndex isn't raised when the user does pass an index and the queue - # is empty. - skip_index = 0 if index is None else index - 1 - if not -1 < skip_index < len(self.queue[ctx.guild.id].queue): - if index: - await self.call_event('on_music_error', ctx, InvalidSkipIndex("Skip index invalid.")) - return - - if len(self.queue[ctx.guild.id].queue) <= skip_index: - await self.call_event('on_music_error', ctx, SkipError("No song to skip to.")) - return - - if skip_index > 0: - removed_songs = self.queue[ctx.guild.id].queue[:skip_index] - - self.queue[ctx.guild.id].queue = self.queue[ctx.guild.id].queue[skip_index:] - if self.queue[ctx.guild.id].loop == Loops.QUEUE_LOOP: - self.queue[ctx.guild.id].queue += removed_songs - - player = self.queue[ctx.guild.id].queue[0] - ctx.voice_client.stop() - return player
    - -
    [docs] async def volume(self, ctx: commands.Context, volume: int = None) -> Optional[float]: - """ - |coro| - - Sets the volume in ctx. - Returns the current volume if volume is None. - - :param volume: The volume to set. - :type volume: int - :param ctx: The context. - :type ctx: commands.Context - :return: The new volume. - :rtype: Optional[float] - """ - - if not await self.__check_connection(ctx, True, check_queue=True): - return - - if volume is None: - return ctx.voice_client.source.volume * 100 - - ctx.voice_client.source.volume = volume / 100 - self.queue[ctx.guild.id].volume = volume / 100 - return ctx.voice_client.source.volume * 100
    - -
    [docs] async def join(self, ctx: commands.Context) -> Optional[discord.VoiceChannel]: - """ - |coro| - - Joins the ctx voice channel. - Calls on_music_error with AlreadyConnected or UserNotConnected. - - :param ctx: The context. - :type ctx: commands.Context - :return: The voice channel it joined. - :rtype: Optional[discord.VoiceChannel] - """ - - if ctx.voice_client and ctx.voice_client.is_connected(): - await self.call_event('on_music_error', - ctx, - AlreadyConnected("Client is already connected to a voice channel")) - return - - if not ctx.author.voice: - await self.call_event('on_music_error', - ctx, - UserNotConnected("User is not connected to a voice channel")) - return - - channel = ctx.author.voice.channel - await channel.connect() - return channel
    - -
    [docs] async def leave(self, ctx: commands.Context) -> Optional[discord.VoiceChannel]: - """ - |coro| - - Leaves the voice channel in ctx. - - :param ctx: The context. - :type ctx: commands.Context - :return: The voice channel it left. - :rtype: Optional[discord.VoiceChannel] - """ - - if not await self.__check_connection(ctx): - return - - channel = ctx.voice_client.channel - await ctx.voice_client.disconnect() - return channel
    - -
    [docs] async def now_playing(self, ctx: commands.Context) -> Optional[Player]: - """ - |coro| - - Returns the currently playing player. - - :param ctx: The context. - :type ctx: commands.Context - :return: The currently playing player. - :rtype: Optional[Player] - """ - - if not await self.__check_connection(ctx, check_queue=True): - return - - now_playing = self.queue[ctx.guild.id].now_playing - if not ctx.voice_client.is_playing() and not ctx.voice_client.is_paused(): - await self.call_event('on_music_error', ctx, NotPlaying("Client is not playing anything currently")) - - return now_playing
    - -
    [docs] async def queueloop(self, ctx: commands.Context) -> Optional[bool]: - """ - |coro| - - Toggles the queue loop. - - :param ctx: The context - :type ctx: commands.Context - :return: A bool indicating if the queue loop is now enabled or disabled. - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx, check_playing=True, check_queue=True): - return - - self.queue[ctx.guild.id].loop = Loops.QUEUE_LOOP if self.queue[ctx.guild.id].loop != Loops.QUEUE_LOOP else \ - Loops.NO_LOOP - - if self.queue[ctx.guild.id].loop == Loops.QUEUE_LOOP: - self.queue[ctx.guild.id].add(self.queue[ctx.guild.id].now_playing) - - return self.queue[ctx.guild.id].loop == Loops.QUEUE_LOOP
    - -
    [docs] async def loop(self, ctx: commands.Context) -> Optional[bool]: - """ - |coro| - - Toggles the loop. - - :param ctx: The context - :type ctx: commands.Context - :return: A bool indicating if the loop is now enabled or disabled. - :rtype: Optional[bool] - """ - - if not await self.__check_connection(ctx, check_playing=True, check_queue=True): - return - - self.queue[ctx.guild.id].loop = Loops.LOOP if self.queue[ctx.guild.id].loop != Loops.LOOP else Loops.NO_LOOP - return self.queue[ctx.guild.id].loop == Loops.LOOP
    - -
    [docs] async def get_queue(self, ctx: commands.Context) -> Optional[QueueManager]: - """ - |coro| - - Returns the queue of ctx. - - :param ctx: The context. - :type ctx: commands.Context - :return: The queue. - :rtype: Optional[QueueManager] - """ - - if not await self.__check_connection(ctx, check_queue=True): - return - - return self.queue[ctx.guild.id]
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Mute.html b/docs/_build/html/_modules/discordSuperUtils/Mute.html deleted file mode 100644 index 897f3d5..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Mute.html +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - - - discordSuperUtils.Mute — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Mute

    -from __future__ import annotations
    -
    -import asyncio
    -from datetime import datetime
    -from typing import (
    -    TYPE_CHECKING,
    -    Union,
    -    Optional,
    -    List,
    -    Any,
    -    Dict
    -)
    -
    -import discord
    -import discord.utils
    -
    -from .Base import DatabaseChecker
    -from .Punishments import Punisher
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    from .Punishments import Punishment
    -
    -
    -__all__ = ("AlreadyMuted", "MuteManager")
    -
    -
    -
    [docs]class AlreadyMuted(Exception): - """Raises an error when a user is already muted."""
    - - -
    [docs]class MuteManager(DatabaseChecker, Punisher): - """ - A MuteManager that handles mutes for guilds. - """ - - __slots__ = ("bot",) - - def __init__(self, bot: commands.Bot): - super().__init__([ - { - 'guild': 'snowflake', - 'member': 'snowflake', - 'timestamp_of_mute': 'snowflake', - 'timestamp_of_unmute': 'snowflake', - 'reason': 'string' - } - ], ['mutes']) - self.bot = bot - - self.add_event(self.on_database_connect) - -
    [docs] async def on_database_connect(self): - self.bot.loop.create_task(self.__check_mutes()) - self.bot.add_listener(self.on_member_join)
    - -
    [docs] async def get_muted_members(self) -> List[Dict[str, Any]]: - """ - |coro| - - This function returns all the members that are supposed to be unmuted but are muted. - - :return: The unmuted members. - :rtype: List[Dict[str, Any]] - """ - - return [x for x in await self.database.select(self.tables['mutes'], [], fetchall=True) - if x["timestamp_of_unmute"] <= datetime.utcnow().timestamp()]
    - -
    [docs] async def on_member_join(self, member: discord.Member) -> None: - """ - |coro| - - The on_member_join event callback. - Used so the member cant leave the guild, join back and be unmuted. - - :param member: The member that joined. - :type member: discord.Member - :return: None - :rtype: None - """ - - muted_members = [x for x in await self.database.select(self.tables['mutes'], ["timestamp_of_unmute", "member"], - { - 'guild': member.guild.id, - 'member': member.id - }, fetchall=True) if - x["timestamp_of_unmute"] > datetime.utcnow().timestamp()] - - if any([muted_member["member"] == member.id for muted_member in muted_members]): - muted_role = discord.utils.get(member.guild.roles, name="Muted") - - if muted_role: - await member.add_roles(muted_role)
    - - async def __check_mutes(self) -> None: - """ - |coro| - - A loop that makes sure the members are unmuted when they are supposed to. - - :return: None - :rtype: None - """ - - await self.bot.wait_until_ready() - - while not self.bot.is_closed(): - for muted_member in await self.get_muted_members(): - guild = self.bot.get_guild(muted_member['guild']) - - if guild is None: - continue - - member = guild.get_member(muted_member['member']) - - if await self.unmute(member): - await self.call_event('on_unmute', member, muted_member["reason"]) - - await asyncio.sleep(300) - -
    [docs] async def punish(self, ctx: commands.Context, member: discord.Member, punishment: Punishment) -> None: - try: - await self.mute(member) - except discord.errors.Forbidden as e: - raise e - else: - await self.call_event("on_punishment", ctx, member, punishment)
    - -
    [docs] @staticmethod - async def ensure_permissions(guild: discord.Guild, muted_role: discord.Role) -> None: - """ - |coro| - - This function loops through the guild's channels and ensures the muted_role is not allowed to - send messages or speak in that channel. - - :param guild: The guild to get the channels from. - :type guild: discord.Guild - :param muted_role: The muted role. - :type muted_role: discord.Role - :return: None - """ - - channels_to_mute = [channel for channel in guild.channels - if channel.overwrites_for(muted_role).send_messages is not False] - # Now, you might say what the heck, why don't you test if the value is True instead of checking if it - # is not False? I am doing it this way because permissions have 3 values, - # None, True and False. - # Now, lets say we have a permission that is set to None, if i test it for a False value, (if not value) it will - # return False which is incorrect and it should return True. - - await asyncio.gather(*[ - channel.set_permissions(muted_role, send_messages=False, speak=False) - for channel in channels_to_mute - ])
    - - async def __handle_unmute(self, time_of_mute: Union[int, float], member: discord.Member, reason: str) -> None: - """ - |coro| - - A function that handles the member's unmute that runs separately from mute so it wont be blocked. - - :param time_of_mute: The time until the member's unmute timestamp. - :type time_of_mute: Union[int, float] - :param member: The member to unmute. - :type member: discord.Member - :param reason: The reason of the mute. - :type reason: str - :return: None - """ - - await asyncio.sleep(time_of_mute) - - if await self.unmute(member): - await self.call_event('on_unmute', member, reason) - -
    [docs] async def mute(self, - member: discord.Member, - reason: str = "No reason provided.", - time_of_mute: Union[int, float] = 0) -> None: - """ - |coro| - - Mutes a member. - - :raises: AlreadyMuted: The member is already muted. - :param member: The member to mute. - :type member: discord.Member - :param reason: The reason of the mute. - :type reason: str - :param time_of_mute: The time of mute. - :type time_of_mute: Union[int, float] - :return: None, - :rtype: None - """ - - self._check_database() - - muted_role = discord.utils.get(member.guild.roles, name="Muted") - if not muted_role: - muted_role = await member.guild.create_role(name="Muted", - permissions=discord.Permissions(send_messages=False, - speak=False)) - - if muted_role in member.roles: - raise AlreadyMuted(f"{member} is already muted.") - - await member.add_roles(muted_role, reason=reason) - - self.bot.loop.create_task(self.ensure_permissions(member.guild, muted_role)) - - if time_of_mute <= 0: - return - - await self.database.insert(self.tables['mutes'], { - 'guild': member.guild.id, - 'member': member.id, - 'timestamp_of_mute': datetime.utcnow().timestamp(), - 'timestamp_of_unmute': datetime.utcnow().timestamp() + time_of_mute, - 'reason': reason - }) - - self.bot.loop.create_task(self.__handle_unmute(time_of_mute, member, reason))
    - -
    [docs] async def unmute(self, member: discord.Member) -> Optional[bool]: - """ - |coro| - - Unmutes a member. - - :param member: The member to unmute. - :type member: discord.Member - :rtype: Optional[bool] - :return: A bool indicating if the unmute was successful - """ - - await self.database.delete(self.tables['mutes'], {'guild': member.guild.id, 'member': member.id}) - muted_role = discord.utils.get(member.guild.roles, name="Muted") - if not muted_role: - return - - if muted_role not in member.roles: - return - - await member.remove_roles(muted_role) - return True
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Paginator.html b/docs/_build/html/_modules/discordSuperUtils/Paginator.html deleted file mode 100644 index eedb55b..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Paginator.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - discordSuperUtils.Paginator — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Paginator

    -import asyncio
    -from math import ceil
    -
    -import discord
    -
    -__all__ = ("generate_embeds", "EmojiError", "PageManager", "ButtonsPageManager")
    -
    -
    -
    [docs]def generate_embeds(list_to_generate, title, description, fields=25, color=0xff0000, string_format='{}'): - num_of_embeds = ceil((len(list_to_generate) + 1) / fields) - - embeds = [ - discord.Embed( - title=f"{title} (Page 1/{num_of_embeds})", - description=description, - color=color - ) - ] - - for i in range(2, num_of_embeds + 1): - embeds.append(discord.Embed( - title=f"{title} (Page {i}/{num_of_embeds})", - color=color - ) - ) - - embed_index = 0 - for index, element in enumerate(list_to_generate): - embeds[embed_index].add_field(name=f"**{index + 1}.**", - value=string_format.format(element), - inline=False) - - if (index + 1) % fields == 0: - embed_index += 1 - - return embeds
    - - -class ButtonError(Exception): - __slots__ = () - - -
    [docs]class EmojiError(Exception): - __slots__ = ()
    - - -
    [docs]class ButtonsPageManager: - __slots__ = ("ctx", "messages", "timeout", "buttons", "public", "index", "button_color") - - def __init__(self, ctx, messages, timeout=60, buttons=None, public=False, index=0, button_color=None): - self.ctx = ctx - self.messages = messages - self.timeout = timeout - self.buttons = buttons if buttons is not None else ["⏪", "◀️", "▶️", "⏩"] - self.public = public - self.index = index - self.button_color = button_color - -
    [docs] async def run(self): - if len(self.buttons) != 4: - raise ButtonError(f"Passed {len(self.buttons)} buttons when 4 are needed.") - - self.index = 0 if not -1 < self.index < len(self.messages) else self.index - - from discord_components import ActionRow, Button, ButtonStyle, DiscordComponents - DiscordComponents(self.ctx.bot) - - components = ActionRow( - [ - Button( - style=self.button_color or ButtonStyle.red, - label=button, - custom_id=button - ) - for button in self.buttons - ] - ) - - message_to_send = self.messages[self.index] - # message_to_send must be of type embed, sadly, discord_components breaks the Messageable.send method - # And breaks the file parameter, too - message = await self.ctx.send(embed=message_to_send, components=components) - - while True: - try: - interaction = await self.ctx.bot.wait_for('button_click', - check=lambda i: i.message == message, - timeout=30) - - if interaction.user.bot: - continue - - if interaction.user != self.ctx.author and not self.public: - continue - - except asyncio.TimeoutError: - break - - if interaction.custom_id == self.buttons[0]: - self.index = 0 - - elif interaction.custom_id == self.buttons[1]: - if self.index > 0: - self.index -= 1 - - elif interaction.custom_id == self.buttons[2]: - if self.index < len(self.messages) - 1: - self.index += 1 - - elif interaction.custom_id == self.buttons[3]: - self.index = len(self.messages) - 1 - - message_to_send = self.messages[self.index] - - for button in components[0]: - button.disabled = False - - if self.index == len(self.messages) - 1: - components[0][2].disabled = True - components[0][3].disabled = True - - if self.index == 0: - components[0][0].disabled = True - components[0][1].disabled = True - - await interaction.respond( - type=7, # Cannot find a proper enum :) - content="", - embed=message_to_send, - components=components - )
    - - -
    [docs]class PageManager: - __slots__ = ("ctx", "messages", "timeout", "emojis", "public", "index") - - def __init__(self, ctx, messages, timeout=60, emojis=None, public=False, index=0): - self.ctx = ctx - self.messages = messages - self.timeout = timeout - self.emojis = emojis if emojis is not None else ["⏪", "◀️", "▶️", "⏩"] - self.public = public - self.index = index - -
    [docs] async def run(self): - if len(self.emojis) != 4: - raise EmojiError(f"Passed {len(self.emojis)} emojis when 4 are needed.") - - self.index = 0 if not -1 < self.index < len(self.messages) else self.index - - message_to_send = self.messages[self.index] - if isinstance(message_to_send, discord.Embed): - message = await self.ctx.send(embed=message_to_send) - else: - message = await self.ctx.send(message_to_send) - - for emoji in self.emojis: - await message.add_reaction(emoji) - - while True: - try: - reaction, user = await self.ctx.bot.wait_for('reaction_add', - check=lambda x, y: x.message == message, - timeout=self.timeout) - - if user.bot: - continue - - if user != self.ctx.author and not self.public: - continue - - except asyncio.TimeoutError: - break - - if reaction.emoji == self.emojis[0]: - self.index = 0 - - elif reaction.emoji == self.emojis[1]: - if self.index > 0: - self.index -= 1 - - elif reaction.emoji == self.emojis[2]: - if self.index < len(self.messages) - 1: - self.index += 1 - - elif reaction.emoji == self.emojis[3]: - self.index = len(self.messages) - 1 - - await message.remove_reaction(reaction.emoji, user) - message_to_send = self.messages[self.index] - - if isinstance(message_to_send, discord.Embed): - await message.edit(embed=message_to_send, content=None) - else: - await message.edit(content=message_to_send, embed=None)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Prefix.html b/docs/_build/html/_modules/discordSuperUtils/Prefix.html deleted file mode 100644 index f97fb88..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Prefix.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - discordSuperUtils.Prefix — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Prefix

    -from typing import (
    -    Union,
    -    Any
    -)
    -
    -import discord
    -from discord.ext import commands
    -
    -from .Base import DatabaseChecker
    -
    -
    -
    [docs]class PrefixManager(DatabaseChecker): - def __init__(self, bot: commands.Bot, default_prefix: str, mentioned: bool = False): - super().__init__([{'guild': 'snowflake', 'prefix': 'string'}], ['prefixes']) - self.default_prefix = default_prefix - self.bot = bot - self.mentioned = mentioned - - self.prefix_cache = {} - bot.command_prefix = self.__get_prefix - -
    [docs] async def get_prefix(self, guild: Union[discord.Guild, Any]) -> str: - if guild.id in self.prefix_cache: - return self.prefix_cache[guild.id] - - prefix = await self.database.select(self.tables['prefixes'], ['prefix'], {'guild': guild.id}) - prefix = prefix["prefix"] if prefix else self.default_prefix - - self.prefix_cache[guild.id] = prefix - - return prefix
    - -
    [docs] async def set_prefix(self, guild: discord.Guild, prefix: str) -> None: - self.prefix_cache[guild.id] = prefix - await self.database.updateorinsert(self.tables['prefixes'], - {'prefix': prefix}, - {'guild': guild.id}, - {'guild': guild.id, 'prefix': prefix})
    - - async def __get_prefix(self, bot, message: discord.Message) -> str: - self._check_database() - - if not message.guild: - return commands.when_mentioned_or(self.default_prefix)(bot, - message) if self.mentioned else self.default_prefix - - prefix = await self.get_prefix(message.guild) - - return commands.when_mentioned_or(prefix)(bot, message) if self.mentioned else prefix
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Punishments.html b/docs/_build/html/_modules/discordSuperUtils/Punishments.html deleted file mode 100644 index 6d049c3..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Punishments.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - - discordSuperUtils.Punishments — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Punishments

    -from __future__ import annotations
    -
    -from abc import ABC, abstractmethod
    -from datetime import timedelta
    -from typing import (
    -    Optional,
    -    List,
    -    TYPE_CHECKING
    -)
    -
    -if TYPE_CHECKING:
    -    import discord
    -    from discord.ext import commands
    -
    -
    -
    [docs]class Punishment: - """ - A punishment class that is used for punishing members. - """ - - def __init__(self, - punishment_manager, - punish_after: int = 3, - punishment_reason: str = "No reason specified.", - punishment_time: timedelta = timedelta(days=1)): - self.punishment_manager = punishment_manager - self.punish_after = punish_after - self.punishment_reason = punishment_reason - self.punishment_time = punishment_time - - if not issubclass(type(punishment_manager), Punisher): - raise TypeError(f"Manager of type '{type(punishment_manager)} is not supported.'")
    - - -
    [docs]def get_relevant_punishment(punishments: List[Punishment], punish_count: int) -> Optional[Punishment]: - """ - Returns the punishment that is suitable for the punish count. - - :param punishments: The punishments to pick from. - :type punishments: List[Punishment] - :param punish_count: The punishment count. - :type punish_count: int - :rtype: Optional[Punishment] - :return: The suitable punishment. - """ - - return {x.punish_after: x for x in punishments}.get(punish_count)
    - - -
    [docs]class Punisher(ABC): -
    [docs] @abstractmethod - async def punish(self, ctx: commands.Context, member: discord.Member, punishment: Punishment) -> None: - """ - The manager's punish function. - - :param ctx: The context of the punishments. - :type ctx: commands.Context - :param member: The member to punish. - :type member: discord.Member - :param punishment: The punishment to punish the member with. - :type punishment: Punishment - :rtype: None - :return: None - """
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/ReactionRoles.html b/docs/_build/html/_modules/discordSuperUtils/ReactionRoles.html deleted file mode 100644 index b9efe29..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/ReactionRoles.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - discordSuperUtils.ReactionRoles — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.ReactionRoles

    -from .Base import DatabaseChecker
    -from .Paginator import EmojiError
    -
    -
    -
    [docs]class ReactionManager(DatabaseChecker): - def __init__(self, bot): - super().__init__([ - { - 'guild': 'snowflake', - 'message': 'snowflake', - 'role': 'snowflake', - 'emoji': 'string', - 'remove_on_reaction': 'smallnumber' - } - ], ['reaction_roles']) - - self.bot = bot - self.add_event(self.on_database_connect) - -
    [docs] async def on_database_connect(self): - self.bot.add_listener(self.__handle_reactions, "on_raw_reaction_add") - self.bot.add_listener(self.__handle_reactions, "on_raw_reaction_remove")
    - -
    [docs] @staticmethod - def get_emoji_sql(emoji): - if not emoji.is_custom_emoji(): - return str(emoji) - - emoji_string = f"<:{emoji.name}:{emoji.id}>" - if emoji.animated: - emoji_string = emoji_string[:1] + 'a' + emoji_string[1:] - - return emoji_string
    - - async def __handle_reactions(self, payload): - self._check_database() - - if payload.user_id == self.bot.user.id: - return - - database_checks = {'guild': payload.guild_id, - 'message': payload.message_id, - 'emoji': self.get_emoji_sql(payload.emoji)} - - reaction_role_data = await self.database.select(self.tables['reaction_roles'], self.tables_column_data[0], - database_checks) - - if not reaction_role_data: - return - - guild = self.bot.get_guild(payload.guild_id) - role = guild.get_role(reaction_role_data["role"]) - channel = guild.get_channel(payload.channel_id) - message = await channel.fetch_message(payload.message_id) - emoji = self.get_emoji_sql(payload.emoji) - - if emoji == reaction_role_data["emoji"]: - member = payload.member if payload.member else guild.get_member(payload.user_id) - - if role is None: - await self.call_event('on_reaction_event', guild, channel, message, member, emoji) - - else: - if role not in member.roles: - await member.add_roles(role) - elif reaction_role_data['remove_on_reaction'] == 1: - await member.remove_roles(role) - -
    [docs] async def create_reaction(self, guild, message, role, emoji, remove_on_reaction: int): - self._check_database() - - await self.database.insertifnotexists(self.tables['reaction_roles'], - dict(zip(self.tables_column_data[0], [ - guild.id, - message.id, - role.id if role is not None else role, - emoji, - int(remove_on_reaction) - ])), {'guild': guild.id, 'message': message.id, 'emoji': emoji}) - - if len(emoji) > 1: - emoji = self.bot.get_emoji(emoji) - - try: - await message.add_reaction(emoji) - except Exception: - raise EmojiError("Cannot add reaction to message.")
    - -
    [docs] async def delete_reaction(self, guild, message, emoji): - await self.database.delete(self.tables['reaction_roles'], - {'guild': guild.id, 'message': message.id, 'emoji': emoji})
    - -
    [docs] async def get_reactions(self, guild=None): - return await self.database.select(self.tables['reaction_roles'], [], {'guild': guild.id} if guild else {}, True)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Spotify.html b/docs/_build/html/_modules/discordSuperUtils/Spotify.html deleted file mode 100644 index ac6d716..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Spotify.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - discordSuperUtils.Spotify — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Spotify

    -import asyncio
    -from typing import (
    -    Optional,
    -    List,
    -    Dict,
    -    Union
    -)
    -
    -import spotipy
    -from spotipy import SpotifyClientCredentials
    -
    -FIELD = "items.track.name,items.track.artists(name),total"
    -
    -
    -
    [docs]class SpotifyClient: - def __init__(self, client_id: str, client_secret: str, loop=None): - self.sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=client_id, - client_secret=client_secret)) - self.loop = asyncio.get_event_loop() if loop is None else loop - -
    [docs] @staticmethod - def get_type(url: str) -> Optional[str]: - """ - This function receives a url and returns the type of the URL. - Return examples: playlist, user, album, etc. - - :param url: - :return: - """ - url = url.replace("https://open.spotify.com/", "") - return url.split("/")[-2]
    - -
    [docs] @staticmethod - def make_title(song: Dict[str, dict]) -> str: - """ - This function receives a song and creates a title that can be used for youtube_dl searching. - Return example: Never Gonna Give You Up by Rick Astley - - :param song: - :return: - """ - artists = " ".join([artist['name'] for artist in song.get('artists')]) - return f"{song['name']} by {artists}"
    - -
    [docs] async def fetch_playlist_data(self, url: str, offset: int) -> Dict[str, Union[int, list]]: - """ - This function receives a URL and an offset and returns 100 tracks from that offset - Example: Offset: 50, the URL has 160 tracks, returns tracks from 50-150 (limit is 100). - - :param url: - :param offset: - :return: - """ - - return await self.loop.run_in_executor(None, lambda: self.sp.playlist_items(playlist_id=url, - fields=FIELD, - offset=offset))
    - -
    [docs] async def fetch_full_playlist(self, url: str) -> List[Dict[str, dict]]: - """ - This function receives a url and returns all the tracks in that URL. - - :param url: - :return: - """ - - initial_request = await self.fetch_playlist_data(url, 0) - total_tracks = initial_request.get('total') - - requests = list( - await asyncio.gather(*(self.fetch_playlist_data(url, offset) for offset in range(100, total_tracks, 100))) - ) - requests.insert(0, initial_request) - result_tracks = [] - - for request in requests: - result_tracks += request.get('items') - - return result_tracks
    - -
    [docs] async def get_songs(self, url: str) -> List[str]: - """ - This function receives a URL and returns all the tracks in that URL. - - :param url: - :return: - """ - - playlist_type = self.get_type(url) - songs = [] - - if playlist_type == "playlist": - return [self.make_title(song['track']) for song in await self.fetch_full_playlist(url)] - - if playlist_type == "track": - songs = [await self.loop.run_in_executor(None, lambda: self.sp.track(track_id=url))] - - if playlist_type == "album": - album = await self.loop.run_in_executor(None, lambda: self.sp.album_tracks(album_id=url, limit=50)) - songs = album.get("items") - - return [self.make_title(song) for song in songs]
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/discordSuperUtils/Template.html b/docs/_build/html/_modules/discordSuperUtils/Template.html deleted file mode 100644 index ff2f556..0000000 --- a/docs/_build/html/_modules/discordSuperUtils/Template.html +++ /dev/null @@ -1,663 +0,0 @@ - - - - - - - - discordSuperUtils.Template — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - -

    Source code for discordSuperUtils.Template

    -from __future__ import annotations
    -
    -import asyncio
    -import uuid
    -from abc import ABC, abstractmethod
    -from typing import (
    -    List,
    -    Dict,
    -    Any,
    -    TYPE_CHECKING,
    -    Optional,
    -    Union
    -)
    -
    -import discord
    -from discord.guild import VerificationLevel
    -
    -from .Base import DatabaseChecker
    -
    -if TYPE_CHECKING:
    -    from discord.ext import commands
    -    from .Database import Database
    -
    -__all__ = (
    -    "DictionaryConvertible",
    -    "TemplateInfo",
    -    "TemplateRole",
    -    "TemplateCategory",
    -    "TemplateTextChannel",
    -    "TemplateVoiceChannel",
    -    "PartialTemplate",
    -    "Template",
    -    "TemplateManager"
    -)
    -
    -
    -
    [docs]class DictionaryConvertible(ABC): - __slots__ = () - -
    [docs] @classmethod - @abstractmethod - def from_dict(cls, convert_from: Dict[str, Any]) -> DictionaryConvertible: - pass
    - - -
    [docs]class TemplateInfo(DictionaryConvertible): - __slots__ = ( - "template_id", - "guild", - "afk_timeout", - "mfa_level", - "verification_level", - "explict_content_filter", - "system_channel", - "afk_channel" - ) - - def __init__(self, - template_id: str, - guild: int, - afk_timeout: int, - mfa_level: int, - verification_level: VerificationLevel, - explict_content_filter: int, - system_channel: int, - afk_channel: int): - self.template_id = template_id - self.guild = guild - self.afk_timeout = afk_timeout - self.mfa_level = mfa_level - self.verification_level = verification_level - self.explict_content_filter = explict_content_filter - self.system_channel = system_channel - self.afk_channel = afk_channel - - def __repr__(self): - return f"<TemplateInfo id={self.template_id} guild={self.guild} mfa_level={self.mfa_level}>" - - def __str__(self): - return f"<TemplateInfo id={self.template_id} guild={self.guild}>" - -
    [docs] @classmethod - def from_dict(cls, convert_from: Dict[str, Any]) -> TemplateInfo: - convert_from["verification_level"] = VerificationLevel(convert_from["verification_level"] or 0) - convert_from["explict_content_filter"] = discord.ContentFilter(convert_from["explict_content_filter"] or 0) - - return cls(*list(convert_from.values()))
    - - -
    [docs]class TemplateCategory(DictionaryConvertible): - __slots__ = ("name", "position", "category_id", "overwrites") - - def __init__(self, name: str, position: int, category_id: int, overwrites: Dict[int, int]): - self.name = name - self.position = position - self.category_id = category_id - self.overwrites = overwrites - - def __repr__(self): - return f"<TemplateCategory id={self.category_id} name={self.name!r} position={self.position}>" - - def __str__(self): - return f"<TemplateCategory name={self.name!r}>" - -
    [docs] @classmethod - def from_dict(cls, convert_from: Dict[Any, Any]) -> TemplateCategory: - return cls(*list(convert_from.values())[1:])
    - - -
    [docs]class TemplateTextChannel(DictionaryConvertible): - __slots__ = ("name", "position", "category", "topic", "slowmode", "nsfw", "channel_id", "overwrites") - - def __init__(self, - name: str, - position: int, - category: int, - topic: str, - slowmode: int, - nsfw: bool, - channel_id: int, - overwrites: Dict[int, discord.PermissionOverwrite]): - self.name = name - self.position = position - self.category = category - self.topic = topic - self.slowmode = slowmode - self.nsfw = nsfw - self.channel_id = channel_id - self.overwrites = overwrites - - def __repr__(self): - return f"<TemplateTextChannel topic={self.topic} name={self.name!r} position={self.position}>" - - def __str__(self): - return f"<TemplateTextChannel name={self.name!r}>" - -
    [docs] @classmethod - def from_dict(cls, convert_from: Dict[str, Any]) -> TemplateTextChannel: - convert_from["nsfw"] = bool(convert_from["nsfw"]) - - return cls(*list(convert_from.values())[1:])
    - - -
    [docs]class TemplateVoiceChannel(DictionaryConvertible): - __slots__ = ("name", "position", "category", "bitrate", "user_limit", "channel_id", "overwrites") - - def __init__(self, - name: str, - position: int, - category: int, - bitrate: int, - user_limit: int, - channel_id: int, - overwrites: Dict[int, discord.PermissionOverwrite]): - self.name = name - self.position = position - self.category = category - self.bitrate = bitrate - self.user_limit = user_limit - self.channel_id = channel_id - self.overwrites = overwrites - - def __repr__(self): - return f"<TemplateVoiceChannel limit={self.user_limit} name={self.name!r} position={self.position}>" - - def __str__(self): - return f"<TemplateVoiceChannel name={self.name!r}>" - -
    [docs] @classmethod - def from_dict(cls, convert_from: Dict[str, Any]) -> TemplateVoiceChannel: - return cls(*list(convert_from.values())[1:])
    - - -
    [docs]class TemplateRole(DictionaryConvertible): - __slots__ = ("default_role", "name", "color", "hoist", "position", "mentionable", "role_id", "permissions") - - def __init__(self, - default_role: bool, - name: str, - color: int, - hoist: bool, - position: int, - mentionable: bool, - role_id: int, - permissions: discord.Permissions): - self.default_role = default_role - self.name = name - self.color = color - self.hoist = hoist - self.position = position - self.mentionable = mentionable - self.role_id = role_id - self.permissions = discord.Permissions(permissions) - -
    [docs] def get_raw(self): - return { - "name": self.name, - "hoist": self.hoist, - "mentionable": self.mentionable, - "color": self.color, - "permissions": self.permissions - }
    - - def __repr__(self): - return f"<TemplateRole hoist={self.hoist} name={self.name!r} position={self.position} color={self.color}>" - - def __str__(self): - return f"<TemplateRole name={self.name!r}>" - -
    [docs] @classmethod - def from_dict(cls, convert_from: Dict[str, Any]) -> TemplateRole: - convert_from["default_role"] = bool(convert_from["default_role"]) - convert_from["hoist"] = bool(convert_from["hoist"]) - convert_from["mentionable"] = bool(convert_from["mentionable"]) - - return cls(*list(convert_from.values())[1:])
    - - -
    [docs]class PartialTemplate: - __slots__ = ("info", "categories", "text_channels", "voice_channels", "roles") - - def __init__(self, - info: TemplateInfo, - categories: List[TemplateCategory], - text_channels: List[TemplateTextChannel], - voice_channels: List[TemplateVoiceChannel], - roles: List[TemplateRole]): - self.info = info - self.categories = categories - self.text_channels = text_channels - self.voice_channels = voice_channels - self.roles = roles
    - - -
    [docs]class Template: - __slots__ = ("database", "tables", "info", "categories", "text_channels", "voice_channels", "roles") - - def __init__(self, - database: Database, - tables: Dict[str, str], - info: TemplateInfo, - categories: List[TemplateCategory], - text_channels: List[TemplateTextChannel], - voice_channels: List[TemplateVoiceChannel], - roles: List[TemplateRole]): - self.database = database - self.tables = tables - self.info = info - self.categories = categories - self.text_channels = text_channels - self.voice_channels = voice_channels - self.roles = roles - - def __repr__(self): - return f"<Template template={self.info} categories={self.categories} text_channels={self.text_channels}>" - - def __str__(self): - return f"Template template={self.info}" - -
    [docs] async def delete(self) -> PartialTemplate: - checks = {"id": self.info.template_id} - - partial = PartialTemplate(self.info, self.categories, self.text_channels, self.voice_channels, self.roles) - await self.database.delete(self.tables["templates"], checks) - await self.database.delete(self.tables["categories"], checks) - await self.database.delete(self.tables["text_channels"], checks) - await self.database.delete(self.tables["voice_channels"], checks) - await self.database.delete(self.tables["roles"], checks) - await self.database.delete(self.tables["overwrites"], checks) - return partial
    - -
    [docs] @staticmethod - def format_overwrites(overwrites: Dict[int, discord.PermissionOverwrite], - roles: Dict[int, discord.Role]) -> Dict[discord.Role, discord.PermissionOverwrite]: - result_overwrites = {} - - for role in roles: - if role in overwrites: - result_overwrites[roles[role]] = overwrites[role] - - return result_overwrites
    - -
    [docs] async def apply_settings(self, - guild: discord.Guild, - reason: str, - channels: Dict[int, Union[discord.VoiceChannel, discord.TextChannel]]) -> None: - await guild.edit( - afk_timeout=self.info.afk_timeout, - verification_level=self.info.verification_level, - explicit_content_filter=self.info.explict_content_filter, - afk_channel=channels.get(self.info.afk_channel), - system_channel=channels.get(self.info.system_channel), - reason=reason - )
    - -
    [docs] async def apply_roles(self, guild: discord.Guild, reason: str) -> Dict[int, discord.Role]: - roles = await asyncio.gather( - *[ - guild.default_role.edit(permissions=role.permissions, reason=reason) - if role.default_role else - guild.create_role(**role.get_raw()) - for role in reversed(self.roles) - ] - ) - - return dict(zip((x.role_id for x in reversed(self.roles)), - (role if role else guild.default_role for role in roles)))
    - -
    [docs] async def apply_categories(self, guild: discord.Guild, reason: str) -> Dict[int, discord.CategoryChannel]: - categories = await asyncio.gather( - *[ - guild.create_category_channel(name=category.name, reason=reason) - for category in self.categories - ] - ) - - return dict(zip((category.category_id for category in self.categories), categories))
    - -
    [docs] async def apply_channels(self, - guild: discord.Guild, - reason: str, - categories: Dict[int, discord.CategoryChannel], - roles: Dict[int, discord.Role]) -> Dict[int, discord.TextChannel]: - text_channels = await asyncio.gather(*[guild.create_text_channel( - name=channel.name, - position=channel.position, - slowmode_delay=channel.slowmode, - topic=channel.topic, - nsfw=channel.nsfw, - category=categories.get(channel.category), - overwrites=self.format_overwrites(channel.overwrites, roles), - reason=reason) for channel in self.text_channels]) - - return dict(zip((channel.channel_id for channel in self.text_channels), text_channels))
    - -
    [docs] async def apply_voice_channels(self, - guild: discord.Guild, - reason: str, - categories: Dict[int, discord.CategoryChannel], - roles: Dict[int, discord.Role]) -> Dict[int, discord.VoiceChannel]: - voice_channels = await asyncio.gather(*[guild.create_voice_channel( - name=channel.name, - position=channel.position, - category=categories.get(channel.category), - bitrate=channel.bitrate, - user_limit=channel.user_limit, - overwrites=self.format_overwrites(channel.overwrites, roles), - reason=reason) for channel in self.voice_channels]) - - return dict(zip((channel.channel_id for channel in self.voice_channels), voice_channels))
    - -
    [docs] async def apply(self, guild: discord.Guild) -> None: - roles_to_delete = list(filter( - lambda r: not r.managed and guild.default_role != r and guild.me.top_role.position > r.position, guild.roles - )) - - reason = f"Applying template {self.info.template_id}" - - await asyncio.gather(*[role.delete(reason=reason) for role in roles_to_delete]) - await asyncio.gather(*[channel.delete(reason=reason) for channel in guild.channels]) - - roles = await self.apply_roles(guild, reason) - categories = await self.apply_categories(guild, reason) - text_channels = await self.apply_channels(guild, reason, categories, roles) - voice_channels = await self.apply_voice_channels(guild, reason, categories, roles) - await self.apply_settings(guild, reason, {**text_channels, **voice_channels})
    - -
    [docs] @staticmethod - def get_overwrite(overwrites: List[Dict[str, Any]], - overwrite_object: int) -> Dict[int, discord.PermissionOverwrite]: - result_overwrites = {} - - for overwrite in overwrites: - if overwrite["overwrite_object"] == overwrite_object: - result_overwrites[overwrite["overwrite_key"]] = discord.PermissionOverwrite.from_pair( - discord.Permissions(overwrite["overwrite_pair"]), - discord.Permissions(overwrite["overwrite_second_pair"]) - ) - - return result_overwrites
    - -
    [docs] @classmethod - async def get_template(cls, database: Database, tables: Dict[str, str], template_id: str) -> Optional[Template]: - checks = {'id': template_id} - - raw_info = await database.select(tables["templates"], [], checks) - if not raw_info: - return None - - raw_categories = await database.select(tables["categories"], [], checks, fetchall=True) - raw_text_channels = await database.select(tables["text_channels"], [], checks, fetchall=True) - raw_voice_channels = await database.select(tables["voice_channels"], [], checks, fetchall=True) - raw_roles = await database.select(tables["roles"], [], checks, fetchall=True) - overwrites = await database.select(tables["overwrites"], [], checks, fetchall=True) - - return cls( - database, - tables, - TemplateInfo.from_dict(raw_info), - [TemplateCategory.from_dict(dict(x, **{"overwrites": cls.get_overwrite(overwrites, x["category_id"])})) - for x in raw_categories], - [TemplateTextChannel.from_dict(dict(x, **{"overwrites": cls.get_overwrite(overwrites, x["channel_id"])})) - for x in raw_text_channels], - [TemplateVoiceChannel.from_dict(dict(x, **{"overwrites": cls.get_overwrite(overwrites, x["channel_id"])})) - for x in raw_voice_channels], - [TemplateRole.from_dict(x) for x in raw_roles] - )
    - - -
    [docs]class TemplateManager(DatabaseChecker): - __slots__ = ("bot",) - - def __init__(self, bot: commands.Bot): - super().__init__( - [ - { - 'id': "string", - 'guild': 'snowflake', - "afk_timeout": "number", - "mfa_level": "smallnumber", - "verification_level": "smallnumber", - "explict_content_filter": "smallnumber", - "system_channel": "snowflake", - "afk_channel": "snowflake" - }, - { - 'id': "string", - "name": "string", - "position": "number", - "category_id": "snowflake" - }, - { - 'id': "string", - "name": "string", - "position": "number", - "category": "snowflake", - "topic": "string", - "slowmode": "number", - "nsfw": "smallnumber", - "channel_id": "snowflake" - }, - { - 'id': "string", - "name": "string", - "position": "number", - "category": "snowflake", - "bitrate": "smallnumber", - "user_limit": "smallnumber", - "channel_id": "snowflake" - }, - { - "id": "string", - "default_role": "smallnumber", - "name": "string", - "color": "number", - "hoist": "smallnumber", - "position": "number", - "mentionable": "smallnumber", - "role_id": "snowflake", - "permissions": "number" - }, - { - "id": "string", - "overwrite_object": "snowflake", - "overwrite_key": "snowflake", - "overwrite_pair": "number", - "overwrite_second_pair": "number" - } - ], - ['templates', 'categories', 'text_channels', 'voice_channels', 'roles', 'overwrites'] - ) - self.bot = bot - -
    [docs] async def get_templates(self, guild: discord.Guild = None) -> List[Template]: - return [await Template.get_template(self.database, self.tables, template["id"]) - for template in await self.database.select(self.tables["templates"], - ['id'], - {'guild': guild.id} if guild else {}, - fetchall=True)]
    - -
    [docs] async def get_template(self, template_id: str) -> Optional[Template]: - return await Template.get_template(self.database, self.tables, template_id)
    - -
    [docs] async def write_overwrites(self, - template_id: str, - overwrites_object: int, - overwrites: discord.PermissionOverwrite) -> None: - for x, y in overwrites.items(): - pairs = [pair.value for pair in y.pair()] - - await self.database.insert(self.tables["overwrites"], - { - "id": template_id, - "overwrite_object": overwrites_object, - "overwrite_key": x.id, - "overwrite_pair": pairs[0], - "overwrite_second_pair": pairs[1] - })
    - -
    [docs] async def create_template(self, guild: discord.Guild) -> Template: - template_id = str(uuid.uuid4()) - - await self.database.insert(self.tables["templates"], - { - 'guild': guild.id, - 'id': template_id, - 'afk_timeout': guild.afk_timeout, - 'mfa_level': guild.mfa_level, - 'verification_level': guild.verification_level.value, - 'explict_content_filter': guild.explicit_content_filter.value, - "system_channel": guild.system_channel and guild.system_channel.id, - "afk_channel": guild.afk_channel and guild.afk_channel.id - }) - - for category in guild.categories: - await self.write_overwrites(template_id, category.id, category.overwrites) - await self.database.insert(self.tables["categories"], - { - "id": template_id, - "name": category.name, - "position": category.position, - "category_id": category.id - }) - - for channel in guild.text_channels: - await self.write_overwrites(template_id, channel.id, channel.overwrites) - await self.database.insert(self.tables["text_channels"], - { - "id": template_id, - "name": channel.name, - "position": channel.position, - "category": channel.category_id, - "topic": channel.topic, - "slowmode": channel.slowmode_delay, - "nsfw": int(channel.is_nsfw()), - "channel_id": channel.id - }) - - for voice_channel in guild.voice_channels: - await self.write_overwrites(template_id, voice_channel.id, voice_channel.overwrites) - await self.database.insert(self.tables["voice_channels"], - { - "id": template_id, - "name": voice_channel.name, - "position": voice_channel.position, - "category": voice_channel.category_id, - "bitrate": voice_channel.bitrate, - "user_limit": voice_channel.user_limit, - "channel_id": voice_channel.id - }) - - for role in guild.roles: - await self.database.insert(self.tables["roles"], - { - "id": template_id, - "default_role": int(role.is_default()), - "name": role.name, - "color": role.color.value, - "hoist": int(role.hoist), - "position": role.position, - "mentionable": int(role.mentionable), - "role_id": role.id, - "permissions": role.permissions.value - }) - - return await Template.get_template(self.database, self.tables, template_id)
    -
    - -
    - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/index.html b/docs/_build/html/_modules/index.html deleted file mode 100644 index 6abce18..0000000 --- a/docs/_build/html/_modules/index.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - Overview: module code — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt deleted file mode 100644 index 08db599..0000000 --- a/docs/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,12 +0,0 @@ -Welcome to discordSuperUtils's documentation! -============================================= - -.. toctree:: - :caption: Table of Contents - :name: mastertoc - -DSU (discord-super-utils) is a modern discord module including many useful managers and features that make discord bot programming in discord.py extremely easy. - -Installation: :ref:`Installation` - -Read the module information here: :ref:`Modules` diff --git a/docs/_build/html/_sources/installation.rst.txt b/docs/_build/html/_sources/installation.rst.txt deleted file mode 100644 index d294c06..0000000 --- a/docs/_build/html/_sources/installation.rst.txt +++ /dev/null @@ -1,23 +0,0 @@ -.. _Installation: - -Installation -========================= - -This page will guide you through the installation progress of discord.py and discord-super-utils ------------------------------------------------------------------------------------------------- - -Installing discord.py is a straight forward task. - -.. code-block:: console - python -m pip install discord.py - -To install discord.py with full voice support, use the following command: - -.. code-block:: console - python -m pip install discord.py[voice] - - -Now, to install discordSuperUtils, use the following command: - -.. code-block:: console - python -m pip install discordSuperUtils diff --git a/docs/_build/html/_sources/source/discordSuperUtils.rst.txt b/docs/_build/html/_sources/source/discordSuperUtils.rst.txt deleted file mode 100644 index 0ea875d..0000000 --- a/docs/_build/html/_sources/source/discordSuperUtils.rst.txt +++ /dev/null @@ -1,197 +0,0 @@ -.. _Modules: - - -discordSuperUtils Modules -========================= - -discordSuperUtils.Antispam module ---------------------------------- - -.. automodule:: discordSuperUtils.Antispam - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Ban module ----------------------------- - -.. automodule:: discordSuperUtils.Ban - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Base module ------------------------------ - -.. automodule:: discordSuperUtils.Base - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Birthday module ---------------------------------- - -.. automodule:: discordSuperUtils.Birthday - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.CommandHinter module --------------------------------------- - -.. automodule:: discordSuperUtils.CommandHinter - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Convertors module ------------------------------------ - -.. automodule:: discordSuperUtils.Convertors - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Database module ---------------------------------- - -.. automodule:: discordSuperUtils.Database - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Economy module --------------------------------- - -.. automodule:: discordSuperUtils.Economy - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.FiveM module ------------------------------- - -.. automodule:: discordSuperUtils.FiveM - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Imaging module --------------------------------- - -.. automodule:: discordSuperUtils.Imaging - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Infractions module ------------------------------------- - -.. automodule:: discordSuperUtils.Infractions - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.InviteTracker module --------------------------------------- - -.. automodule:: discordSuperUtils.InviteTracker - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Kick module ------------------------------ - -.. automodule:: discordSuperUtils.Kick - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Leveling module ---------------------------------- - -.. automodule:: discordSuperUtils.Leveling - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.MessageFilter module --------------------------------------- - -.. automodule:: discordSuperUtils.MessageFilter - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Music module ------------------------------- - -.. automodule:: discordSuperUtils.Music - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Mute module ------------------------------ - -.. automodule:: discordSuperUtils.Mute - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Paginator module ----------------------------------- - -.. automodule:: discordSuperUtils.Paginator - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Prefix module -------------------------------- - -.. automodule:: discordSuperUtils.Prefix - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Punishments module ------------------------------------- - -.. automodule:: discordSuperUtils.Punishments - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.ReactionRoles module --------------------------------------- - -.. automodule:: discordSuperUtils.ReactionRoles - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Spotify module --------------------------------- - -.. automodule:: discordSuperUtils.Spotify - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Template module ---------------------------------- - -.. automodule:: discordSuperUtils.Template - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Youtube module --------------------------------- - -.. automodule:: discordSuperUtils.Youtube - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/_build/html/_sources/source/modules.rst.txt b/docs/_build/html/_sources/source/modules.rst.txt deleted file mode 100644 index 5f2ca5b..0000000 --- a/docs/_build/html/_sources/source/modules.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -discordSuperUtils -================= - -.. toctree:: - :maxdepth: 4 - - discordSuperUtils diff --git a/docs/_build/html/_static/alabaster.css b/docs/_build/html/_static/alabaster.css deleted file mode 100644 index 0eddaeb..0000000 --- a/docs/_build/html/_static/alabaster.css +++ /dev/null @@ -1,701 +0,0 @@ -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Georgia, serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 940px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 220px; -} - -div.sphinxsidebar { - width: 220px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 940px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Georgia, serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: Georgia, serif; - font-size: 1em; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -div.sphinxsidebar .badge { - border-bottom: none; -} - -div.sphinxsidebar .badge:hover { - border-bottom: none; -} - -/* To address an issue with donation coming after search */ -div.sphinxsidebar h3.donation { - margin-top: 10px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Georgia, serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: Georgia, serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: #fff; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #EEE; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Make nested-list/multi-paragraph items look better in Releases changelog - * pages. Without this, docutils' magical list fuckery causes inconsistent - * formatting between different release sub-lists. - */ -div#changelog > div.section > ul > li > p:only-child { - margin-bottom: 0; -} - -/* Hide fugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - - -/* relbar */ - -.related { - line-height: 30px; - width: 100%; - font-size: 0.9rem; -} - -.related.top { - border-bottom: 1px solid #EEE; - margin-bottom: 20px; -} - -.related.bottom { - border-top: 1px solid #EEE; -} - -.related ul { - padding: 0; - margin: 0; - list-style: none; -} - -.related li { - display: inline; -} - -nav#rellinks { - float: right; -} - -nav#rellinks li+li:before { - content: "|"; -} - -nav#breadcrumbs li+li:before { - content: "\00BB"; -} - -/* Hide certain items when printing */ -@media print { - div.related { - display: none; - } -} \ No newline at end of file diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css deleted file mode 100644 index 912859b..0000000 --- a/docs/_build/html/_static/basic.css +++ /dev/null @@ -1,904 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a.brackets:before, -span.brackets > a:before{ - content: "["; -} - -a.brackets:after, -span.brackets > a:after { - content: "]"; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -dl.footnote > dt, -dl.citation > dt { - float: left; - margin-right: 0.5em; -} - -dl.footnote > dd, -dl.citation > dd { - margin-bottom: 0em; -} - -dl.footnote > dd:after, -dl.citation > dd:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dt:after { - content: ":"; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0.5em; - content: ":"; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/_build/html/_static/custom.css b/docs/_build/html/_static/custom.css deleted file mode 100644 index 2a924f1..0000000 --- a/docs/_build/html/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/docs/_build/html/_static/doctools.js b/docs/_build/html/_static/doctools.js deleted file mode 100644 index 8cbf1b1..0000000 --- a/docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,323 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { - this.initOnKeyListeners(); - } - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keydown(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box, textarea, dropdown or button - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey - && !event.shiftKey) { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - break; - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - break; - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/_build/html/_static/documentation_options.js b/docs/_build/html/_static/documentation_options.js deleted file mode 100644 index 7c7c6af..0000000 --- a/docs/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,12 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.1.9', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false -}; \ No newline at end of file diff --git a/docs/_build/html/_static/file.png b/docs/_build/html/_static/file.png deleted file mode 100644 index a858a410e4faa62ce324d814e4b816fff83a6fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( diff --git a/docs/_build/html/_static/jquery-3.5.1.js b/docs/_build/html/_static/jquery-3.5.1.js deleted file mode 100644 index 5093733..0000000 --- a/docs/_build/html/_static/jquery-3.5.1.js +++ /dev/null @@ -1,10872 +0,0 @@ -/*! - * jQuery JavaScript Library v3.5.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2020-05-04T22:49Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var flat = arr.flat ? function( array ) { - return arr.flat.call( array ); -} : function( array ) { - return arr.concat.apply( [], array ); -}; - - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - -var isFunction = function isFunction( obj ) { - - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - return typeof obj === "function" && typeof obj.nodeType !== "number"; - }; - - -var isWindow = function isWindow( obj ) { - return obj != null && obj === obj.window; - }; - - -var document = window.document; - - - - var preservedScriptAttributes = { - type: true, - src: true, - nonce: true, - noModule: true - }; - - function DOMEval( code, node, doc ) { - doc = doc || document; - - var i, val, - script = doc.createElement( "script" ); - - script.text = code; - if ( node ) { - for ( i in preservedScriptAttributes ) { - - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `getAttribute` is not enough as - // the `nonce` attribute is reset to an empty string whenever it - // becomes browsing-context connected. - // See https://github.com/whatwg/html/issues/2369 - // See https://html.spec.whatwg.org/#nonce-attributes - // The `node.getAttribute` check was added for the sake of - // `jQuery.globalEval` so that it can fake a nonce-containing node - // via an object. - val = node[ i ] || node.getAttribute && node.getAttribute( i ); - if ( val ) { - script.setAttribute( i, val ); - } - } - } - doc.head.appendChild( script ).parentNode.removeChild( script ); - } - - -function toType( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; -} -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.5.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - even: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return ( i + 1 ) % 2; - } ) ); - }, - - odd: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return i % 2; - } ) ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - copy = options[ name ]; - - // Prevent Object.prototype pollution - // Prevent never-ending loop - if ( name === "__proto__" || target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - src = target[ name ]; - - // Ensure proper type for the source value - if ( copyIsArray && !Array.isArray( src ) ) { - clone = []; - } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { - clone = {}; - } else { - clone = src; - } - copyIsArray = false; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - // Evaluates a script in a provided context; falls back to the global one - // if not specified. - globalEval: function( code, options, doc ) { - DOMEval( code, { nonce: options && options.nonce }, doc ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return flat( ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( _i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = toType( obj ); - - if ( isFunction( obj ) || isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.5 - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://js.foundation/ - * - * Date: 2020-03-14 - */ -( function( window ) { -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - nonnativeSelectorCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ( {} ).hasOwnProperty, - arr = [], - pop = arr.pop, - pushNative = arr.push, - push = arr.push, - slice = arr.slice, - - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[ i ] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + - "ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram - identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - - // "Attribute values must be CSS identifiers [capture 5] - // or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + - whitespace + "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + - whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + - "*" ), - rdescend = new RegExp( whitespace + "|>" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + - whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + - whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + - "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rhtml = /HTML$/i, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), - funescape = function( escape, nonHex ) { - var high = "0x" + escape.slice( 1 ) - 0x10000; - - return nonHex ? - - // Strip the backslash prefix from a non-hex escape sequence - nonHex : - - // Replace a hexadecimal escape sequence with the encoded Unicode code point - // Support: IE <=11+ - // For values outside the Basic Multilingual Plane (BMP), manually construct a - // surrogate pair - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + - ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - inDisabledFieldset = addCombinator( - function( elem ) { - return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - ( arr = slice.call( preferredDoc.childNodes ) ), - preferredDoc.childNodes - ); - - // Support: Android<4.0 - // Detect silently failing push.apply - // eslint-disable-next-line no-unused-expressions - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - pushNative.apply( target, slice.call( els ) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - - // Can't trust NodeList.length - while ( ( target[ j++ ] = els[ i++ ] ) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - setDocument( context ); - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { - - // ID selector - if ( ( m = match[ 1 ] ) ) { - - // Document context - if ( nodeType === 9 ) { - if ( ( elem = context.getElementById( m ) ) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && ( elem = newContext.getElementById( m ) ) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[ 2 ] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !nonnativeSelectorCache[ selector + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && - - // Support: IE 8 only - // Exclude object elements - ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { - - newSelector = selector; - newContext = context; - - // qSA considers elements outside a scoping root when evaluating child or - // descendant combinators, which is not what we want. - // In such cases, we work around the behavior by prefixing every selector in the - // list with an ID selector referencing the scope context. - // The technique has to be used as well when a leading combinator is used - // as such selectors are not recognized by querySelectorAll. - // Thanks to Andrew Dupont for this technique. - if ( nodeType === 1 && - ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - - // We can use :scope instead of the ID hack if the browser - // supports it & if we're not changing the context. - if ( newContext !== context || !support.scope ) { - - // Capture the context ID, setting it first if necessary - if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", ( nid = expando ) ); - } - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + - toSelector( groups[ i ] ); - } - newSelector = groups.join( "," ); - } - - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - nonnativeSelectorCache( selector, true ); - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return ( cache[ key + " " ] = value ); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement( "fieldset" ); - - try { - return !!fn( el ); - } catch ( e ) { - return false; - } finally { - - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split( "|" ), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[ i ] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( ( cur = cur.nextSibling ) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return ( name === "input" || name === "button" ) && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction( function( argument ) { - argument = +argument; - return markFunction( function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ ( j = matchIndexes[ i ] ) ] ) { - seed[ j ] = !( matches[ j ] = seed[ j ] ); - } - } - } ); - } ); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - var namespace = elem.namespaceURI, - docElem = ( elem.ownerDocument || elem ).documentElement; - - // Support: IE <=8 - // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes - // https://bugs.jquery.com/ticket/4833 - return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9 - 11+, Edge 12 - 18+ - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( preferredDoc != document && - ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, - // Safari 4 - 5 only, Opera <=11.6 - 12.x only - // IE/Edge & older browsers don't support the :scope pseudo-class. - // Support: Safari 6.0 only - // Safari 6.0 supports :scope but it's an alias of :root there. - support.scope = assert( function( el ) { - docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); - return typeof el.querySelectorAll !== "undefined" && - !el.querySelectorAll( ":scope fieldset div" ).length; - } ); - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert( function( el ) { - el.className = "i"; - return !el.getAttribute( "className" ); - } ); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert( function( el ) { - el.appendChild( document.createComment( "" ) ); - return !el.getElementsByTagName( "*" ).length; - } ); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert( function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - } ); - - // ID filter and find - if ( support.getById ) { - Expr.filter[ "ID" ] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute( "id" ) === attrId; - }; - }; - Expr.find[ "ID" ] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter[ "ID" ] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode( "id" ); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find[ "ID" ] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( ( elem = elems[ i++ ] ) ) { - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find[ "TAG" ] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert( function( el ) { - - var input; - - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll( "[selected]" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push( "~=" ); - } - - // Support: IE 11+, Edge 15 - 18+ - // IE 11/Edge don't find elements on a `[name='']` query in some cases. - // Adding a temporary attribute to the document before the selection works - // around the issue. - // Interestingly, IE 10 & older don't seem to have the issue. - input = document.createElement( "input" ); - input.setAttribute( "name", "" ); - el.appendChild( input ); - if ( !el.querySelectorAll( "[name='']" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + - whitespace + "*(?:''|\"\")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll( ":checked" ).length ) { - rbuggyQSA.push( ":checked" ); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push( ".#.+[+~]" ); - } - - // Support: Firefox <=3.6 - 5 only - // Old Firefox doesn't throw on a badly-escaped identifier. - el.querySelectorAll( "\\\f" ); - rbuggyQSA.push( "[\\r\\n\\f]" ); - } ); - - assert( function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement( "input" ); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll( "[name=d]" ).length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: Opera 10 - 11 only - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll( "*,:x" ); - rbuggyQSA.push( ",.*:" ); - } ); - } - - if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector ) ) ) ) { - - assert( function( el ) { - - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - } ); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - ) ); - } : - function( a, b ) { - if ( b ) { - while ( ( b = b.parentNode ) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { - - // Choose the first element that is related to our preferred document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( a == document || a.ownerDocument == preferredDoc && - contains( preferredDoc, a ) ) { - return -1; - } - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( b == document || b.ownerDocument == preferredDoc && - contains( preferredDoc, b ) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - return a == document ? -1 : - b == document ? 1 : - /* eslint-enable eqeqeq */ - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( ( cur = cur.parentNode ) ) { - ap.unshift( cur ); - } - cur = b; - while ( ( cur = cur.parentNode ) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[ i ] === bp[ i ] ) { - i++; - } - - return i ? - - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[ i ], bp[ i ] ) : - - // Otherwise nodes in our document sort first - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - ap[ i ] == preferredDoc ? -1 : - bp[ i ] == preferredDoc ? 1 : - /* eslint-enable eqeqeq */ - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - setDocument( elem ); - - if ( support.matchesSelector && documentIsHTML && - !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch ( e ) { - nonnativeSelectorCache( expr, true ); - } - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( context.ownerDocument || context ) != document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( elem.ownerDocument || elem ) != document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - - // If no nodeType, this is expected to be an array - while ( ( node = elem[ i++ ] ) ) { - - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[ 1 ] = match[ 1 ].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[ 3 ] = ( match[ 3 ] || match[ 4 ] || - match[ 5 ] || "" ).replace( runescape, funescape ); - - if ( match[ 2 ] === "~=" ) { - match[ 3 ] = " " + match[ 3 ] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[ 1 ] = match[ 1 ].toLowerCase(); - - if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { - - // nth-* requires argument - if ( !match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[ 4 ] = +( match[ 4 ] ? - match[ 5 ] + ( match[ 6 ] || 1 ) : - 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); - match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - - // other types prohibit arguments - } else if ( match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[ 6 ] && match[ 2 ]; - - if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[ 3 ] ) { - match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - - // Get excess from tokenize (recursively) - ( excess = tokenize( unquoted, true ) ) && - - // advance to the next closing parenthesis - ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { - - // excess is a negative index - match[ 0 ] = match[ 0 ].slice( 0, excess ); - match[ 2 ] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { - return true; - } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - ( pattern = new RegExp( "(^|" + whitespace + - ")" + className + "(" + whitespace + "|$)" ) ) && classCache( - className, function( elem ) { - return pattern.test( - typeof elem.className === "string" && elem.className || - typeof elem.getAttribute !== "undefined" && - elem.getAttribute( "class" ) || - "" - ); - } ); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - /* eslint-disable max-len */ - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - /* eslint-enable max-len */ - - }; - }, - - "CHILD": function( type, what, _argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, _context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( ( node = node[ dir ] ) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( ( node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - - // Use previously-cached element index if available - if ( useCache ) { - - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - - // Use the same loop as above to seek `elem` from the start - while ( ( node = ++nodeIndex && node && node[ dir ] || - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || - ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction( function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[ i ] ); - seed[ idx ] = !( matches[ idx ] = matched[ i ] ); - } - } ) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - - // Potentially complex pseudos - "not": markFunction( function( selector ) { - - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction( function( seed, matches, _context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( ( elem = unmatched[ i ] ) ) { - seed[ i ] = !( matches[ i ] = elem ); - } - } - } ) : - function( elem, _context, xml ) { - input[ 0 ] = elem; - matcher( input, null, xml, results ); - - // Don't keep the element (issue #299) - input[ 0 ] = null; - return !results.pop(); - }; - } ), - - "has": markFunction( function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - } ), - - "contains": markFunction( function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; - }; - } ), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - - // lang value must be a valid identifier - if ( !ridentifier.test( lang || "" ) ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( ( elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); - return false; - }; - } ), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && - ( !document.hasFocus || document.hasFocus() ) && - !!( elem.type || elem.href || ~elem.tabIndex ); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return ( nodeName === "input" && !!elem.checked ) || - ( nodeName === "option" && !!elem.selected ); - }, - - "selected": function( elem ) { - - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - // eslint-disable-next-line no-unused-expressions - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos[ "empty" ]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( ( attr = elem.getAttribute( "type" ) ) == null || - attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo( function() { - return [ 0 ]; - } ), - - "last": createPositionalPseudo( function( _matchIndexes, length ) { - return [ length - 1 ]; - } ), - - "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - } ), - - "even": createPositionalPseudo( function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "odd": createPositionalPseudo( function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? - argument + length : - argument > length ? - length : - argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ) - } -}; - -Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || ( match = rcomma.exec( soFar ) ) ) { - if ( match ) { - - // Don't consume trailing commas as valid - soFar = soFar.slice( match[ 0 ].length ) || soFar; - } - groups.push( ( tokens = [] ) ); - } - - matched = false; - - // Combinators - if ( ( match = rcombinators.exec( soFar ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - - // Cast descendant combinators to space - type: match[ 0 ].replace( rtrim, " " ) - } ); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || - ( match = preFilters[ type ]( match ) ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - type: type, - matches: match - } ); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[ i ].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || - ( outerCache[ elem.uniqueID ] = {} ); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( ( oldCache = uniqueCache[ key ] ) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return ( newCache[ 2 ] = oldCache[ 2 ] ); - } else { - - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[ i ]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[ 0 ]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[ i ], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( ( elem = unmatched[ i ] ) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( - selector || "*", - context.nodeType ? [ context ] : context, - [] - ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( ( elem = temp[ i ] ) ) { - matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) ) { - - // Restore matcherIn since elem is not yet a final match - temp.push( ( matcherIn[ i ] = elem ) ); - } - } - postFinder( null, ( matcherOut = [] ), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { - - seed[ temp ] = !( results[ temp ] = elem ); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - } ); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[ 0 ].type ], - implicitRelative = leadingRelative || Expr.relative[ " " ], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - ( checkContext = context ).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[ j ].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens - .slice( 0, i - 1 ) - .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), - - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), - len = elems.length; - - if ( outermost ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - outermostContext = context == document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( !context && elem.ownerDocument != document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( ( matcher = elementMatchers[ j++ ] ) ) { - if ( matcher( elem, context || document, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - - // They will have gone through all possible matchers - if ( ( elem = !matcher && elem ) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( ( matcher = setMatchers[ j++ ] ) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !( unmatched[ i ] || setMatched[ i ] ) ) { - setMatched[ i ] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[ i ] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( - selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) - ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( ( selector = compiled.selector || selector ) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[ 0 ] = match[ 0 ].slice( 0 ); - if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - - context = ( Expr.find[ "ID" ]( token.matches[ 0 ] - .replace( runescape, funescape ), context ) || [] )[ 0 ]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[ i ]; - - // Abort if we hit a combinator - if ( Expr.relative[ ( type = token.type ) ] ) { - break; - } - if ( ( find = Expr.find[ type ] ) ) { - - // Search, expanding context for leading sibling combinators - if ( ( seed = find( - token.matches[ 0 ].replace( runescape, funescape ), - rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || - context - ) ) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert( function( el ) { - - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; -} ); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert( function( el ) { - el.innerHTML = ""; - return el.firstChild.getAttribute( "href" ) === "#"; -} ) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - } ); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert( function( el ) { - el.innerHTML = ""; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -} ) ) { - addHandle( "value", function( elem, _name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - } ); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert( function( el ) { - return el.getAttribute( "disabled" ) == null; -} ) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; - } - } ); -} - -return Sizzle; - -} )( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; - - - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -}; -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Filtered directly for both simple and complex selectors - return jQuery.filter( qualifier, elements, not ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, _i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, _i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, _i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( elem.contentDocument != null && - - // Support: IE 11+ - // elements with no `data` attribute has an object - // `contentDocument` with a `null` prototype. - getProto( elem.contentDocument ) ) { - - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && toType( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( _i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.stackTrace ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the stack, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // rejected_handlers.disable - // fulfilled_handlers.disable - tuples[ 3 - i ][ 3 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock, - - // progress_handlers.lock - tuples[ 0 ][ 3 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the master Deferred - master = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - master.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( master.state() === "pending" || - isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return master.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); - } - - return master.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -jQuery.Deferred.exceptionHook = function( error, stack ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( toType( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, _key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; - - -// Matches dashed string for camelizing -var rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g; - -// Used by camelCase as callback to replace() -function fcamelCase( _all, letter ) { - return letter.toUpperCase(); -} - -// Convert dashed to camelCase; used by the css and data modules -// Support: IE <=9 - 11, Edge 12 - 15 -// Microsoft forgot to hump their vendor prefix (#9572) -function camelCase( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); -} -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( camelCase ); - } else { - key = camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var documentElement = document.documentElement; - - - - var isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ); - }, - composed = { composed: true }; - - // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only - // Check attachment across shadow DOM boundaries when possible (gh-3504) - // Support: iOS 10.0-10.2 only - // Early iOS 10 versions support `attachShadow` but not `getRootNode`, - // leading to errors. We need to check for `getRootNode`. - if ( documentElement.getRootNode ) { - isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ) || - elem.getRootNode( composed ) === elem.ownerDocument; - }; - } -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - isAttached( elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, scale, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = elem.nodeType && - ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Support: Firefox <=54 - // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) - initial = initial / 2; - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - while ( maxIterations-- ) { - - // Evaluate and update our best guess (doubling guesses that zero out). - // Finish if the scale equals or crosses 1 (making the old*new product non-positive). - jQuery.style( elem, prop, initialInUnit + unit ); - if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { - maxIterations = 0; - } - initialInUnit = initialInUnit / scale; - - } - - initialInUnit = initialInUnit * 2; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - -var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // Support: IE <=9 only - // IE <=9 replaces "; - support.option = !!div.lastChild; -} )(); - - -// We have to close these tags to support XHTML (#13200) -var wrapMap = { - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
    " ], - col: [ 2, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] -}; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: IE <=9 only -if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "" ]; -} - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (#15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 - 11+ -// focus() and blur() are asynchronous, except when they are no-op. -// So expect focus to be synchronous when the element is already active, -// and blur to be synchronous when the element is not already active. -// (focus and blur are always synchronous in other supported browsers, -// this just defines when we can count on it). -function expectSync( elem, type ) { - return ( elem === safeActiveElement() ) === ( type === "focus" ); -} - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, expectSync ) { - - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var notAsync, result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - // Support: IE <=9 - 11+ - // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); - this[ type ](); - result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - return result.value; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering the - // native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved.length ) { - - // ...and capture the result - dataPriv.set( this, type, { - value: jQuery.event.trigger( - - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), - this - ) - } ); - - // Abort handling of the native event - event.stopImmediatePropagation(); - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (#504, #13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - - which: function( event ) { - var button = event.button; - - // Add which for key events - if ( event.which == null && rkeyEvent.test( event.type ) ) { - return event.charCode != null ? event.charCode : event.keyCode; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { - if ( button & 1 ) { - return 1; - } - - if ( button & 2 ) { - return 3; - } - - if ( button & 4 ) { - return 2; - } - - return 0; - } - - return event.which; - } -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - delegateType: delegateType - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (#15098, #14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (#8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px"; - tr.style.height = "1px"; - trChild.style.height = "9px"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, #12537) - // .css('--customProperty) (#3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "gridArea": true, - "gridColumn": true, - "gridColumnEnd": true, - "gridColumnStart": true, - "gridRow": true, - "gridRowEnd": true, - "gridRowStart": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (#7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug #9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (#7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - // Use proper attribute retrieval(#12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) > -1 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - return this.each( function() { - var className, i, self, classNames; - - if ( isValidValue ) { - - // Toggle individual class names - i = 0; - self = jQuery( this ); - classNames = classesToArray( value ); - - while ( ( className = classNames[ i++ ] ) ) { - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (#14686, #14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -support.focusin = "onfocusin" in window; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( - dataPriv.get( cur, "events" ) || Object.create( null ) - )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = { guid: Date.now() }; - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ) - .filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ) - .map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (#10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket #12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script - if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - -jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } -} ); - - -jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (#11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // #1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see #8605, #14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // #14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - - - - - - - - - -
    -
    -
    -
    - - -

    Index

    - -
    - A - | B - | C - | D - | E - | F - | G - | H - | I - | J - | K - | L - | M - | N - | O - | P - | Q - | R - | S - | T - | U - | V - | W - | X - -
    -

    A

    - - - -
    - -

    B

    - - - -
    - -

    C

    - - - -
    - -

    D

    - - - -
    - -

    E

    - - - -
    - -

    F

    - - - -
    - -

    G

    - - - -
    - -

    H

    - - - -
    - -

    I

    - - - -
    - -

    J

    - - -
    - -

    K

    - - -
    - -

    L

    - - - -
    - -

    M

    - - - -
    - -

    N

    - - - -
    - -

    O

    - - - -
    - -

    P

    - - - -
    - -

    Q

    - - - -
    - -

    R

    - - - -
    - -

    S

    - - - -
    - -

    T

    - - - -
    - -

    U

    - - - -
    - -

    V

    - - - -
    - -

    W

    - - - -
    - -

    X

    - - -
    - - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html deleted file mode 100644 index 00d8e62..0000000 --- a/docs/_build/html/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - Welcome to discordSuperUtils’s documentation! — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - -
    -
    -
    -
    - -
    -

    Welcome to discordSuperUtils’s documentation!

    -
    -
    -

    DSU (discord-super-utils) is a modern discord module including many useful managers and features that make discord bot programming in discord.py extremely easy.

    -

    Installation: Installation

    -

    Read the module information here: discordSuperUtils Modules

    -
    - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/installation.html b/docs/_build/html/installation.html deleted file mode 100644 index 0872dbe..0000000 --- a/docs/_build/html/installation.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - Installation — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - -
    -
    -
    -
    - -
    -

    Installation

    -
    -

    This page will guide you through the installation progress of discord.py and discord-super-utils

    -

    Installing discord.py is a straight forward task.

    -

    To install discord.py with full voice support, use the following command:

    -

    Now, to install discordSuperUtils, use the following command:

    -
    -
    - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/objects.inv b/docs/_build/html/objects.inv deleted file mode 100644 index 8a735b007306f662fb7a5fcfca86c437aca1d837..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3524 zcmV;#4LkB9AX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkpX>((5 za%59=aAk5;bZKmJ3L_v^WpZ8b#rNMXCQiPX<{x4c-qBX%XZ_q5#9SMl#@9pD>2D-c5d6< z@tJP7$B#*7#RHL$gfU340Z_KGn%~IpEGW>o8ZY*lLqSmE!Ky{b5Piqfv=#y2ujC%S;4^9SC>9(KZ{ zBm<|y)jU%>juiVg-!UoDUIYr!jALMIAQUK?Xx8-?vdr?>?h79JrFEGnr^@epyjJ8` zb5p3iGD!q_#`Y^Ow^pSUISlqo$&JUzna~hWq3t$hdsr`0N;LNhvg+33 zB%7Hc3IWntSnBvIeie3mE=rN+VzYIflgRkLRn849Us)t76ZwXYg(TXXB6CKbr@mv) zIdhJLf+^?(-iiWjhv>lt)^IzwaRx+r&OIEf*53j7(F?^)P6j@WyW#+K-|9rHbo@NS}JA%0r_<| zH2`Sc2CoNvO0y0CqeLn|Dq{q<3hmt|NdXTE8HT|8jVC1uvT!yHP~0hCvCfs{Z^8hk zVTK&WwRb`xd|5yuvMFGtshS-$HlD(bP~)DK;5j#CQ7CPB+I1(Vrjw2>KHHlw65va3 zTmz^KmfNV|n%B!Dal<})>fIYh_483LC*QaU>}1RG4Uf-2k(65VJlT`c5aZ|EqM<=X zKaSd15Ig85f$W{Ute@L3bTpQ}CN`FwrChHR)9J}H@5PRPLOGlOR{g)1Y=64a3;?4w zx5*Y$>+1g_*E^`76(Cg53i3|b_daij=)rC?wrSFtfE!=KMekVhvI)sWb83r@Kt}IA zUf%r_e|Wh36)}n*WB73S@$z4U=$~%yJ|NNkD=I7-!)(T$vB(fP*Kvxf3y^%rr7MgJ zt=2-)lD%@7sLb^T2p>91lxp_cR3$D%V(TRoc&2Tcu{>TYoiR(pAQekvr&yc_KVvUE z);!D=5;1V?qB}w*&PwSDMec_G{0&fve63j+O2E=q9mvl6lbyyWYUIbEK536^h#o#B z7cznFe1uUs6}q1>A!R`I2Xo-?Ww7&XKI|1oHbKV*T9!9Y^2#(;0qQ9&q;2nn}hi`R0%HXam)r4PN^O0~?GyE(R=c)>&a#3J|JlYg4I%Pe{ zgHBq{5ZQCo2AjB^CDKZA!Um&Qt2s$#LKx=4h#ix(ESOgNRBilwFC=ggTo1HaHB?`O zFH!m?vgrEu@#^!#b$omG%k9%m{BVN^d%jJC=ZBB~7hc#Rtv>BQgK#&q{|TMLnBxM) z!*dkSHI8#c;4QavRIj4&S$fw6#wnN8wgM79VlQ$A8!C-RVE)#FMVHcrI_Vy|Nw0@| z&)i5H2wQLQdVCSZZJzQs9P&q{ij(IwJ5bTb&!6uh%G|-c`y5li{``FNd;>-f|3Q)Z z5E15WhscQ(&hNz{rNuYg4Kv8

    o=Uc8$jAj;Bw+b5m+RM6zOsIQU_NqRNB%Fq{J2 z48O%QtA100q;=e=aYxcJ(s?%$+`Sfw zp+L^nYtTo;PQ(%T!gt3cz><+Tf!1gU)Hz_sZiql8cwB9sC&Q=GZ_Q1ak%(Ce7%y2*-aV1Tjd3R7#$c`+SK=oqz&7~w*T9&F;`WlaIR6~-#P2kXIcFirq{6g3kt zfaA~}HZ(}=As-?NjMzTy1~$QG6ZSoTV-?GBc~Z-pWTt-)6EOUP`JO%=E<1LFTc>^8 z7cyZCWTEDOBP}VA(`E%qj(|Lb@%#iaW_KloFH1j^BEY0c0 zMI2XpulZWM!TR8UR==Rkas;!Q^gD=t((N0=H$eQORZECwK0wy*0nI#uiHaeI*cm^4 zy?qxotR&@%;&C^1!PdS`Y1tOTZ26fwrGO1~UuZEGW?Yu<`7qdpploZT>tL zo7aNSrN;FkKg3*3wG1&A#d!4bFwch2=o+qhy$VZXPSxa5(0Vz^x|A}c3Z*cx))-}K zNH8l{#+ke}8I^AgA-+H1oCrS5?zGbxClNgH%r!VJ?bqN%*CN;y)BE~xMNyyqcZ!D2 zqepA4vN#n0b8U(0t;5}Bf>huLH%i>UjnV=W-A~dqJ3Gk;+MN<)4E_{}*dI!kb4kmz zQzGFsRB(N2->{;Pdz}9FapS(4(ol5Tn}h(fnP&>!4P~rA^ej^ZL#+hU5V@lEP4|&G z1n8LaPlmwNF+#C&z0Io@mPCbo+|Uvb;oEMHkA;W#E`;66p+E755>sK5^;PHWq^>H5 zu!nb64RGUf_MpuJ(?kRj;()^++D}`Hz7L|~DU=bYWp)FW+uB(`HOhlVZ2i(01exT4 z1ekOlC*f~}bjs?p5Z3Q~S+^TMY4`}uBj9gJAyYc>vo$*$`#?is_QrLf^y=00(o`NX zkT^FY)*=Z@X$cz+lxUs8MJ?Ky#BZKnKo&T4lbq*eF?2McgCY%=efAcvR30>fEiM^{ zPpi`d!bp^kh0vJ?{E(7ouSDouRVSKh8plsp`2^yM@(z$if&u+#F(DBNWe6A;yb)0K za!+pjb#>^fp^l=1-HuhIU@MtA$b6yE)Eoj|MpNZDr^tcEePLGGoiVP{qJCr&P?F{;Ow%RP(sUk%fRv~MJ6}2@nLq5@)+gaZ zW$PZX|F!Z-`Cj}f>Oir=sq7P%CxvJpc7zn9J<@!pXi}hh*F`<`0+gzMnCI8Zr{xQ$ z8zSWjfK%6JA=pH zPD<9S?XDJj0A=fMDEM0Wqv?1daTXWz_JKuo?aZjma4>msq>A0R?od#ST$t)@9+O{s-u$9 zD{+`D{gcBomPuJ1L)3#jTyMA!SB5`p1s?p+jedJS&6s~=igh$cziaB?e()3~G}L-= zRjn8>7p{>;TBq1An}SNnYLHvE({~K0f0!9KcgVgX#r@uHUAy#zF`VgSdn8M^R{iF& zD)*ZKFQ#Pd-k{lf%+#WvpBO>xW-dnZ7Iy2mp8Ch^y$UH8n^L5F;dFgrw_25(t$XD? zC0G<%ZL|vl{Qa!)qGG>r yts8%_bhpg$FUR - - - - - - Python Module Index — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - -

    - -
    -
    -
    -
    - - -

    Python Module Index

    - -
    - d -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     
    - d
    - discordSuperUtils -
        - discordSuperUtils.Antispam -
        - discordSuperUtils.Ban -
        - discordSuperUtils.Base -
        - discordSuperUtils.Birthday -
        - discordSuperUtils.CommandHinter -
        - discordSuperUtils.Convertors -
        - discordSuperUtils.Database -
        - discordSuperUtils.Economy -
        - discordSuperUtils.FiveM -
        - discordSuperUtils.Imaging -
        - discordSuperUtils.Infractions -
        - discordSuperUtils.InviteTracker -
        - discordSuperUtils.Kick -
        - discordSuperUtils.Leveling -
        - discordSuperUtils.MessageFilter -
        - discordSuperUtils.Music -
        - discordSuperUtils.Mute -
        - discordSuperUtils.Paginator -
        - discordSuperUtils.Prefix -
        - discordSuperUtils.Punishments -
        - discordSuperUtils.ReactionRoles -
        - discordSuperUtils.Spotify -
        - discordSuperUtils.Template -
    - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/search.html b/docs/_build/html/search.html deleted file mode 100644 index ea473fd..0000000 --- a/docs/_build/html/search.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - Search — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Search

    - - - - -

    - Searching for multiple words only shows matches that contain - all words. -

    - - -
    - - - -
    - - - -
    - -
    - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js deleted file mode 100644 index d2d68e2..0000000 --- a/docs/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({docnames:["index","installation","source/discordSuperUtils","source/modules"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["index.rst","installation.rst","source\\discordSuperUtils.rst","source\\modules.rst"],objects:{"discordSuperUtils.Antispam":{DefaultSpamDetectionGenerator:[2,1,1,""],SpamDetectionGenerator:[2,1,1,""],SpamManager:[2,1,1,""]},"discordSuperUtils.Antispam.DefaultSpamDetectionGenerator":{generate:[2,2,1,""]},"discordSuperUtils.Antispam.SpamDetectionGenerator":{generate:[2,2,1,""]},"discordSuperUtils.Antispam.SpamManager":{add_punishments:[2,2,1,""],bot:[2,3,1,""],generator:[2,3,1,""],get_messages_similarity:[2,2,1,""],punishments:[2,3,1,""]},"discordSuperUtils.Ban":{BanManager:[2,1,1,""],UnbanFailure:[2,4,1,""]},"discordSuperUtils.Ban.BanManager":{ban:[2,2,1,""],bot:[2,3,1,""],get_ban:[2,2,1,""],get_banned_members:[2,2,1,""],on_database_connect:[2,2,1,""],punish:[2,2,1,""],unban:[2,2,1,""]},"discordSuperUtils.Base":{CogManager:[2,1,1,""],DatabaseChecker:[2,1,1,""],DatabaseNotConnected:[2,4,1,""],EventManager:[2,1,1,""],InvalidGenerator:[2,4,1,""],generate_column_types:[2,5,1,""],get_generator_response:[2,5,1,""],maybe_coroutine:[2,5,1,""],questionnaire:[2,5,1,""]},"discordSuperUtils.Base.CogManager":{Cog:[2,1,1,""],event:[2,2,1,""]},"discordSuperUtils.Base.DatabaseChecker":{connect_to_database:[2,2,1,""]},"discordSuperUtils.Base.EventManager":{add_event:[2,2,1,""],call_event:[2,2,1,""],event:[2,2,1,""],remove_event:[2,2,1,""]},"discordSuperUtils.Base.InvalidGenerator":{generator:[2,3,1,""]},"discordSuperUtils.Birthday":{BirthdayManager:[2,1,1,""],BirthdayMember:[2,1,1,""],PartialBirthdayMember:[2,1,1,""]},"discordSuperUtils.Birthday.BirthdayManager":{create_birthday:[2,2,1,""],get_birthday:[2,2,1,""],get_members_with_birthday:[2,2,1,""],get_midnight_timezones:[2,2,1,""],get_upcoming:[2,2,1,""],on_database_connect:[2,2,1,""],round_to_nearest:[2,2,1,""]},"discordSuperUtils.Birthday.BirthdayMember":{"delete":[2,2,1,""],age:[2,2,1,""],birthday_date:[2,2,1,""],set_birthday_date:[2,2,1,""],set_timezone:[2,2,1,""],timezone:[2,2,1,""]},"discordSuperUtils.CommandHinter":{CommandHinter:[2,1,1,""],CommandResponseGenerator:[2,1,1,""],DefaultResponseGenerator:[2,1,1,""]},"discordSuperUtils.CommandHinter.CommandHinter":{bot:[2,3,1,""],command_names:[2,6,1,""],generator:[2,3,1,""]},"discordSuperUtils.CommandHinter.CommandResponseGenerator":{generate:[2,2,1,""]},"discordSuperUtils.CommandHinter.DefaultResponseGenerator":{generate:[2,2,1,""]},"discordSuperUtils.Convertors":{TimeConvertor:[2,1,1,""],isfloat:[2,5,1,""]},"discordSuperUtils.Convertors.TimeConvertor":{convert:[2,2,1,""]},"discordSuperUtils.Database":{Database:[2,1,1,""],DatabaseManager:[2,1,1,""],UnsupportedDatabase:[2,4,1,""],create_mysql:[2,5,1,""]},"discordSuperUtils.Database.Database":{"delete":[2,2,1,""],close:[2,2,1,""],create_table:[2,2,1,""],execute:[2,2,1,""],insert:[2,2,1,""],insertifnotexists:[2,2,1,""],select:[2,2,1,""],update:[2,2,1,""],updateorinsert:[2,2,1,""]},"discordSuperUtils.Database.DatabaseManager":{connect:[2,2,1,""]},"discordSuperUtils.Economy":{EconomyAccount:[2,1,1,""],EconomyManager:[2,1,1,""]},"discordSuperUtils.Economy.EconomyAccount":{bank:[2,2,1,""],change_bank:[2,2,1,""],change_currency:[2,2,1,""],currency:[2,2,1,""],net:[2,2,1,""]},"discordSuperUtils.Economy.EconomyManager":{create_account:[2,2,1,""],generate_checks:[2,2,1,""],get_account:[2,2,1,""],get_leaderboard:[2,2,1,""]},"discordSuperUtils.FiveM":{FiveMPlayer:[2,1,1,""],FiveMServer:[2,1,1,""],ServerNotFound:[2,4,1,""]},"discordSuperUtils.FiveM.FiveMPlayer":{fetch:[2,2,1,""]},"discordSuperUtils.FiveM.FiveMServer":{fetch:[2,2,1,""]},"discordSuperUtils.Imaging":{Backgrounds:[2,1,1,""],ImageManager:[2,1,1,""]},"discordSuperUtils.Imaging.Backgrounds":{BLANK_GRAY:[2,3,1,""],GALAXY:[2,3,1,""],GAMING:[2,3,1,""]},"discordSuperUtils.Imaging.ImageManager":{convert_image:[2,2,1,""],create_leveling_profile:[2,2,1,""],create_welcome_card:[2,2,1,""],draw_profile_picture:[2,2,1,""],human_format:[2,2,1,""],load_asset:[2,2,1,""],make_request:[2,2,1,""],merge_image:[2,2,1,""],multiline_text:[2,2,1,""]},"discordSuperUtils.Infractions":{Infraction:[2,1,1,""],InfractionManager:[2,1,1,""],PartialInfraction:[2,1,1,""]},"discordSuperUtils.Infractions.Infraction":{"delete":[2,2,1,""],datetime:[2,2,1,""],reason:[2,2,1,""],set_reason:[2,2,1,""]},"discordSuperUtils.Infractions.InfractionManager":{add_punishments:[2,2,1,""],get_infractions:[2,2,1,""],punish:[2,2,1,""],warn:[2,2,1,""]},"discordSuperUtils.InviteTracker":{InviteAccount:[2,1,1,""],InviteTracker:[2,1,1,""]},"discordSuperUtils.InviteTracker.InviteAccount":{get_invited_users:[2,2,1,""]},"discordSuperUtils.InviteTracker.InviteTracker":{fetch_inviter:[2,2,1,""],get_invite:[2,2,1,""],get_members_invited:[2,2,1,""],get_user_info:[2,2,1,""],register_invite:[2,2,1,""]},"discordSuperUtils.Kick":{KickManager:[2,1,1,""]},"discordSuperUtils.Kick.KickManager":{bot:[2,3,1,""],punish:[2,2,1,""]},"discordSuperUtils.Leveling":{LevelingAccount:[2,1,1,""],LevelingManager:[2,1,1,""]},"discordSuperUtils.Leveling.LevelingAccount":{initial_rank_xp:[2,2,1,""],level:[2,2,1,""],next_level:[2,2,1,""],percentage_next_level:[2,2,1,""],set_level:[2,2,1,""],set_next_level:[2,2,1,""],set_xp:[2,2,1,""],xp:[2,2,1,""]},"discordSuperUtils.Leveling.LevelingManager":{create_account:[2,2,1,""],generate_checks:[2,2,1,""],get_account:[2,2,1,""],get_leaderboard:[2,2,1,""],get_roles:[2,2,1,""],on_database_connect:[2,2,1,""],set_interval:[2,2,1,""],set_roles:[2,2,1,""]},"discordSuperUtils.MessageFilter":{DefaultMessageResponseGenerator:[2,1,1,""],MessageFilter:[2,1,1,""],MessageResponseGenerator:[2,1,1,""]},"discordSuperUtils.MessageFilter.DefaultMessageResponseGenerator":{DISCORD_INVITE_RE:[2,3,1,""],URL_RE:[2,3,1,""],generate:[2,2,1,""]},"discordSuperUtils.MessageFilter.MessageFilter":{add_punishments:[2,2,1,""],bot:[2,3,1,""],generator:[2,3,1,""],punishments:[2,3,1,""],wipe_cache_delay:[2,3,1,""]},"discordSuperUtils.MessageFilter.MessageResponseGenerator":{generate:[2,2,1,""]},"discordSuperUtils.Music":{AlreadyConnected:[2,4,1,""],AlreadyPaused:[2,4,1,""],InvalidSkipIndex:[2,4,1,""],Loops:[2,1,1,""],MusicManager:[2,1,1,""],NotConnected:[2,4,1,""],NotPaused:[2,4,1,""],NotPlaying:[2,4,1,""],Player:[2,1,1,""],QueueEmpty:[2,4,1,""],QueueError:[2,4,1,""],QueueManager:[2,1,1,""],SkipError:[2,4,1,""],UserNotConnected:[2,4,1,""]},"discordSuperUtils.Music.Loops":{LOOP:[2,3,1,""],NO_LOOP:[2,3,1,""],QUEUE_LOOP:[2,3,1,""]},"discordSuperUtils.Music.MusicManager":{bot:[2,3,1,""],client_id:[2,3,1,""],client_secret:[2,3,1,""],create_player:[2,2,1,""],ensure_activity:[2,2,1,""],fetch_data:[2,2,1,""],get_player_played_duration:[2,2,1,""],get_queue:[2,2,1,""],inactivity_timeout:[2,3,1,""],join:[2,2,1,""],leave:[2,2,1,""],loop:[2,2,1,""],lyrics:[2,2,1,""],now_playing:[2,2,1,""],pause:[2,2,1,""],play:[2,2,1,""],queue:[2,3,1,""],queue_add:[2,2,1,""],queue_remove:[2,2,1,""],queueloop:[2,2,1,""],resume:[2,2,1,""],skip:[2,2,1,""],spotify:[2,3,1,""],spotify_support:[2,3,1,""],volume:[2,2,1,""]},"discordSuperUtils.Music.Player":{data:[2,3,1,""],duration:[2,3,1,""],last_pause_timestamp:[2,3,1,""],make_multiple_players:[2,2,1,""],make_player:[2,2,1,""],start_timestamp:[2,3,1,""],stream_url:[2,3,1,""],title:[2,3,1,""],url:[2,3,1,""]},"discordSuperUtils.Music.QueueManager":{add:[2,2,1,""],clear:[2,2,1,""],history:[2,3,1,""],loop:[2,3,1,""],now_playing:[2,3,1,""],queue:[2,3,1,""],remove:[2,2,1,""],volume:[2,3,1,""]},"discordSuperUtils.Mute":{AlreadyMuted:[2,4,1,""],MuteManager:[2,1,1,""]},"discordSuperUtils.Mute.MuteManager":{bot:[2,3,1,""],ensure_permissions:[2,2,1,""],get_muted_members:[2,2,1,""],mute:[2,2,1,""],on_database_connect:[2,2,1,""],on_member_join:[2,2,1,""],punish:[2,2,1,""],unmute:[2,2,1,""]},"discordSuperUtils.Paginator":{ButtonsPageManager:[2,1,1,""],EmojiError:[2,4,1,""],PageManager:[2,1,1,""],generate_embeds:[2,5,1,""]},"discordSuperUtils.Paginator.ButtonsPageManager":{"public":[2,3,1,""],button_color:[2,3,1,""],buttons:[2,3,1,""],ctx:[2,3,1,""],index:[2,3,1,""],messages:[2,3,1,""],run:[2,2,1,""],timeout:[2,3,1,""]},"discordSuperUtils.Paginator.PageManager":{"public":[2,3,1,""],ctx:[2,3,1,""],emojis:[2,3,1,""],index:[2,3,1,""],messages:[2,3,1,""],run:[2,2,1,""],timeout:[2,3,1,""]},"discordSuperUtils.Prefix":{PrefixManager:[2,1,1,""]},"discordSuperUtils.Prefix.PrefixManager":{get_prefix:[2,2,1,""],set_prefix:[2,2,1,""]},"discordSuperUtils.Punishments":{Punisher:[2,1,1,""],Punishment:[2,1,1,""],get_relevant_punishment:[2,5,1,""]},"discordSuperUtils.Punishments.Punisher":{punish:[2,2,1,""]},"discordSuperUtils.ReactionRoles":{ReactionManager:[2,1,1,""]},"discordSuperUtils.ReactionRoles.ReactionManager":{create_reaction:[2,2,1,""],delete_reaction:[2,2,1,""],get_emoji_sql:[2,2,1,""],get_reactions:[2,2,1,""],on_database_connect:[2,2,1,""]},"discordSuperUtils.Spotify":{SpotifyClient:[2,1,1,""]},"discordSuperUtils.Spotify.SpotifyClient":{fetch_full_playlist:[2,2,1,""],fetch_playlist_data:[2,2,1,""],get_songs:[2,2,1,""],get_type:[2,2,1,""],make_title:[2,2,1,""]},"discordSuperUtils.Template":{DictionaryConvertible:[2,1,1,""],PartialTemplate:[2,1,1,""],Template:[2,1,1,""],TemplateCategory:[2,1,1,""],TemplateInfo:[2,1,1,""],TemplateManager:[2,1,1,""],TemplateRole:[2,1,1,""],TemplateTextChannel:[2,1,1,""],TemplateVoiceChannel:[2,1,1,""]},"discordSuperUtils.Template.DictionaryConvertible":{from_dict:[2,2,1,""]},"discordSuperUtils.Template.PartialTemplate":{categories:[2,3,1,""],info:[2,3,1,""],roles:[2,3,1,""],text_channels:[2,3,1,""],voice_channels:[2,3,1,""]},"discordSuperUtils.Template.Template":{"delete":[2,2,1,""],apply:[2,2,1,""],apply_categories:[2,2,1,""],apply_channels:[2,2,1,""],apply_roles:[2,2,1,""],apply_settings:[2,2,1,""],apply_voice_channels:[2,2,1,""],categories:[2,3,1,""],database:[2,3,1,""],format_overwrites:[2,2,1,""],get_overwrite:[2,2,1,""],get_template:[2,2,1,""],info:[2,3,1,""],roles:[2,3,1,""],tables:[2,3,1,""],text_channels:[2,3,1,""],voice_channels:[2,3,1,""]},"discordSuperUtils.Template.TemplateCategory":{category_id:[2,3,1,""],from_dict:[2,2,1,""],name:[2,3,1,""],overwrites:[2,3,1,""],position:[2,3,1,""]},"discordSuperUtils.Template.TemplateInfo":{afk_channel:[2,3,1,""],afk_timeout:[2,3,1,""],explict_content_filter:[2,3,1,""],from_dict:[2,2,1,""],guild:[2,3,1,""],mfa_level:[2,3,1,""],system_channel:[2,3,1,""],template_id:[2,3,1,""],verification_level:[2,3,1,""]},"discordSuperUtils.Template.TemplateManager":{bot:[2,3,1,""],create_template:[2,2,1,""],get_template:[2,2,1,""],get_templates:[2,2,1,""],write_overwrites:[2,2,1,""]},"discordSuperUtils.Template.TemplateRole":{color:[2,3,1,""],default_role:[2,3,1,""],from_dict:[2,2,1,""],get_raw:[2,2,1,""],hoist:[2,3,1,""],mentionable:[2,3,1,""],name:[2,3,1,""],permissions:[2,3,1,""],position:[2,3,1,""],role_id:[2,3,1,""]},"discordSuperUtils.Template.TemplateTextChannel":{category:[2,3,1,""],channel_id:[2,3,1,""],from_dict:[2,2,1,""],name:[2,3,1,""],nsfw:[2,3,1,""],overwrites:[2,3,1,""],position:[2,3,1,""],slowmode:[2,3,1,""],topic:[2,3,1,""]},"discordSuperUtils.Template.TemplateVoiceChannel":{bitrate:[2,3,1,""],category:[2,3,1,""],channel_id:[2,3,1,""],from_dict:[2,2,1,""],name:[2,3,1,""],overwrites:[2,3,1,""],position:[2,3,1,""],user_limit:[2,3,1,""]},discordSuperUtils:{Antispam:[2,0,0,"-"],Ban:[2,0,0,"-"],Base:[2,0,0,"-"],Birthday:[2,0,0,"-"],CommandHinter:[2,0,0,"-"],Convertors:[2,0,0,"-"],Database:[2,0,0,"-"],Economy:[2,0,0,"-"],FiveM:[2,0,0,"-"],Imaging:[2,0,0,"-"],Infractions:[2,0,0,"-"],InviteTracker:[2,0,0,"-"],Kick:[2,0,0,"-"],Leveling:[2,0,0,"-"],MessageFilter:[2,0,0,"-"],Music:[2,0,0,"-"],Mute:[2,0,0,"-"],Paginator:[2,0,0,"-"],Prefix:[2,0,0,"-"],Punishments:[2,0,0,"-"],ReactionRoles:[2,0,0,"-"],Spotify:[2,0,0,"-"],Template:[2,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"],"6":["py","property","Python property"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function","6":"py:property"},terms:{"0":2,"1":2,"100":2,"100j":2,"12":2,"150":2,"160":2,"16711680":2,"180":2,"1m":2,"2":2,"25":2,"255":2,"3":2,"30":2,"300":2,"5":2,"50":2,"6":2,"60":2,"604800":2,"7":2,"7d":2,"9":2,"abstract":2,"boolean":2,"break":2,"byte":2,"case":2,"class":2,"do":2,"enum":2,"float":2,"function":[0,1,2,3],"int":2,"new":2,"public":2,"return":2,"static":2,"super":0,"true":2,"while":2,A:2,If:2,In:2,No:2,TOS:2,The:2,To:1,ToS:2,abc:2,add:2,add_ev:2,add_punish:2,afk_channel:2,afk_timeout:2,ag:2,album:2,all:2,allow:2,alreadi:2,alreadyconnect:2,alreadymut:2,alreadypaus:2,am:2,amount:2,an:2,ani:2,answer:2,antispam:3,anyth:2,appli:2,apply_categori:2,apply_channel:2,apply_rol:2,apply_set:2,apply_voice_channel:2,ar:2,arg:2,argument:2,ask:2,asset:2,astlei:2,async:2,await:2,award_rol:2,back:2,background:2,badargu:2,ban:3,bank:2,banmanag:2,base:3,befor:2,being:2,between:2,birthdai:3,birthday_d:2,birthday_manag:2,birthdaymanag:2,birthdaymemb:2,bitrat:2,blank_grai:2,blend_level:2,block:[],bool:2,bot:[0,2],button:2,button_color:2,buttonspagemanag:2,call:2,call_ev:2,callabl:2,callback:2,can:2,cant:2,card:2,categori:2,category_id:2,categorychannel:2,change_bank:2,change_curr:2,channel:2,channel_id:2,check:2,checker:2,classmethod:2,clear:2,client:2,client_id:2,client_secret:2,close:2,code:[],cog:2,cogmanag:2,color:2,column:2,com:2,command:[1,2],command_nam:2,commanderror:2,commandhint:3,commandresponsegener:2,compar:2,compil:2,connect:2,connect_to_databas:2,consol:[],contact:2,contain:2,content:2,context:2,convers:2,convert:2,convert_from:2,convert_imag:2,convertor:3,coro:[],coroutin:[0,1,2,3],count:2,creat:2,create_account:2,create_birthdai:2,create_leveling_profil:2,create_mysql:2,create_play:2,create_react:2,create_t:2,create_templ:2,create_welcome_card:2,creation:2,ctx:2,currenc:2,current:2,d:2,dai:2,data:2,databas:3,database_typ:2,databasecheck:2,databasemanag:2,databasenotconnect:2,date:2,date_of_infract:2,datetim:2,dbname:2,decor:2,default_prefix:2,default_rol:2,default_role_interv:2,defaultmessageresponsegener:2,defaultresponsegener:2,defaultspamdetectiongener:2,delet:2,delete_messag:2,delete_react:2,deriv:2,descript:2,detect:2,dict:2,dictionari:2,dictionaryconvert:2,disabl:2,disco:2,disconnect:2,discord:[0,2],discord_fil:2,discord_invite_r:2,discordapp:2,discordsuperutil:1,doe:2,draw_profile_pictur:2,dsu:0,durat:2,easi:0,economi:3,economyaccount:2,economymanag:2,element:2,emb:2,emoji:2,emojierror:2,empti:2,enabl:2,ensur:2,ensure_act:2,ensure_permiss:2,enumer:2,error:2,etc:2,event:2,eventmanag:2,exampl:2,except:2,execut:2,exist:2,explict_content_filt:2,ext:2,extrem:0,fail:2,fals:2,featur:[0,2],feel:2,fetch:2,fetch_data:2,fetch_full_playlist:2,fetch_invit:2,fetch_playlist_data:2,fetchal:2,field:2,file:2,filter:2,find:2,first:2,fivem:3,fivemplay:2,fivemserv:2,follow:1,font:2,font_path:2,foreground:2,format_overwrit:2,forward:1,found:2,free:2,from:2,from_dict:2,from_timestamp:2,full:1,func:2,galaxi:2,game:2,gener:2,generate_check:2,generate_column_typ:2,generate_emb:2,generator_typ:2,get:2,get_account:2,get_ban:2,get_banned_memb:2,get_birthdai:2,get_emoji_sql:2,get_generator_respons:2,get_infract:2,get_invit:2,get_invited_us:2,get_leaderboard:2,get_members_invit:2,get_members_with_birthdai:2,get_messages_similar:2,get_midnight_timezon:2,get_muted_memb:2,get_overwrit:2,get_player_played_dur:2,get_prefix:2,get_queu:2,get_raw:2,get_react:2,get_relevant_punish:2,get_rol:2,get_song:2,get_templ:2,get_typ:2,get_upcom:2,get_user_info:2,gg:2,give:2,given:2,gonna:2,guild:2,ha:2,handl:2,handler:2,have:2,he:2,head:[],hei:2,help:2,here:0,heyh:2,histori:2,hoist:2,host:2,http:2,human_format:2,id:2,identifi:2,imag:3,imagedraw:2,imagefont:2,imagemanag:2,inact:2,inactivity_timeout:2,inappropri:2,incas:2,includ:0,index:2,indic:2,info:2,inform:0,infract:3,infraction_id:2,infractionmanag:2,initial_rank_xp:2,inner:2,insert:2,insert_data:2,insertifnotexist:2,insid:2,instal:0,intend:2,intern:2,interv:2,invalid:2,invalid_command:2,invalidgener:2,invalidskipindex:2,invit:2,invite_track:2,inviteaccount:2,invitetrack:3,invoc:2,io:2,ip:2,isfloat:2,isn:2,iter:2,j:2,join:2,kei:2,kick:3,kickmanag:2,kwarg:2,last:2,last_messag:2,last_pause_timestamp:2,leav:2,left:2,level:3,leveling_manag:2,levelingaccount:2,levelingmanag:2,li:2,liabl:2,lib:2,limit:2,link:2,list:2,list_to_gener:2,listen:2,load_asset:2,locat:2,logic:2,loop:2,lyric:2,m:[],make:[0,2],make_multiple_play:2,make_play:2,make_request:2,make_titl:2,manag:[0,2],manager_typ:2,mani:0,max:2,maybe_coroutin:2,me:2,member:2,member_account:2,member_birthdai:2,mention:2,merg:2,merge_imag:2,messag:2,messagefilt:3,messageresponsegener:2,method:2,mfa_level:2,minut:2,modern:0,modul:[0,3],multiline_text:2,multipli:2,music:3,musicmanag:2,mute:3,muted_rol:2,mutemanag:2,name:2,net:2,never:2,new_reason:2,next:2,next_level:2,no_loop:2,none:2,notconnect:2,notpaus:2,notplai:2,now:[1,2],now_plai:2,nsfw:2,num:2,number:2,object:2,occur:2,offlin:2,offset:2,on_database_connect:2,on_inactivity_timeout:2,on_member_join:2,on_music_error:2,one:2,option:2,org:2,out:2,outlin:2,outline_color:2,outline_thick:2,overrid:2,overwrit:2,overwrite_object:2,overwrites_object:2,packag:2,page:[],pagemanag:2,pagin:3,param:2,paramet:2,partial:2,partialbirthdaymemb:2,partialinfract:2,partialtempl:2,pass:2,password:2,paus:2,pcmvolumetransform:2,percentage_next_level:2,permiss:2,permissionoverwrit:2,pick:2,ping:2,pip:[],plai:2,player:2,player_dict:2,player_id:2,playlist:2,pleas:2,png:2,poop:[],port:2,posit:2,prefix:3,prefixmanag:2,privat:2,program:0,propag:2,properli:2,properti:2,provid:2,punish:3,punish_aft:2,punish_count:2,punishment_manag:2,punishment_reason:2,punishment_tim:2,py:0,python3:[],python:2,queri:2,question:2,questionnair:2,queue:2,queue_add:2,queue_loop:2,queue_remov:2,queueempti:2,queueerror:2,queueloop:2,queuemanag:2,quiz:2,rais:2,rank:2,rank_multipli:2,re:2,reactionmanag:2,reactionrol:3,read:[0,2],reason:2,receiv:2,recommend:2,register_invit:2,remov:2,remove_ev:2,remove_on_react:2,repres:2,request:2,resourc:2,respons:2,resum:2,rick:2,role:2,role_id:2,round:2,round_to_nearest:2,run:2,s:2,same:2,search:2,second:2,select:2,send:2,sent:2,server:2,servernotfound:2,servic:2,set:2,set_birthday_d:2,set_interv:2,set_level:2,set_next_level:2,set_prefix:2,set_reason:2,set_rol:2,set_timezon:2,set_xp:2,sh:[],should:2,similar:2,site:2,size:2,skip:2,skiperror:2,slowmod:2,so:2,someth:2,song:2,sourc:2,spam:2,spamdetectiongener:2,spammanag:2,speak:2,specif:2,specifi:2,spotifi:3,spotify_support:2,spotifycli:2,sql_queri:2,start_height:2,start_timestamp:2,statu:2,stop:2,str:2,straight:1,stream_url:2,string:2,string_format:2,submodul:[],success:2,suggest:2,suitabl:2,support:[1,2],suppos:2,sure:2,system_channel:2,t:2,tabl:2,table_identifi:2,table_nam:2,tables_column_data:2,task:1,team:2,templat:3,template_id:2,templatecategori:2,templateinfo:2,templatemanag:2,templaterol:2,templatetextchannel:2,templatevoicechannel:2,term:2,text:2,text_channel:2,text_color:2,textchannel:2,thei:2,thi:[0,2,3],through:2,time:2,time_of_ban:2,time_of_mut:2,timeconvertor:2,timedelta:2,timedelta_to_round:2,timeout:2,timestamp:2,timezon:2,titl:2,togeth:2,toggl:2,topic:2,track:2,transpar:2,tri:2,tupl:2,two:2,type:2,typeerror:2,unban:2,unbanfailur:2,union:2,unmut:2,unsupport:2,unsupporteddatabas:2,until:2,up:2,updat:2,updateorinsert:2,url:2,url_r:2,us:[0,1,2],user:2,user_limit:2,usernotconnect:2,utc:2,util:0,valid:2,valu:2,valueerror:2,variabl:2,verification_level:2,verificationlevel:2,version:2,video:2,violat:2,voic:[1,2],voice_channel:2,voicechannel:2,volum:2,wa:2,wai:2,wait:2,warn:2,we:2,when:2,where:2,which:2,width:2,wipe_cache_delai:2,without:2,write_overwrit:2,wrong:2,www:2,xp:2,xp_cooldown:2,xp_on_messag:2,you:2,youtub:3,youtube_dl:2,ytdl:2,z0:2,za:2},titles:["Welcome to discordSuperUtils\u2019s documentation!","Installation","discordSuperUtils Modules","discordSuperUtils"],titleterms:{"super":1,antispam:2,ban:2,base:2,birthdai:2,commandhint:2,content:[],convertor:2,databas:2,discord:1,discordsuperutil:[0,2,3],document:0,economi:2,fivem:2,guid:1,imag:2,indic:[],infract:2,instal:1,invitetrack:2,kick:2,level:2,messagefilt:2,modul:2,music:2,mute:2,packag:[],page:1,pagin:2,prefix:2,progress:1,punish:2,py:1,reactionrol:2,requir:[],s:0,spotifi:2,submodul:[],tabl:[],templat:2,thi:1,through:1,util:1,welcom:0,you:1,youtub:2}}) \ No newline at end of file diff --git a/docs/_build/html/source/discordSuperUtils.html b/docs/_build/html/source/discordSuperUtils.html deleted file mode 100644 index e927fa2..0000000 --- a/docs/_build/html/source/discordSuperUtils.html +++ /dev/null @@ -1,2941 +0,0 @@ - - - - - - - - - discordSuperUtils Modules — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - -
    -
    -
    -
    - -
    -

    discordSuperUtils Modules

    -
    -

    discordSuperUtils.Antispam module

    -
    -
    -class discordSuperUtils.Antispam.DefaultSpamDetectionGenerator[source]
    -

    Bases: discordSuperUtils.Antispam.SpamDetectionGenerator

    -
    -
    -generate(last_messages: List[discord.message.Message]) Union[bool, Any][source]
    -

    This function is an abstract method. -The generate function of the generator.

    -
    -
    Parameters
    -

    last_messages (List[discord.Message]) – The last messages sent (5 is max).

    -
    -
    Returns
    -

    A boolean representing if the message is spam.

    -
    -
    Return type
    -

    Union[bool, Any]

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.Antispam.SpamDetectionGenerator[source]
    -

    Bases: abc.ABC

    -

    Represents a SpamManager that filters messages to find spam.

    -
    -
    -abstract generate(last_messages: List[discord.message.Message]) Union[bool, Any][source]
    -

    This function is an abstract method. -The generate function of the generator.

    -
    -
    Parameters
    -

    last_messages (List[discord.Message]) – The last messages sent (5 is max).

    -
    -
    Returns
    -

    A boolean representing if the message is spam.

    -
    -
    Return type
    -

    Union[bool, Any]

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.Antispam.SpamManager(bot: commands.Bot, generator: SpamDetectionGenerator = None, wipe_cache_delay: timedelta = datetime.timedelta(seconds=300))[source]
    -

    Bases: discordSuperUtils.Base.EventManager

    -

    Represents a SpamManager which detects spam.

    -
    -
    -add_punishments(punishments: List[Punishment]) None[source]
    -
    - -
    -
    -bot
    -
    - -
    -
    -generator
    -
    - -
    -
    -static get_messages_similarity(messages: Iterable[str]) float[source]
    -

    Gets the similarity between messages.

    -
    -
    Parameters
    -

    messages (Iterable[str]) – Messages to compare.

    -
    -
    Return type
    -

    float

    -
    -
    Returns
    -

    The similarity between messages (0-1)

    -
    -
    -
    - -
    -
    -punishments
    -
    - -
    - -
    -
    -

    discordSuperUtils.Ban module

    -
    -
    -class discordSuperUtils.Ban.BanManager(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker, discordSuperUtils.Punishments.Punisher

    -

    A BanManager that manages guild bans.

    -
    -
    -async ban(member: discord.member.Member, reason: str = 'No reason provided.', time_of_ban: Union[int, float] = 0) None[source]
    -

    This function is a coroutine.

    -

    Bans the member from the guild.

    -
    -
    Parameters
    -
      -
    • member (discord.Member) – The member to ban.

    • -
    • reason (str) – The reason of the ban.

    • -
    • time_of_ban (Union[int, float]) – The time of ban.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -bot
    -
    - -
    -
    -async static get_ban(member: Union[discord.member.Member, discord.user.User], guild: discord.guild.Guild) Optional[discord.user.User][source]
    -

    This function is a coroutine.

    -

    This function returns the user object of the member if he is banned from the guild.

    -
    -
    Parameters
    -
      -
    • member (discord.Member) – The banned member.

    • -
    • guild (discord.Guild) – The guild.

    • -
    -
    -
    Returns
    -

    The user object if found.

    -
    -
    Return type
    -

    Optional[discord.User]

    -
    -
    -
    - -
    -
    -async get_banned_members() List[Dict[str, Any]][source]
    -

    This function is a coroutine.

    -

    This function returns all the members that are supposed to be unbanned but are banned.

    -
    -
    Returns
    -

    The list of unbanned members.

    -
    -
    Return type
    -

    List[Dict[str, Any]]

    -
    -
    -
    - -
    -
    -async on_database_connect()[source]
    -
    - -
    -
    -async punish(ctx: commands.Context, member: discord.Member, punishment: Punishment) None[source]
    -

    The manager’s punish function.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context of the punishments.

    • -
    • member (discord.Member) – The member to punish.

    • -
    • punishment (Punishment) – The punishment to punish the member with.

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    -
    -async unban(member: Union[discord.member.Member, discord.user.User], guild: Optional[discord.guild.Guild] = None) bool[source]
    -
    - -
    - -
    -
    -exception discordSuperUtils.Ban.UnbanFailure[source]
    -

    Bases: Exception

    -

    Raises an exception when the user tries to unban a discord.User without passing the guild.

    -
    - -
    -
    -

    discordSuperUtils.Base module

    -
    -
    -class discordSuperUtils.Base.CogManager[source]
    -

    Bases: object

    -

    A CogManager which helps the user use the managers inside discord cogs.

    -
    -
    -class Cog(managers: Optional[List] = None)[source]
    -

    Bases: object

    -

    The internal Cog class.

    -
    - -
    -
    -static event(manager_type: Any) Callable[source]
    -

    Adds an event to the Cog event list.

    -
    -
    Parameters
    -

    manager_type (Any) – The manager type of the event.

    -
    -
    Return type
    -

    Callable

    -
    -
    Returns
    -

    The inner function.

    -
    -
    Raises
    -

    TypeError: The listener isn’t async.

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.Base.DatabaseChecker(tables_column_data: List[Dict[str, str]], table_identifiers: List[str])[source]
    -

    Bases: discordSuperUtils.Base.EventManager

    -

    A database checker which makes sure the database is connected to a manager and handles the table creation.

    -
    -
    -async connect_to_database(database: Database, tables: List[str]) None[source]
    -

    Connects to the database. -Calls on_database_connect when connected.

    -
    -
    Parameters
    -
      -
    • database (Database) – The database to connect to.

    • -
    • tables (List[str]) – The tables to create (incase they do not exist).

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    - -
    -
    -exception discordSuperUtils.Base.DatabaseNotConnected[source]
    -

    Bases: Exception

    -

    Raises an error when the user tries to use a method of a manager without a database connected to it.

    -
    - -
    -
    -class discordSuperUtils.Base.EventManager[source]
    -

    Bases: object

    -

    An event manager that manages events for managers.

    -
    -
    -add_event(func: Callable, name: Optional[str] = None) None[source]
    -

    Adds an event to the event dictionary.

    -
    -
    Parameters
    -
      -
    • func (Callable) – The event callback.

    • -
    • name (str) – The event name.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    Raises
    -

    TypeError: The listener isn’t async.

    -
    -
    -
    - -
    -
    -async call_event(name: str, *args, **kwargs) None[source]
    -

    Calls the event name with the arguments

    -
    -
    Parameters
    -
      -
    • name (str) – The event name.

    • -
    • args – The arguments.

    • -
    • kwargs – The key arguments.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -event(name: Optional[str] = None) Callable[source]
    -

    A decorator which adds an event listener.

    -
    -
    Parameters
    -

    name (str) – The event name.

    -
    -
    Returns
    -

    The inner function.

    -
    -
    Return type
    -

    Callable

    -
    -
    -
    - -
    -
    -remove_event(func: Callable, name: Optional[str] = None) None[source]
    -

    Removes an event from the event dictionary.

    -
    -
    Parameters
    -
      -
    • func (Callable) – The event callback.

    • -
    • name (str) – The event name.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    - -
    -
    -exception discordSuperUtils.Base.InvalidGenerator(generator)[source]
    -

    Bases: Exception

    -

    Raises an exception when the user passes an invalid generator.

    -
    -
    -generator
    -
    - -
    - -
    -
    -discordSuperUtils.Base.generate_column_types(types: Iterable[str], database_type: Any) Optional[List[str]][source]
    -

    Generates the column type names that are suitable for the database type.

    -
    -
    Parameters
    -
      -
    • types (Iterable[str]) – The column types.

    • -
    • database_type (Any) – The database type.

    • -
    -
    -
    Returns
    -

    The suitable column types for the database types.

    -
    -
    Return type
    -

    Optional[List[str]]

    -
    -
    -
    - -
    -
    -discordSuperUtils.Base.get_generator_response(generator: Any, generator_type: Any, *args, **kwargs) Any[source]
    -

    Returns the generator response with the arguments.

    -
    -
    Parameters
    -
      -
    • generator (Any) – The generator to get the response from.

    • -
    • generator_type (Any) – The generator type. (Should be same as the generator type.

    • -
    • args – The arguments of the generator.

    • -
    • kwargs – The key arguments of the generator

    • -
    -
    -
    Returns
    -

    The generator response.

    -
    -
    Return type
    -

    Any

    -
    -
    -
    - -
    -
    -async discordSuperUtils.Base.maybe_coroutine(function: Callable, *args, **kwargs) Any[source]
    -

    This function is a coroutine.

    -

    Returns the coroutine version of the function.

    -
    -
    Parameters
    -
      -
    • function (Union[Awaitable, Callable]) – The function to convert.

    • -
    • args – The arguments.

    • -
    • kwargs – The key arguments:

    • -
    -
    -
    Returns
    -

    The coroutine version of the function.

    -
    -
    Return type
    -

    Awaitable

    -
    -
    -
    - -
    -
    -async discordSuperUtils.Base.questionnaire(ctx: commands.Context, questions: Iterable[Union[str, discord.Embed]], public: bool = False, timeout: Union[float, int] = 30, member: discord.Member = None) Tuple[List[str], bool][source]
    -

    This function is a coroutine.

    -

    Questions the member using a “quiz” and returns the answers. -The questionnaire can be used without a specific member and be public. -If no member was passed and the questionnaire public argument is true, a ValueError will be raised.

    -
    -
    Raises
    -

    ValueError: The questionnaire is private and no member was provided.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context (where the questionnaire will ask the questions).

    • -
    • questions (Iterable[Union[str, discord.Embed]]) – The questions the questionnaire will ask.

    • -
    • public (bool) – A bool indicating if the questionnaire is public.

    • -
    • timeout (Union[float, int]) – The number of seconds until the questionnaire will stop and time out.

    • -
    • member (discord.Member) – The member the questionnaire will get the answers from.

    • -
    -
    -
    Returns
    -

    The answers and a boolean indicating if the questionnaire timed out.

    -
    -
    Return type
    -

    Tuple[List[str], bool]

    -
    -
    -
    - -
    -
    -

    discordSuperUtils.Birthday module

    -
    -
    -class discordSuperUtils.Birthday.BirthdayManager(bot: discord.ext.commands.bot.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async create_birthday(member: discord.member.Member, member_birthday: float, timezone: str = 'UTC') None[source]
    -
    - -
    -
    -async get_birthday(member: discord.member.Member) Optional[discordSuperUtils.Birthday.BirthdayMember][source]
    -
    - -
    -
    -async get_members_with_birthday(timezones: List[str]) List[Dict[str, Any]][source]
    -

    This function receives a list of timezones and returns a list of members that have birthdays in that date -and timezone.

    -
    -
    Parameters
    -

    timezones

    -
    -
    Returns
    -

    -
    -
    -
    - -
    -
    -static get_midnight_timezones() List[str][source]
    -

    This method returns a list of timezones where the current time is 12 am. -:return:

    -
    - -
    -
    -async get_upcoming(guild: discord.guild.Guild) List[discordSuperUtils.Birthday.BirthdayMember][source]
    -
    - -
    -
    -async on_database_connect()[source]
    -
    - -
    -
    -static round_to_nearest(timedelta_to_round)[source]
    -

    This function receives a timedelta to round to and gets the amount of seconds before that timestamp.

    -
    -
    Parameters
    -

    timedelta_to_round

    -
    -
    Returns
    -

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.Birthday.BirthdayMember(birthday_manager: discordSuperUtils.Birthday.BirthdayManager, member: discord.member.Member)[source]
    -

    Bases: object

    -
    -
    -async age() int[source]
    -
    - -
    -
    -async birthday_date() datetime.datetime[source]
    -
    - -
    -
    -async delete() discordSuperUtils.Birthday.PartialBirthdayMember[source]
    -
    - -
    -
    -async set_birthday_date(timestamp: float) None[source]
    -
    - -
    -
    -async set_timezone(timezone: str) None[source]
    -
    - -
    -
    -async timezone() str[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Birthday.PartialBirthdayMember(member: discord.member.Member, birthday_date: datetime.datetime, timezone: str)[source]
    -

    Bases: object

    -
    - -
    -
    -

    discordSuperUtils.CommandHinter module

    -
    -
    -class discordSuperUtils.CommandHinter.CommandHinter(bot: discord.ext.commands.bot.Bot, generator=None)[source]
    -

    Bases: object

    -
    -
    -bot
    -
    - -
    -
    -property command_names: List[str]
    -
    - -
    -
    -generator
    -
    - -
    - -
    -
    -class discordSuperUtils.CommandHinter.CommandResponseGenerator[source]
    -

    Bases: abc.ABC

    -
    -
    -abstract generate(invalid_command: str, suggestions: List[str]) Union[str, discord.embeds.Embed][source]
    -
    - -
    - -
    -
    -class discordSuperUtils.CommandHinter.DefaultResponseGenerator[source]
    -

    Bases: discordSuperUtils.CommandHinter.CommandResponseGenerator

    -
    -
    -generate(invalid_command: str, suggestions: List[str]) discord.embeds.Embed[source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.Convertors module

    -
    -
    -class discordSuperUtils.Convertors.TimeConvertor[source]
    -

    Bases: discord.ext.commands.converter.Converter

    -

    Converts a given argument to an int that represents time in seconds.

    -

    Examples

    -

    7d: 604800 (7 days in seconds)

    -

    1m: 60 (1 minute in seconds)

    -

    heyh: BadArgument (‘hey’ is not an int)

    -

    100j: BadArgument (‘j’ is not a valid time multiplier)

    -
    -
    -async convert(ctx: discord.ext.commands.context.Context, argument: str) Optional[Union[int, float]][source]
    -

    This function is a coroutine.

    -

    The method to override to do conversion logic.

    -

    If an error is found while converting, it is recommended to -raise a CommandError derived exception as it will -properly propagate to the error handlers.

    -
    -
    Parameters
    -
      -
    • ctx (Context) – The invocation context that the argument is being used in.

    • -
    • argument (str) – The argument that is being converted.

    • -
    -
    -
    Raises
    -
      -
    • CommandError – A generic exception occurred when converting the argument.

    • -
    • BadArgument – The converter failed to convert the argument.

    • -
    -
    -
    -
    - -
    - -
    -
    -discordSuperUtils.Convertors.isfloat(string: str) bool[source]
    -

    This function receives a string and returns if it is a float or not -:param string: -:return:

    -
    - -
    -
    -

    discordSuperUtils.Database module

    -
    -
    -class discordSuperUtils.Database.Database(database)[source]
    -

    Bases: abc.ABC

    -
    -
    -abstract async close()[source]
    -
    - -
    -
    -abstract async create_table(table_name: str, columns: Optional[Dict[str, str]] = None, exists: Optional[bool] = False)[source]
    -
    - -
    -
    -abstract async delete(table_name: str, checks: Dict[str, Any])[source]
    -
    - -
    -
    -abstract async execute(sql_query: str, values: List[Any], fetchall: bool = True) Union[List[Dict[str, Any]], Dict[str, Any]][source]
    -
    - -
    -
    -abstract async insert(table_name: str, data: Dict[str, Any])[source]
    -
    - -
    -
    -abstract async insertifnotexists(table_name: str, data: Dict[str, Any], checks: Dict[str, Any])[source]
    -
    - -
    -
    -abstract async select(table_name: str, keys: List[str], checks: Optional[Dict[str, Any]] = None, fetchall: Optional[bool] = False)[source]
    -
    - -
    -
    -abstract async update(table_name: str, data: Dict[str, Any], checks: Dict[str, Any])[source]
    -
    - -
    -
    -abstract async updateorinsert(table_name: str, data: Dict[str, Any], checks: Dict[str, Any], insert_data: Dict[str, Any])[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Database.DatabaseManager[source]
    -

    Bases: object

    -
    -
    -static connect(database)[source]
    -
    - -
    - -
    -
    -exception discordSuperUtils.Database.UnsupportedDatabase[source]
    -

    Bases: Exception

    -

    Raises error when the user tries to use an unsupported database.

    -
    - -
    -
    -async discordSuperUtils.Database.create_mysql(host, port, user, password, dbname)[source]
    -
    - -
    -
    -

    discordSuperUtils.Economy module

    -
    -
    -class discordSuperUtils.Economy.EconomyAccount(guild: int, member: int, database, table)[source]
    -

    Bases: object

    -
    -
    -async bank()[source]
    -
    - -
    -
    -async change_bank(amount: int)[source]
    -
    - -
    -
    -async change_currency(amount: int)[source]
    -
    - -
    -
    -async currency()[source]
    -
    - -
    -
    -async net()[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Economy.EconomyManager(bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async create_account(member: discord.member.Member) None[source]
    -
    - -
    -
    -static generate_checks(guild: int, member: int)[source]
    -
    - -
    -
    -async get_account(member: discord.member.Member) Optional[discordSuperUtils.Economy.EconomyAccount][source]
    -
    - -
    -
    -async get_leaderboard(guild) List[discordSuperUtils.Economy.EconomyAccount][source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.FiveM module

    -
    -
    -class discordSuperUtils.FiveM.FiveMPlayer(player_id, identifiers, name, ping)[source]
    -

    Bases: object

    -
    -
    -classmethod fetch(player_dict)[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.FiveM.FiveMServer(ip, resources, players, name, variables)[source]
    -

    Bases: object

    -
    -
    -async classmethod fetch(ip)[source]
    -
    - -
    - -
    -
    -exception discordSuperUtils.FiveM.ServerNotFound[source]
    -

    Bases: Exception

    -

    Raises an error when a server is invalid or offline.

    -
    - -
    -
    -

    discordSuperUtils.Imaging module

    -
    -
    -class discordSuperUtils.Imaging.Backgrounds(value)[source]
    -

    Bases: enum.Enum

    -

    An enumeration.

    -
    -
    -BLANK_GRAY = 'd:\\python\\lib\\site-packages\\discordSuperUtils\\assets\\2.png'
    -
    - -
    -
    -GALAXY = 'd:\\python\\lib\\site-packages\\discordSuperUtils\\assets\\1.png'
    -
    - -
    -
    -GAMING = 'd:\\python\\lib\\site-packages\\discordSuperUtils\\assets\\3.png'
    -
    - -
    - -
    -
    -class discordSuperUtils.Imaging.ImageManager[source]
    -

    Bases: object

    -
    -
    -async classmethod convert_image(url: str) Image[source]
    -
    - -
    -
    -async create_leveling_profile(member: discord.Member, member_account: LevelingAccount, background: Backgrounds, text_color: Tuple[int, int, int], rank: int, font_path: str = None, outline: int = 5) discord.File[source]
    -
    - -
    -
    -async create_welcome_card(member: discord.member.Member, background: discordSuperUtils.Imaging.Backgrounds, text_color: Tuple[int, int, int], title: str, description: str, font_path: Optional[str] = None, outline: int = 5, transparency: int = 0) discord.file.File[source]
    -
    - -
    -
    -async draw_profile_picture(card: Image, member: discord.Member, location: Tuple[int, int], size: int = 180, outline_thickness: int = 5, status: bool = True, outline_color: Tuple[int, int, int] = (255, 255, 255))[source]
    -
    - -
    -
    -classmethod human_format(num)[source]
    -
    - -
    -
    -static load_asset(name: str) str[source]
    -
    - -
    -
    -async static make_request(url: str) Optional[bytes][source]
    -
    - -
    -
    -async merge_image(foreground: str, background: str, blend_level: float = 0.6, discord_file: bool = True) Union[discord.File, Image][source]
    -

    Merges two images together

    -
    - -
    -
    -static multiline_text(card: ImageDraw, text: str, font: ImageFont, text_color: Tuple[int, int, int], start_height: Union[int, float], width: int)[source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.Infractions module

    -
    -
    -class discordSuperUtils.Infractions.Infraction(database, table: str, member: discord.Member, infraction_id: str)[source]
    -

    Bases: object

    -

    An infraction object.

    -
    -
    -async datetime() Optional[datetime.datetime][source]
    -
    - -
    -
    -async delete() discordSuperUtils.Infractions.PartialInfraction[source]
    -
    - -
    -
    -async reason() Optional[str][source]
    -
    - -
    -
    -async set_reason(new_reason: str) None[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Infractions.InfractionManager(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker, discordSuperUtils.Punishments.Punisher

    -
    -
    -add_punishments(punishments: List[Punishment]) None[source]
    -
    - -
    -
    -async get_infractions(member: discord.Member, infraction_id: str = None, from_timestamp: Union[int, float] = 0) List[Infraction][source]
    -
    - -
    -
    -async punish(ctx: commands.Context, member: discord.Member, punishment: Punishment) None[source]
    -

    The manager’s punish function.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context of the punishments.

    • -
    • member (discord.Member) – The member to punish.

    • -
    • punishment (Punishment) – The punishment to punish the member with.

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    -
    -async warn(ctx: commands.Context, member: discord.Member, reason: str) Infraction[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Infractions.PartialInfraction(member: discord.Member, infraction_id: str, reason: str, date_of_infraction: datetime)[source]
    -

    Bases: object

    -

    A partial infraction.

    -
    - -
    -
    -

    discordSuperUtils.InviteTracker module

    -

    ” -If InviteTracker is used in any way that breaks Discord TOS we, (the DiscordSuperUtils team) -are not responsible or liable in any way. -InviteTracker by DiscordSuperUtils was not intended to violate Discord TOS in any way. -In case we are contacted by Discord, we will remove any and all features that violate the Discord ToS. -Please feel free to read the Discord Terms of Service https://discord.com/terms.

    -
    -
    -class discordSuperUtils.InviteTracker.InviteAccount(invite_tracker: InviteTracker, member: discord.Member)[source]
    -

    Bases: object

    -
    -
    -async get_invited_users()[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.InviteTracker.InviteTracker(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async fetch_inviter(invite: discord.Invite) Union[discord.Member, discord.User][source]
    -
    - -
    -
    -async get_invite(member: discord.Member) Optional[discord.Invite][source]
    -
    - -
    -
    -async get_members_invited(user: Union[discord.User, discord.Member], guild: discord.Guild)[source]
    -
    - -
    -
    -get_user_info(member: discord.Member) InviteAccount[source]
    -
    - -
    -
    -async register_invite(invite: discord.Invite, member: discord.Member, inviter: Union[discord.Member, discord.User]) None[source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.Kick module

    -
    -
    -class discordSuperUtils.Kick.KickManager(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.EventManager, discordSuperUtils.Punishments.Punisher

    -

    A KickManager that manages kicks for guilds.

    -
    -
    -bot
    -
    - -
    -
    -async punish(ctx: commands.Context, member: discord.Member, punishment: Punishment) None[source]
    -

    The manager’s punish function.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context of the punishments.

    • -
    • member (discord.Member) – The member to punish.

    • -
    • punishment (Punishment) – The punishment to punish the member with.

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    - -
    -
    -

    discordSuperUtils.Leveling module

    -
    -
    -class discordSuperUtils.Leveling.LevelingAccount(leveling_manager: LevelingManager, member: discord.Member)[source]
    -

    Bases: object

    -
    -
    -async initial_rank_xp()[source]
    -
    - -
    -
    -async level()[source]
    -
    - -
    -
    -async next_level()[source]
    -
    - -
    -
    -async percentage_next_level()[source]
    -
    - -
    -
    -async set_level(value)[source]
    -
    - -
    -
    -async set_next_level(value)[source]
    -
    - -
    -
    -async set_xp(value)[source]
    -
    - -
    -
    -async xp()[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Leveling.LevelingManager(bot, award_role: bool = False, default_role_interval: int = 5, xp_on_message=5, rank_multiplier=1.5, xp_cooldown=60)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async create_account(member)[source]
    -
    - -
    -
    -static generate_checks(member: discord.Member)[source]
    -
    - -
    -
    -async get_account(member)[source]
    -
    - -
    -
    -async get_leaderboard(guild: discord.Guild)[source]
    -
    - -
    -
    -async get_roles(guild: discord.Guild) List[int][source]
    -

    Returns the role IDs of the guild.

    -
    -
    Parameters
    -

    guild (discord.Guild) – The guild to get the roles from.

    -
    -
    Returns
    -

    -
    -
    Return type
    -

    List[int]

    -
    -
    -
    - -
    -
    -async on_database_connect()[source]
    -
    - -
    -
    -async set_interval(guild: discord.Guild, interval: int = None) None[source]
    -

    Set the role interval of a guild.

    -
    -
    Parameters
    -
      -
    • interval (int) – The interval to set.

    • -
    • guild (discord.Guild) – The guild to set the role interval in.

    • -
    -
    -
    Returns
    -

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -async set_roles(guild: discord.Guild, roles: Iterable[discord.Role]) None[source]
    -

    Sets the roles of the guild.

    -
    -
    Parameters
    -
      -
    • guild (discord.Guild) – The guild to set the roles in.

    • -
    • roles (Iterable[discord.Role]) – The roles to set.

    • -
    -
    -
    Returns
    -

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    - -
    -
    -

    discordSuperUtils.MessageFilter module

    -
    -
    -class discordSuperUtils.MessageFilter.DefaultMessageResponseGenerator[source]
    -

    Bases: discordSuperUtils.MessageFilter.MessageResponseGenerator

    -
    -
    -DISCORD_INVITE_RE = re.compile('(?:(?:http|https)://)?(?:www.)?(?:disco|discord|discordapp).(?:com|gg|io|li|me|net|org)(?:/(?:invite))?/([a-z0-9-.]+)')
    -
    - -
    -
    -URL_RE = re.compile('(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0)
    -
    - -
    -
    -generate(message: discord.Message) Union[bool, Any][source]
    -

    This function is an abstract method. -The generate function of the generator.

    -
    -
    Parameters
    -

    message (discord.Message) – The message to filter.

    -
    -
    Returns
    -

    A boolean representing if the message contains inappropriate content.

    -
    -
    Return type
    -

    Union[bool, Any]

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.MessageFilter.MessageFilter(bot: commands.Bot, generator: MessageResponseGenerator = None, delete_message: bool = True, wipe_cache_delay: timedelta = datetime.timedelta(seconds=300))[source]
    -

    Bases: discordSuperUtils.Base.EventManager

    -

    Represents a discordSuperUtils message filter that filters messages and finds inappropriate content.

    -
    -
    -add_punishments(punishments: List[Punishment]) None[source]
    -
    - -
    -
    -bot
    -
    - -
    -
    -generator
    -
    - -
    -
    -punishments
    -
    - -
    -
    -wipe_cache_delay
    -
    - -
    - -
    -
    -class discordSuperUtils.MessageFilter.MessageResponseGenerator[source]
    -

    Bases: abc.ABC

    -

    Represents a URL response generator that filters messages and checks if they contain URLs or anything -inappropriate.

    -
    -
    -abstract generate(message: discord.Message) Union[bool, Any][source]
    -

    This function is an abstract method. -The generate function of the generator.

    -
    -
    Parameters
    -

    message (discord.Message) – The message to filter.

    -
    -
    Returns
    -

    A boolean representing if the message contains inappropriate content.

    -
    -
    Return type
    -

    Union[bool, Any]

    -
    -
    -
    - -
    - -
    -
    -

    discordSuperUtils.Music module

    -
    -
    -exception discordSuperUtils.Music.AlreadyConnected[source]
    -

    Bases: Exception

    -

    Raises error when client is already connected to voice

    -
    - -
    -
    -exception discordSuperUtils.Music.AlreadyPaused[source]
    -

    Bases: Exception

    -

    Raises error when player is already paused.

    -
    - -
    -
    -exception discordSuperUtils.Music.InvalidSkipIndex[source]
    -

    Bases: Exception

    -

    Raises error when the skip index is < 0

    -
    - -
    -
    -class discordSuperUtils.Music.Loops(value)[source]
    -

    Bases: enum.Enum

    -

    An enumeration.

    -
    -
    -LOOP = 1
    -
    - -
    -
    -NO_LOOP = 0
    -
    - -
    -
    -QUEUE_LOOP = 2
    -
    - -
    - -
    -
    -class discordSuperUtils.Music.MusicManager(bot: commands.Bot, spotify_support: bool = True, inactivity_timeout: int = 60, **kwargs)[source]
    -

    Bases: discordSuperUtils.Base.EventManager

    -

    Represents a MusicManager.

    -
    -
    -bot
    -
    - -
    -
    -client_id
    -
    - -
    -
    -client_secret
    -
    - -
    -
    -async create_player(query: str, requester: discord.member.Member) List[discordSuperUtils.Music.Player][source]
    -

    This function is a coroutine.

    -

    Creates a list of players from the query. -This function supports Spotify and all YTDL supported links.

    -
    -
    Parameters
    -
      -
    • requester (discord.Member) – The requester.

    • -
    • query (str) – The query.

    • -
    -
    -
    Returns
    -

    The list of players.

    -
    -
    Return type
    -

    List[Player]

    -
    -
    -
    - -
    -
    -async ensure_activity(ctx: commands.Context) None[source]
    -

    This function is a coroutine.

    -

    Waits the inactivity timeout and ensures the voice client in ctx is playing a song. -If no song is playing, it disconnects and calls the on_inactivity_timeout event.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -async static fetch_data(query: str) Optional[dict][source]
    -

    This function is a coroutine.

    -

    Fetches the YTDL data of the query.

    -
    -
    Parameters
    -

    query (str) – The query.

    -
    -
    Returns
    -

    The YTDL data.

    -
    -
    Return type
    -

    Optional[dict]

    -
    -
    -
    - -
    -
    -async get_player_played_duration(ctx: commands.Context, player: Player) Optional[float][source]
    -

    This function is a coroutine.

    -

    Returns the played duration of a player.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context.

    • -
    • player (Player) – The player.

    • -
    -
    -
    Returns
    -

    The played duration of the player in seconds.

    -
    -
    Return type
    -

    Optional[float]

    -
    -
    -
    - -
    -
    -async get_queue(ctx: commands.Context) Optional[QueueManager][source]
    -

    This function is a coroutine.

    -

    Returns the queue of ctx.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    The queue.

    -
    -
    Return type
    -

    Optional[QueueManager]

    -
    -
    -
    - -
    -
    -inactivity_timeout
    -
    - -
    -
    -async join(ctx: commands.Context) Optional[discord.VoiceChannel][source]
    -

    This function is a coroutine.

    -

    Joins the ctx voice channel. -Calls on_music_error with AlreadyConnected or UserNotConnected.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    The voice channel it joined.

    -
    -
    Return type
    -

    Optional[discord.VoiceChannel]

    -
    -
    -
    - -
    -
    -async leave(ctx: commands.Context) Optional[discord.VoiceChannel][source]
    -

    This function is a coroutine.

    -

    Leaves the voice channel in ctx.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    The voice channel it left.

    -
    -
    Return type
    -

    Optional[discord.VoiceChannel]

    -
    -
    -
    - -
    -
    -async loop(ctx: commands.Context) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Toggles the loop.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context

    -
    -
    Returns
    -

    A bool indicating if the loop is now enabled or disabled.

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -async lyrics(ctx: commands.Context, query: str = None) Optional[str][source]
    -

    This function is a coroutine.

    -

    Returns the lyrics from the query or the currently playing song.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context.

    • -
    • query (str) – The query.

    • -
    -
    -
    Returns
    -

    The lyrics.

    -
    -
    Return type
    -

    Optional[str]

    -
    -
    -
    - -
    -
    -async now_playing(ctx: commands.Context) Optional[Player][source]
    -

    This function is a coroutine.

    -

    Returns the currently playing player.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    The currently playing player.

    -
    -
    Return type
    -

    Optional[Player]

    -
    -
    -
    - -
    -
    -async pause(ctx: commands.Context) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Pauses the currently playing song in ctx. -Calls on_music_error with AlreadyPaused if already paused.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    A bool indicating if the pause was successful

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -async play(ctx: commands.Context, player: Player = None) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Plays the player or the next song in the queue if the player is not passed.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context.

    • -
    • player (Player) – The player.

    • -
    -
    -
    Returns
    -

    A bool indicating if the play was successful

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -queue
    -
    - -
    -
    -async queue_add(players: List[Player], ctx: commands.Context) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Adds a list of players to the ctx queue. -If a queue does not exist in ctx, it creates one.

    -
    -
    Parameters
    -
      -
    • players (List[Player]) – The list of players.

    • -
    • ctx (commands.Context) – The context.

    • -
    -
    -
    Returns
    -

    A bool indicating if it was successful

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -async queue_remove(ctx: commands.Context, index: int) None[source]
    -

    This function is a coroutine.

    -

    Removes a player from the queue in ctx at the specified index. -Calls on_music_error with QueueError if index is invalid.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context.

    • -
    • index (int) – The index.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -async queueloop(ctx: commands.Context) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Toggles the queue loop.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context

    -
    -
    Returns
    -

    A bool indicating if the queue loop is now enabled or disabled.

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -async resume(ctx: commands.Context) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Resumes the currently paused song in ctx. -Calls on_music_error with NotPaused if not paused.

    -
    -
    Parameters
    -

    ctx (commands.Context) – The context.

    -
    -
    Returns
    -

    A bool indicating if the resume was successful

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    -
    - -
    -
    -async skip(ctx: commands.Context, index: int = None) Optional[Player][source]
    -

    This function is a coroutine.

    -

    Skips to the index in ctx. -Calls on_music_error with InvalidSkipIndex or SkipError.

    -
    -
    Parameters
    -
      -
    • index (int) – The index to skip to.

    • -
    • ctx (commands.Context) – The context.

    • -
    -
    -
    Returns
    -

    A bool indicating if the skip was successful

    -
    -
    Return type
    -

    Optional[Player]

    -
    -
    -
    - -
    -
    -spotify
    -
    - -
    -
    -spotify_support
    -
    - -
    -
    -async volume(ctx: commands.Context, volume: int = None) Optional[float][source]
    -

    This function is a coroutine.

    -

    Sets the volume in ctx. -Returns the current volume if volume is None.

    -
    -
    Parameters
    -
      -
    • volume (int) – The volume to set.

    • -
    • ctx (commands.Context) – The context.

    • -
    -
    -
    Returns
    -

    The new volume.

    -
    -
    Return type
    -

    Optional[float]

    -
    -
    -
    - -
    - -
    -
    -exception discordSuperUtils.Music.NotConnected[source]
    -

    Bases: Exception

    -

    Raises error when client is not connected to a voice channel

    -
    - -
    -
    -exception discordSuperUtils.Music.NotPaused[source]
    -

    Bases: Exception

    -

    Raises error when player is not paused

    -
    - -
    -
    -exception discordSuperUtils.Music.NotPlaying[source]
    -

    Bases: Exception

    -

    Raises error when client is not playing

    -
    - -
    -
    -class discordSuperUtils.Music.Player(source, requester: discord.member.Member, *, data, volume=0.1)[source]
    -

    Bases: discord.player.PCMVolumeTransformer

    -

    Represents a music player.

    -
    -
    -data
    -
    - -
    -
    -duration
    -
    - -
    -
    -last_pause_timestamp
    -
    - -
    -
    -async static make_multiple_players(songs: Iterable[str], requester: discord.member.Member) List[discordSuperUtils.Music.Player][source]
    -

    This function is a coroutine.

    -

    Returns a list of players from a iterable of queries.

    -
    -
    Parameters
    -
      -
    • requester (discord.Member) – The requester.

    • -
    • songs (Iterable[str]) – The queries.

    • -
    -
    -
    Returns
    -

    The list of created players.

    -
    -
    Return type
    -

    List[Player]

    -
    -
    -
    - -
    -
    -async classmethod make_player(query: str, requester: discord.member.Member, playlist: bool = True) List[discordSuperUtils.Music.Player][source]
    -

    This function is a coroutine.

    -

    Returns a list of players from the query. -The list will contain the first video incase it is not a playlist.

    -
    -
    Parameters
    -
      -
    • requester (discord.Member) – The requester.

    • -
    • query (str) – The query.

    • -
    • playlist (bool) – A bool indicating if the function should fetch playlists or get the first video.

    • -
    -
    -
    Returns
    -

    The list of created players.

    -
    -
    Return type
    -

    List[Player]

    -
    -
    -
    - -
    -
    -start_timestamp
    -
    - -
    -
    -stream_url
    -
    - -
    -
    -title
    -
    - -
    -
    -url
    -
    - -
    - -
    -
    -exception discordSuperUtils.Music.QueueEmpty[source]
    -

    Bases: Exception

    -

    Raises error when queue is empty

    -
    - -
    -
    -exception discordSuperUtils.Music.QueueError[source]
    -

    Bases: Exception

    -

    Raises error when something is wrong with the queue

    -
    - -
    -
    -class discordSuperUtils.Music.QueueManager(volume: float, queue: List[discordSuperUtils.Music.Player])[source]
    -

    Bases: object

    -
    -
    -add(player: discordSuperUtils.Music.Player) None[source]
    -

    Adds a player to the queue.

    -
    -
    Parameters
    -

    player (Player) – The player to add.

    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -clear() None[source]
    -

    Clears the queue.

    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -history
    -
    - -
    -
    -loop
    -
    - -
    -
    -now_playing
    -
    - -
    -
    -queue
    -
    - -
    -
    -remove(index: int) Union[discordSuperUtils.Music.Player, Any][source]
    -

    Removes and element from the queue at the specified index, and returns the element’s value.

    -
    -
    Parameters
    -

    index (int) – The index.

    -
    -
    Returns
    -

    The element’s value

    -
    -
    Return type
    -

    Union[Player, Any]

    -
    -
    -
    - -
    -
    -volume
    -
    - -
    - -
    -
    -exception discordSuperUtils.Music.SkipError[source]
    -

    Bases: Exception

    -

    Raises error when there is no song to skip to

    -
    - -
    -
    -exception discordSuperUtils.Music.UserNotConnected[source]
    -

    Bases: Exception

    -

    Raises error when user is not connected to channel

    -
    - -
    -
    -

    discordSuperUtils.Mute module

    -
    -
    -exception discordSuperUtils.Mute.AlreadyMuted[source]
    -

    Bases: Exception

    -

    Raises an error when a user is already muted.

    -
    - -
    -
    -class discordSuperUtils.Mute.MuteManager(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker, discordSuperUtils.Punishments.Punisher

    -

    A MuteManager that handles mutes for guilds.

    -
    -
    -bot
    -
    - -
    -
    -async static ensure_permissions(guild: discord.guild.Guild, muted_role: discord.role.Role) None[source]
    -

    This function is a coroutine.

    -

    This function loops through the guild’s channels and ensures the muted_role is not allowed to -send messages or speak in that channel.

    -
    -
    Parameters
    -
      -
    • guild (discord.Guild) – The guild to get the channels from.

    • -
    • muted_role (discord.Role) – The muted role.

    • -
    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    -
    -async get_muted_members() List[Dict[str, Any]][source]
    -

    This function is a coroutine.

    -

    This function returns all the members that are supposed to be unmuted but are muted.

    -
    -
    Returns
    -

    The unmuted members.

    -
    -
    Return type
    -

    List[Dict[str, Any]]

    -
    -
    -
    - -
    -
    -async mute(member: discord.member.Member, reason: str = 'No reason provided.', time_of_mute: Union[int, float] = 0) None[source]
    -

    This function is a coroutine.

    -

    Mutes a member.

    -
    -
    Raises
    -

    AlreadyMuted: The member is already muted.

    -
    -
    Parameters
    -
      -
    • member (discord.Member) – The member to mute.

    • -
    • reason (str) – The reason of the mute.

    • -
    • time_of_mute (Union[int, float]) – The time of mute.

    • -
    -
    -
    Returns
    -

    None,

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -async on_database_connect()[source]
    -
    - -
    -
    -async on_member_join(member: discord.member.Member) None[source]
    -

    This function is a coroutine.

    -

    The on_member_join event callback. -Used so the member cant leave the guild, join back and be unmuted.

    -
    -
    Parameters
    -

    member (discord.Member) – The member that joined.

    -
    -
    Returns
    -

    None

    -
    -
    Return type
    -

    None

    -
    -
    -
    - -
    -
    -async punish(ctx: commands.Context, member: discord.Member, punishment: Punishment) None[source]
    -

    The manager’s punish function.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context of the punishments.

    • -
    • member (discord.Member) – The member to punish.

    • -
    • punishment (Punishment) – The punishment to punish the member with.

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    -
    -async unmute(member: discord.member.Member) Optional[bool][source]
    -

    This function is a coroutine.

    -

    Unmutes a member.

    -
    -
    Parameters
    -

    member (discord.Member) – The member to unmute.

    -
    -
    Return type
    -

    Optional[bool]

    -
    -
    Returns
    -

    A bool indicating if the unmute was successful

    -
    -
    -
    - -
    - -
    -
    -

    discordSuperUtils.Paginator module

    -
    -
    -class discordSuperUtils.Paginator.ButtonsPageManager(ctx, messages, timeout=60, buttons=None, public=False, index=0, button_color=None)[source]
    -

    Bases: object

    -
    -
    -button_color
    -
    - -
    -
    -buttons
    -
    - -
    -
    -ctx
    -
    - -
    -
    -index
    -
    - -
    -
    -messages
    -
    - -
    -
    -public
    -
    - -
    -
    -async run()[source]
    -
    - -
    -
    -timeout
    -
    - -
    - -
    -
    -exception discordSuperUtils.Paginator.EmojiError[source]
    -

    Bases: Exception

    -
    - -
    -
    -class discordSuperUtils.Paginator.PageManager(ctx, messages, timeout=60, emojis=None, public=False, index=0)[source]
    -

    Bases: object

    -
    -
    -ctx
    -
    - -
    -
    -emojis
    -
    - -
    -
    -index
    -
    - -
    -
    -messages
    -
    - -
    -
    -public
    -
    - -
    -
    -async run()[source]
    -
    - -
    -
    -timeout
    -
    - -
    - -
    -
    -discordSuperUtils.Paginator.generate_embeds(list_to_generate, title, description, fields=25, color=16711680, string_format='{}')[source]
    -
    - -
    -
    -

    discordSuperUtils.Prefix module

    -
    -
    -class discordSuperUtils.Prefix.PrefixManager(bot: discord.ext.commands.bot.Bot, default_prefix: str, mentioned: bool = False)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async get_prefix(guild: Union[discord.guild.Guild, Any]) str[source]
    -
    - -
    -
    -async set_prefix(guild: discord.guild.Guild, prefix: str) None[source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.Punishments module

    -
    -
    -class discordSuperUtils.Punishments.Punisher[source]
    -

    Bases: abc.ABC

    -
    -
    -abstract async punish(ctx: commands.Context, member: discord.Member, punishment: Punishment) None[source]
    -

    The manager’s punish function.

    -
    -
    Parameters
    -
      -
    • ctx (commands.Context) – The context of the punishments.

    • -
    • member (discord.Member) – The member to punish.

    • -
    • punishment (Punishment) – The punishment to punish the member with.

    • -
    -
    -
    Return type
    -

    None

    -
    -
    Returns
    -

    None

    -
    -
    -
    - -
    - -
    -
    -class discordSuperUtils.Punishments.Punishment(punishment_manager, punish_after: int = 3, punishment_reason: str = 'No reason specified.', punishment_time: datetime.timedelta = datetime.timedelta(days=1))[source]
    -

    Bases: object

    -

    A punishment class that is used for punishing members.

    -
    - -
    -
    -discordSuperUtils.Punishments.get_relevant_punishment(punishments: List[discordSuperUtils.Punishments.Punishment], punish_count: int) Optional[discordSuperUtils.Punishments.Punishment][source]
    -

    Returns the punishment that is suitable for the punish count.

    -
    -
    Parameters
    -
      -
    • punishments (List[Punishment]) – The punishments to pick from.

    • -
    • punish_count (int) – The punishment count.

    • -
    -
    -
    Return type
    -

    Optional[Punishment]

    -
    -
    Returns
    -

    The suitable punishment.

    -
    -
    -
    - -
    -
    -

    discordSuperUtils.ReactionRoles module

    -
    -
    -class discordSuperUtils.ReactionRoles.ReactionManager(bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -async create_reaction(guild, message, role, emoji, remove_on_reaction: int)[source]
    -
    - -
    -
    -async delete_reaction(guild, message, emoji)[source]
    -
    - -
    -
    -static get_emoji_sql(emoji)[source]
    -
    - -
    -
    -async get_reactions(guild=None)[source]
    -
    - -
    -
    -async on_database_connect()[source]
    -
    - -
    - -
    -
    -

    discordSuperUtils.Spotify module

    -
    -
    -class discordSuperUtils.Spotify.SpotifyClient(client_id: str, client_secret: str, loop=None)[source]
    -

    Bases: object

    -
    -
    -async fetch_full_playlist(url: str) List[Dict[str, dict]][source]
    -

    This function receives a url and returns all the tracks in that URL.

    -
    -
    Parameters
    -

    url

    -
    -
    Returns
    -

    -
    -
    -
    - -
    -
    -async fetch_playlist_data(url: str, offset: int) Dict[str, Union[int, list]][source]
    -

    This function receives a URL and an offset and returns 100 tracks from that offset -Example: Offset: 50, the URL has 160 tracks, returns tracks from 50-150 (limit is 100).

    -
    -
    Parameters
    -
      -
    • url

    • -
    • offset

    • -
    -
    -
    Returns
    -

    -
    -
    -
    - -
    -
    -async get_songs(url: str) List[str][source]
    -

    This function receives a URL and returns all the tracks in that URL.

    -
    -
    Parameters
    -

    url

    -
    -
    Returns
    -

    -
    -
    -
    - -
    -
    -static get_type(url: str) Optional[str][source]
    -

    This function receives a url and returns the type of the URL. -Return examples: playlist, user, album, etc.

    -
    -
    Parameters
    -

    url

    -
    -
    Returns
    -

    -
    -
    -
    - -
    -
    -static make_title(song: Dict[str, dict]) str[source]
    -

    This function receives a song and creates a title that can be used for youtube_dl searching. -Return example: Never Gonna Give You Up by Rick Astley

    -
    -
    Parameters
    -

    song

    -
    -
    Returns
    -

    -
    -
    -
    - -
    - -
    -
    -

    discordSuperUtils.Template module

    -
    -
    -class discordSuperUtils.Template.DictionaryConvertible[source]
    -

    Bases: abc.ABC

    -
    -
    -abstract classmethod from_dict(convert_from: Dict[str, Any]) discordSuperUtils.Template.DictionaryConvertible[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.PartialTemplate(info: discordSuperUtils.Template.TemplateInfo, categories: List[discordSuperUtils.Template.TemplateCategory], text_channels: List[discordSuperUtils.Template.TemplateTextChannel], voice_channels: List[discordSuperUtils.Template.TemplateVoiceChannel], roles: List[discordSuperUtils.Template.TemplateRole])[source]
    -

    Bases: object

    -
    -
    -categories
    -
    - -
    -
    -info
    -
    - -
    -
    -roles
    -
    - -
    -
    -text_channels
    -
    - -
    -
    -voice_channels
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.Template(database: Database, tables: Dict[str, str], info: TemplateInfo, categories: List[TemplateCategory], text_channels: List[TemplateTextChannel], voice_channels: List[TemplateVoiceChannel], roles: List[TemplateRole])[source]
    -

    Bases: object

    -
    -
    -async apply(guild: discord.guild.Guild) None[source]
    -
    - -
    -
    -async apply_categories(guild: discord.guild.Guild, reason: str) Dict[int, discord.channel.CategoryChannel][source]
    -
    - -
    -
    -async apply_channels(guild: discord.guild.Guild, reason: str, categories: Dict[int, discord.channel.CategoryChannel], roles: Dict[int, discord.role.Role]) Dict[int, discord.channel.TextChannel][source]
    -
    - -
    -
    -async apply_roles(guild: discord.guild.Guild, reason: str) Dict[int, discord.role.Role][source]
    -
    - -
    -
    -async apply_settings(guild: discord.guild.Guild, reason: str, channels: Dict[int, Union[discord.channel.VoiceChannel, discord.channel.TextChannel]]) None[source]
    -
    - -
    -
    -async apply_voice_channels(guild: discord.guild.Guild, reason: str, categories: Dict[int, discord.channel.CategoryChannel], roles: Dict[int, discord.role.Role]) Dict[int, discord.channel.VoiceChannel][source]
    -
    - -
    -
    -categories
    -
    - -
    -
    -database
    -
    - -
    -
    -async delete() discordSuperUtils.Template.PartialTemplate[source]
    -
    - -
    -
    -static format_overwrites(overwrites: Dict[int, discord.permissions.PermissionOverwrite], roles: Dict[int, discord.role.Role]) Dict[discord.role.Role, discord.permissions.PermissionOverwrite][source]
    -
    - -
    -
    -static get_overwrite(overwrites: List[Dict[str, Any]], overwrite_object: int) Dict[int, discord.permissions.PermissionOverwrite][source]
    -
    - -
    -
    -async classmethod get_template(database: Database, tables: Dict[str, str], template_id: str) Optional[Template][source]
    -
    - -
    -
    -info
    -
    - -
    -
    -roles
    -
    - -
    -
    -tables
    -
    - -
    -
    -text_channels
    -
    - -
    -
    -voice_channels
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateCategory(name: str, position: int, category_id: int, overwrites: Dict[int, int])[source]
    -

    Bases: discordSuperUtils.Template.DictionaryConvertible

    -
    -
    -category_id
    -
    - -
    -
    -classmethod from_dict(convert_from: Dict[Any, Any]) discordSuperUtils.Template.TemplateCategory[source]
    -
    - -
    -
    -name
    -
    - -
    -
    -overwrites
    -
    - -
    -
    -position
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateInfo(template_id: str, guild: int, afk_timeout: int, mfa_level: int, verification_level: discord.enums.VerificationLevel, explict_content_filter: int, system_channel: int, afk_channel: int)[source]
    -

    Bases: discordSuperUtils.Template.DictionaryConvertible

    -
    -
    -afk_channel
    -
    - -
    -
    -afk_timeout
    -
    - -
    -
    -explict_content_filter
    -
    - -
    -
    -classmethod from_dict(convert_from: Dict[str, Any]) discordSuperUtils.Template.TemplateInfo[source]
    -
    - -
    -
    -guild
    -
    - -
    -
    -mfa_level
    -
    - -
    -
    -system_channel
    -
    - -
    -
    -template_id
    -
    - -
    -
    -verification_level
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateManager(bot: commands.Bot)[source]
    -

    Bases: discordSuperUtils.Base.DatabaseChecker

    -
    -
    -bot
    -
    - -
    -
    -async create_template(guild: discord.guild.Guild) discordSuperUtils.Template.Template[source]
    -
    - -
    -
    -async get_template(template_id: str) Optional[discordSuperUtils.Template.Template][source]
    -
    - -
    -
    -async get_templates(guild: Optional[discord.guild.Guild] = None) List[discordSuperUtils.Template.Template][source]
    -
    - -
    -
    -async write_overwrites(template_id: str, overwrites_object: int, overwrites: discord.permissions.PermissionOverwrite) None[source]
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateRole(default_role: bool, name: str, color: int, hoist: bool, position: int, mentionable: bool, role_id: int, permissions: discord.permissions.Permissions)[source]
    -

    Bases: discordSuperUtils.Template.DictionaryConvertible

    -
    -
    -color
    -
    - -
    -
    -default_role
    -
    - -
    -
    -classmethod from_dict(convert_from: Dict[str, Any]) discordSuperUtils.Template.TemplateRole[source]
    -
    - -
    -
    -get_raw()[source]
    -
    - -
    -
    -hoist
    -
    - -
    -
    -mentionable
    -
    - -
    -
    -name
    -
    - -
    -
    -permissions
    -
    - -
    -
    -position
    -
    - -
    -
    -role_id
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateTextChannel(name: str, position: int, category: int, topic: str, slowmode: int, nsfw: bool, channel_id: int, overwrites: Dict[int, discord.permissions.PermissionOverwrite])[source]
    -

    Bases: discordSuperUtils.Template.DictionaryConvertible

    -
    -
    -category
    -
    - -
    -
    -channel_id
    -
    - -
    -
    -classmethod from_dict(convert_from: Dict[str, Any]) discordSuperUtils.Template.TemplateTextChannel[source]
    -
    - -
    -
    -name
    -
    - -
    -
    -nsfw
    -
    - -
    -
    -overwrites
    -
    - -
    -
    -position
    -
    - -
    -
    -slowmode
    -
    - -
    -
    -topic
    -
    - -
    - -
    -
    -class discordSuperUtils.Template.TemplateVoiceChannel(name: str, position: int, category: int, bitrate: int, user_limit: int, channel_id: int, overwrites: Dict[int, discord.permissions.PermissionOverwrite])[source]
    -

    Bases: discordSuperUtils.Template.DictionaryConvertible

    -
    -
    -bitrate
    -
    - -
    -
    -category
    -
    - -
    -
    -channel_id
    -
    - -
    -
    -classmethod from_dict(convert_from: Dict[str, Any]) discordSuperUtils.Template.TemplateVoiceChannel[source]
    -
    - -
    -
    -name
    -
    - -
    -
    -overwrites
    -
    - -
    -
    -position
    -
    - -
    -
    -user_limit
    -
    - -
    - -
    -
    -

    discordSuperUtils.Youtube module

    -
    -
    - - -
    -
    -
    -
    - -
    -
    - - - - \ No newline at end of file diff --git a/docs/_build/html/source/modules.html b/docs/_build/html/source/modules.html deleted file mode 100644 index 3f25098..0000000 --- a/docs/_build/html/source/modules.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - discordSuperUtils — discordSuperUtils 0.1.9 documentation - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 08db599..e69de29 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,12 +0,0 @@ -Welcome to discordSuperUtils's documentation! -============================================= - -.. toctree:: - :caption: Table of Contents - :name: mastertoc - -DSU (discord-super-utils) is a modern discord module including many useful managers and features that make discord bot programming in discord.py extremely easy. - -Installation: :ref:`Installation` - -Read the module information here: :ref:`Modules` diff --git a/docs/installation.rst b/docs/installation.rst index d294c06..e69de29 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,23 +0,0 @@ -.. _Installation: - -Installation -========================= - -This page will guide you through the installation progress of discord.py and discord-super-utils ------------------------------------------------------------------------------------------------- - -Installing discord.py is a straight forward task. - -.. code-block:: console - python -m pip install discord.py - -To install discord.py with full voice support, use the following command: - -.. code-block:: console - python -m pip install discord.py[voice] - - -Now, to install discordSuperUtils, use the following command: - -.. code-block:: console - python -m pip install discordSuperUtils diff --git a/docs/make.bat b/docs/make.bat index 8084272..e69de29 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/source/discordSuperUtils.rst b/docs/source/discordSuperUtils.rst index 0ea875d..e69de29 100644 --- a/docs/source/discordSuperUtils.rst +++ b/docs/source/discordSuperUtils.rst @@ -1,197 +0,0 @@ -.. _Modules: - - -discordSuperUtils Modules -========================= - -discordSuperUtils.Antispam module ---------------------------------- - -.. automodule:: discordSuperUtils.Antispam - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Ban module ----------------------------- - -.. automodule:: discordSuperUtils.Ban - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Base module ------------------------------ - -.. automodule:: discordSuperUtils.Base - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Birthday module ---------------------------------- - -.. automodule:: discordSuperUtils.Birthday - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.CommandHinter module --------------------------------------- - -.. automodule:: discordSuperUtils.CommandHinter - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Convertors module ------------------------------------ - -.. automodule:: discordSuperUtils.Convertors - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Database module ---------------------------------- - -.. automodule:: discordSuperUtils.Database - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Economy module --------------------------------- - -.. automodule:: discordSuperUtils.Economy - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.FiveM module ------------------------------- - -.. automodule:: discordSuperUtils.FiveM - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Imaging module --------------------------------- - -.. automodule:: discordSuperUtils.Imaging - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Infractions module ------------------------------------- - -.. automodule:: discordSuperUtils.Infractions - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.InviteTracker module --------------------------------------- - -.. automodule:: discordSuperUtils.InviteTracker - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Kick module ------------------------------ - -.. automodule:: discordSuperUtils.Kick - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Leveling module ---------------------------------- - -.. automodule:: discordSuperUtils.Leveling - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.MessageFilter module --------------------------------------- - -.. automodule:: discordSuperUtils.MessageFilter - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Music module ------------------------------- - -.. automodule:: discordSuperUtils.Music - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Mute module ------------------------------ - -.. automodule:: discordSuperUtils.Mute - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Paginator module ----------------------------------- - -.. automodule:: discordSuperUtils.Paginator - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Prefix module -------------------------------- - -.. automodule:: discordSuperUtils.Prefix - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Punishments module ------------------------------------- - -.. automodule:: discordSuperUtils.Punishments - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.ReactionRoles module --------------------------------------- - -.. automodule:: discordSuperUtils.ReactionRoles - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Spotify module --------------------------------- - -.. automodule:: discordSuperUtils.Spotify - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Template module ---------------------------------- - -.. automodule:: discordSuperUtils.Template - :members: - :undoc-members: - :show-inheritance: - -discordSuperUtils.Youtube module --------------------------------- - -.. automodule:: discordSuperUtils.Youtube - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/modules.rst b/docs/source/modules.rst deleted file mode 100644 index 5f2ca5b..0000000 --- a/docs/source/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -discordSuperUtils -================= - -.. toctree:: - :maxdepth: 4 - - discordSuperUtils From 34e04b930f37203741348a2d963c3051d851fa0b Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Sat, 16 Oct 2021 19:32:52 +0300 Subject: [PATCH 02/21] conf --- docs/conf.py | 78 ---------------------------------------------------- 1 file changed, 78 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f8163f4..e69de29 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,78 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath("../discordSuperUtils")) - - -# -- Project information ----------------------------------------------------- - -project = "discordSuperUtils" -copyright = "2021, Adam7100 & Koyashie" -author = "Adam7100 & Koyashie" - -# The full version, including alpha/beta/rc tags -release = "0.1.9" - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.doctest", - "sphinx.ext.intersphinx", - "sphinx.ext.todo", - "sphinx.ext.coverage", - "sphinx.ext.mathjax", - "sphinx.ext.ifconfig", - "sphinx.ext.viewcode", - "sphinx.ext.githubpages", - "sphinx.ext.napoleon", - "sphinx.ext.autosectionlabel", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - - -rst_prolog = """ -.. |coro| replace:: This function is a |coroutine_link|_. -.. |coroutine_link| replace:: *coroutine* -.. _coroutine_link: https://docs.python.org/3/library/asyncio-task.html#coroutine -""" From 88e05185d7191bdf87abdd8c0f24d24e4ca8ab34 Mon Sep 17 00:00:00 2001 From: Yash Date: Sat, 16 Oct 2021 12:38:55 -0400 Subject: [PATCH 03/21] Black formatted and added Move method to MusicManager --- discordSuperUtils/birthday.py | 2 +- discordSuperUtils/music/music.py | 32 ++++++++++++++++++++++++++++++-- discordSuperUtils/music/queue.py | 2 +- examples/lavalinkmusic.py | 7 ++++--- examples/music.py | 17 ++++++++++++----- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/discordSuperUtils/birthday.py b/discordSuperUtils/birthday.py index 9d7f837..afc7ac3 100644 --- a/discordSuperUtils/birthday.py +++ b/discordSuperUtils/birthday.py @@ -269,7 +269,7 @@ async def get_upcoming(self, guild: discord.Guild) -> List[BirthdayMember]: self._check_database() member_data = await self.database.select( - self.tables["birthdays"], [],{"guild": guild.id}, fetchall=True + self.tables["birthdays"], [], {"guild": guild.id}, fetchall=True ) birthdays = {} diff --git a/discordSuperUtils/music/music.py b/discordSuperUtils/music/music.py index 73ab4c4..1577ac5 100644 --- a/discordSuperUtils/music/music.py +++ b/discordSuperUtils/music/music.py @@ -888,9 +888,11 @@ async def shuffle(self, ctx: commands.Context) -> Optional[bool]: if queue.shuffle: queue.original_queue = queue.queue - play_queue = queue.queue[queue.pos + 1:] + play_queue = queue.queue[queue.pos + 1 :] shuffled_queue = random.sample(play_queue, len(play_queue)) - queue.queue = queue.queue[:queue.pos] + [queue.now_playing] + shuffled_queue + queue.queue = ( + queue.queue[: queue.pos] + [queue.now_playing] + shuffled_queue + ) return queue.shuffle @@ -964,3 +966,29 @@ def parse_duration(duration: Union[str, float], hour_format: bool = True) -> str time_format = "%H:%M:%S" if hour_format else "%M:%S" return time.strftime(time_format, time.gmtime(round(duration))) + + @ensure_connection(check_queue=True) + async def move( + self, ctx: commands.Context, player_index: int, new_index: int + ) -> Optional[Player]: + """ + + :param player_index: The index of the player that you want to move. + :param new_index: The index you want to move the player to. + :param ctx: Context to fetch the queue from + :return: The player object that was moved + :rtype: Optional[Player] + """ + + queue = await self.get_queue(ctx) + player_index += queue.pos + new_index += queue.pos + + if new_index > len(queue.queue) or player_index > len(queue.queue): + return await self.call_event( + "on_music_error", ctx, InvalidSkipIndex("Skip index is invalid") + ) + + player = queue.remove(player_index) + queue.queue.insert(new_index, player) + return player diff --git a/discordSuperUtils/music/queue.py b/discordSuperUtils/music/queue.py index 806c462..3692803 100644 --- a/discordSuperUtils/music/queue.py +++ b/discordSuperUtils/music/queue.py @@ -28,7 +28,7 @@ class QueueManager: "vote_skips", "played_history", "queue_loop_start", - "original_queue" + "original_queue", ) def __init__(self, volume: float, queue: List[Player]): diff --git a/examples/lavalinkmusic.py b/examples/lavalinkmusic.py index 25a6b14..d5acace 100644 --- a/examples/lavalinkmusic.py +++ b/examples/lavalinkmusic.py @@ -9,11 +9,12 @@ client_secret = "" bot = commands.Bot(command_prefix="-") -MusicManager = LavalinkMusicManager(bot, spotify_support=False) +# MusicManager = LavalinkMusicManager(bot, spotify_support=False) -# = LavaLinkMusicManager(bot, client_id=client_id, -# client_secret=client_secret, spotify_support=True) +MusicManager = LavalinkMusicManager( + bot, client_id=client_id, client_secret=client_secret, spotify_support=True +) # if using spotify support use this instead ^^^ diff --git a/examples/music.py b/examples/music.py index f98f466..08f5f0a 100644 --- a/examples/music.py +++ b/examples/music.py @@ -8,11 +8,13 @@ client_secret = "" bot = commands.Bot(command_prefix="-") -MusicManager = MusicManager(bot, spotify_support=False) +# MusicManager = MusicManager(bot, spotify_support=False) -# MusicManager = MusicManager(bot, client_id=client_id, -# client_secret=client_secret, spotify_support=True) +MusicManager = MusicManager( + bot, client_id=client_id, client_secret=client_secret, spotify_support=True +) + # if using spotify support use this instead ^^^ @@ -40,8 +42,8 @@ async def on_play(ctx, player): @bot.event async def on_ready(): - database = discordSuperUtils.DatabaseManager.connect(...) - await MusicManager.connect_to_database(database, ["playlists"]) + # database = discordSuperUtils.DatabaseManager.connect(...) + # await MusicManager.connect_to_database(database, ["playlists"]) print("Music manager is ready.", bot.user) @@ -319,4 +321,9 @@ async def ls(ctx): await ctx.send(loop_status) +@bot.command() +async def move(ctx, player_index: int, index: int): + await MusicManager.move(ctx, player_index, index) + + bot.run("token") From f2f9622439e6bc53b4699bd590034c48df51375a Mon Sep 17 00:00:00 2001 From: adam757521 Date: Sun, 17 Oct 2021 22:42:28 +0300 Subject: [PATCH 04/21] Added guild docstring Added player_queue property to queue Fixed clear method Added remove_member method --- discordSuperUtils/music/music.py | 1 + discordSuperUtils/music/queue.py | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/discordSuperUtils/music/music.py b/discordSuperUtils/music/music.py index 73ab4c4..1a4b940 100644 --- a/discordSuperUtils/music/music.py +++ b/discordSuperUtils/music/music.py @@ -123,6 +123,7 @@ async def cleanup( Cleans up after a guild. + :param discord.Guild guild: The guild to cleanup. :param Optional[discord.VoiceClient] voice_client: The voice client. :return: None :rtype: None diff --git a/discordSuperUtils/music/queue.py b/discordSuperUtils/music/queue.py index 806c462..83c3d86 100644 --- a/discordSuperUtils/music/queue.py +++ b/discordSuperUtils/music/queue.py @@ -1,12 +1,12 @@ from __future__ import annotations -import random from typing import List, TYPE_CHECKING, Union, Any from .enums import Loops if TYPE_CHECKING: from ..youtube import YoutubeClient + import discord from .player import Player @@ -86,6 +86,17 @@ def is_finished(self) -> bool: return self.pos >= len(self.queue) + @property + def player_queue(self) -> List[Player]: + """ + Returns the player queue. + + :return: The list of players + :rtype: List[Player] + """ + + return self.queue[self.pos + 1:] + @property def now_playing(self) -> Player: """ @@ -128,7 +139,7 @@ def clear(self) -> None: :rtype: None """ - self.queue.clear() + self.queue = self.queue[:self.pos + 1] def remove(self, index: int) -> Union[Player, Any]: """ @@ -142,6 +153,23 @@ def remove(self, index: int) -> Union[Player, Any]: return self.queue.pop(index) + def remove_member(self, member: discord.Member) -> List[Player]: + """ + Removes the member from the queue. + + :param discord.Member member: The member to remove from the queue. + :return: The players removed. + :rtype: None + """ + + removed_players = [] + for player in self.player_queue: + if player.requester == member: + removed_players.append(player) + self.queue.remove(player) + + return removed_players + def cleanup(self): """ Clears the queue. From 5be3d89043f63709ae11b7d9c41708f3197dda38 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Sun, 17 Oct 2021 22:44:03 +0300 Subject: [PATCH 05/21] Black formatted --- discordSuperUtils/music/queue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discordSuperUtils/music/queue.py b/discordSuperUtils/music/queue.py index 9be93f4..81de049 100644 --- a/discordSuperUtils/music/queue.py +++ b/discordSuperUtils/music/queue.py @@ -95,7 +95,7 @@ def player_queue(self) -> List[Player]: :rtype: List[Player] """ - return self.queue[self.pos + 1:] + return self.queue[self.pos + 1 :] @property def now_playing(self) -> Player: @@ -139,7 +139,7 @@ def clear(self) -> None: :rtype: None """ - self.queue = self.queue[:self.pos + 1] + self.queue = self.queue[: self.pos + 1] def remove(self, index: int) -> Union[Player, Any]: """ From 0bd1e23a289e12fe7cb506ddcc49c3031451c179 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Sun, 17 Oct 2021 23:01:39 +0300 Subject: [PATCH 06/21] Moved database checks to decorator Deleted applications.py --- discordSuperUtils/applications.py | 17 ----------------- discordSuperUtils/ban.py | 7 +++---- discordSuperUtils/birthday.py | 10 ++++------ discordSuperUtils/infractions.py | 6 ++---- discordSuperUtils/invitetracker.py | 9 +++------ discordSuperUtils/leveling.py | 19 +++++++------------ discordSuperUtils/messagefilter.py | 2 -- discordSuperUtils/mute.py | 6 ++++-- discordSuperUtils/prefix.py | 5 +++-- discordSuperUtils/punishments.py | 2 ++ discordSuperUtils/reactionroles.py | 8 +++----- 11 files changed, 31 insertions(+), 60 deletions(-) delete mode 100644 discordSuperUtils/applications.py diff --git a/discordSuperUtils/applications.py b/discordSuperUtils/applications.py deleted file mode 100644 index 778ec70..0000000 --- a/discordSuperUtils/applications.py +++ /dev/null @@ -1,17 +0,0 @@ -from .base import DatabaseChecker -from discord.ext import commands - - -class ApplicationManager(DatabaseChecker): - def __init__(self, bot: commands.Bot): - # super().__init__( - # [ - # { - # "guild": "snowflake", - # "member": "snowflake", - # "" - # } - # ], - # ["applications_questions"] - # ) - pass diff --git a/discordSuperUtils/ban.py b/discordSuperUtils/ban.py index f1200c1..f708e25 100644 --- a/discordSuperUtils/ban.py +++ b/discordSuperUtils/ban.py @@ -47,6 +47,7 @@ def __init__(self, bot: commands.Bot): async def _on_database_connect(self): self.bot.loop.create_task(self.__check_bans()) + @DatabaseChecker.uses_database async def get_banned_members(self) -> List[Dict[str, Any]]: """ |coro| @@ -127,6 +128,7 @@ async def get_ban( if x.user.id == member.id: return x.user + @DatabaseChecker.uses_database async def unban( self, member: Union[discord.Member, discord.User], guild: discord.Guild = None ) -> bool: @@ -142,8 +144,6 @@ async def unban( :raises: UnbanFailure: Cannot unban a discord.User without a guild. """ - self._check_database() - if isinstance(member, discord.User) and not guild: raise UnbanFailure("Cannot unban a discord.User without a guild.") @@ -176,6 +176,7 @@ async def __handle_unban( if await self.unban(member): await self.call_event("on_unban", member, reason) + @DatabaseChecker.uses_database async def ban( self, member: discord.Member, @@ -197,8 +198,6 @@ async def ban( :rtype: None """ - self._check_database() - await member.ban(reason=reason) if time_of_ban <= 0: diff --git a/discordSuperUtils/birthday.py b/discordSuperUtils/birthday.py index afc7ac3..c21ad85 100644 --- a/discordSuperUtils/birthday.py +++ b/discordSuperUtils/birthday.py @@ -202,6 +202,7 @@ def __init__(self, bot: commands.Bot): async def _on_database_connect(self): self.bot.loop.create_task(self.__detect_birthdays()) + @DatabaseChecker.uses_database async def create_birthday( self, member: discord.Member, member_birthday: float, timezone: str = "UTC" ) -> None: @@ -217,8 +218,6 @@ async def create_birthday( :rtype: None """ - self._check_database() - await self.database.insertifnotexists( self.tables["birthdays"], dict( @@ -230,6 +229,7 @@ async def create_birthday( {"guild": member.guild.id, "member": member.id}, ) + @DatabaseChecker.uses_database async def get_birthday(self, member: discord.Member) -> Optional[BirthdayMember]: """ |coro| @@ -241,8 +241,6 @@ async def get_birthday(self, member: discord.Member) -> Optional[BirthdayMember] :rtype: Optional[BirthdayMember] """ - self._check_database() - member_data = await self.database.select( self.tables["birthdays"], [], @@ -255,6 +253,7 @@ async def get_birthday(self, member: discord.Member) -> Optional[BirthdayMember] return None + @DatabaseChecker.uses_database async def get_upcoming(self, guild: discord.Guild) -> List[BirthdayMember]: """ |coro| @@ -266,8 +265,6 @@ async def get_upcoming(self, guild: discord.Guild) -> List[BirthdayMember]: :rtype: List[BirthdayMember] """ - self._check_database() - member_data = await self.database.select( self.tables["birthdays"], [], {"guild": guild.id}, fetchall=True ) @@ -314,6 +311,7 @@ def get_midnight_timezones() -> List[str]: if current_utc_time.astimezone(tz).utcoffset() in checks ] + @DatabaseChecker.uses_database async def get_members_with_birthday( self, timezones: List[str] ) -> List[Dict[str, Any]]: diff --git a/discordSuperUtils/infractions.py b/discordSuperUtils/infractions.py index 9bace77..961c7d4 100644 --- a/discordSuperUtils/infractions.py +++ b/discordSuperUtils/infractions.py @@ -104,11 +104,10 @@ def __init__(self, bot: commands.Bot): def add_punishments(self, punishments: List[Punishment]) -> None: self.punishments = punishments + @DatabaseChecker.uses_database async def warn( self, ctx: commands.Context, member: discord.Member, reason: str ) -> Infraction: - self._check_database() - generated_id = str(uuid.uuid4()) await self.database.insert( self.tables["infractions"], @@ -136,14 +135,13 @@ async def punish( await self.warn(ctx, member, punishment.punishment_reason) await self.call_event("on_punishment", ctx, member, punishment) + @DatabaseChecker.uses_database async def get_infractions( self, member: discord.Member, infraction_id: str = None, from_timestamp: Union[int, float] = 0, ) -> List[Infraction]: - self._check_database() - checks = {"guild": member.guild.id, "member": member.id} if infraction_id: checks["id"] = infraction_id diff --git a/discordSuperUtils/invitetracker.py b/discordSuperUtils/invitetracker.py index a9fcbf1..df9bc5f 100644 --- a/discordSuperUtils/invitetracker.py +++ b/discordSuperUtils/invitetracker.py @@ -64,11 +64,10 @@ async def get_invite(self, member: discord.Member) -> Optional[discord.Invite]: await self.__update_guild_cache(member.guild) return inv + @DatabaseChecker.uses_database async def get_members_invited( self, user: Union[discord.User, discord.Member], guild: discord.Guild ): - self._check_database() - invited_members = await self.database.select( self.tables["invites"], ["members_invited"], @@ -92,14 +91,13 @@ async def fetch_inviter( inviter = invite.guild.get_member(invite.inviter.id) return inviter if inviter else await self.bot.get_user(invite.inviter.id) + @DatabaseChecker.uses_database async def register_invite( self, invite: discord.Invite, member: discord.Member, inviter: Union[discord.Member, discord.User], ) -> None: - self._check_database() - invited_members = await self.get_members_invited(inviter, invite.guild) if member.id in invited_members: return @@ -139,7 +137,6 @@ async def __cleanup_invite(self, invite: discord.Invite) -> None: async def __cleanup_guild_cache(self, guild: discord.Guild) -> None: self.cache.pop(guild.id) + @DatabaseChecker.uses_database def get_user_info(self, member: discord.Member) -> InviteAccount: - self._check_database() - return InviteAccount(self, member) diff --git a/discordSuperUtils/leveling.py b/discordSuperUtils/leveling.py index a4bd043..f3f2ee8 100644 --- a/discordSuperUtils/leveling.py +++ b/discordSuperUtils/leveling.py @@ -109,6 +109,7 @@ def __init__( self.cooldown_members = {} self.add_event(self.on_database_connect) + @DatabaseChecker.uses_database async def set_interval(self, guild: discord.Guild, interval: int = None) -> None: """ Set the role interval of a guild. @@ -132,6 +133,7 @@ async def set_interval(self, guild: discord.Guild, interval: int = None) -> None self.tables["roles"], sql_insert_data, {"guild": guild.id}, sql_insert_data ) + @DatabaseChecker.uses_database async def get_roles(self, guild: discord.Guild) -> List[int]: """ Returns the role IDs of the guild. @@ -142,8 +144,6 @@ async def get_roles(self, guild: discord.Guild) -> List[int]: :rtype: List[int] """ - self._check_database() - return [ role["role"] for role in await self.database.select( @@ -151,6 +151,7 @@ async def get_roles(self, guild: discord.Guild) -> List[int]: ) ] + @DatabaseChecker.uses_database async def set_roles( self, guild: discord.Guild, roles: Iterable[discord.Role] ) -> None: @@ -165,8 +166,6 @@ async def set_roles( :rtype: None """ - self._check_database() - await self.database.delete(self.tables["role_list"], {"guild": guild.id}) for role in roles: @@ -181,9 +180,8 @@ async def on_database_connect(self): def generate_checks(member: discord.Member): return {"guild": member.guild.id, "member": member.id} + @DatabaseChecker.uses_database async def __handle_experience(self, message): - self._check_database() - if not message.guild or message.author.bot: return @@ -235,9 +233,8 @@ async def __handle_experience(self, message): if roles: await message.author.add_roles(*roles) + @DatabaseChecker.uses_database async def create_account(self, member): - self._check_database() - await self.database.insertifnotexists( self.tables["xp"], dict( @@ -246,9 +243,8 @@ async def create_account(self, member): self.generate_checks(member), ) + @DatabaseChecker.uses_database async def get_account(self, member): - self._check_database() - member_data = await self.database.select( self.tables["xp"], [], self.generate_checks(member), True ) @@ -258,9 +254,8 @@ async def get_account(self, member): return None + @DatabaseChecker.uses_database async def get_leaderboard(self, guild: discord.Guild): - self._check_database() - guild_info = sorted( await self.database.select( self.tables["xp"], [], {"guild": guild.id}, True diff --git a/discordSuperUtils/messagefilter.py b/discordSuperUtils/messagefilter.py index 26db0ad..3aa19f1 100644 --- a/discordSuperUtils/messagefilter.py +++ b/discordSuperUtils/messagefilter.py @@ -40,8 +40,6 @@ def generate(self, message: discord.Message) -> Union[bool, Any]: :rtype: Union[bool, Any] """ - pass - class DefaultMessageResponseGenerator(MessageResponseGenerator): URL_RE = re.compile( diff --git a/discordSuperUtils/mute.py b/discordSuperUtils/mute.py index f89f29c..016b219 100644 --- a/discordSuperUtils/mute.py +++ b/discordSuperUtils/mute.py @@ -50,6 +50,7 @@ async def on_database_connect(self): self.bot.loop.create_task(self.__check_mutes()) self.bot.add_listener(self.on_member_join) + @DatabaseChecker.uses_database async def get_muted_members(self) -> List[Dict[str, Any]]: """ |coro| @@ -66,6 +67,7 @@ async def get_muted_members(self) -> List[Dict[str, Any]]: if x["timestamp_of_unmute"] <= datetime.utcnow().timestamp() ] + @DatabaseChecker.uses_database async def on_member_join(self, member: discord.Member) -> None: """ |coro| @@ -189,6 +191,7 @@ async def __handle_unmute( if await self.unmute(member): await self.call_event("on_unmute", member, reason) + @DatabaseChecker.uses_database async def mute( self, member: discord.Member, @@ -211,8 +214,6 @@ async def mute( :rtype: None """ - self._check_database() - muted_role = discord.utils.get(member.guild.roles, name="Muted") if not muted_role: muted_role = await member.guild.create_role( @@ -243,6 +244,7 @@ async def mute( self.bot.loop.create_task(self.__handle_unmute(time_of_mute, member, reason)) + @DatabaseChecker.uses_database async def unmute(self, member: discord.Member) -> Optional[bool]: """ |coro| diff --git a/discordSuperUtils/prefix.py b/discordSuperUtils/prefix.py index 9dc18be..3678d00 100644 --- a/discordSuperUtils/prefix.py +++ b/discordSuperUtils/prefix.py @@ -30,6 +30,7 @@ def __init__( self.prefix_cache = {} bot.command_prefix = self.__get_prefix + @DatabaseChecker.uses_database async def get_prefix(self, guild: Union[discord.Guild, Any]) -> Iterable[str]: """ |coro| @@ -53,6 +54,7 @@ async def get_prefix(self, guild: Union[discord.Guild, Any]) -> Iterable[str]: return prefix + @DatabaseChecker.uses_database async def delete_prefix(self, guild: discord.Guild) -> None: """ |coro| @@ -67,6 +69,7 @@ async def delete_prefix(self, guild: discord.Guild) -> None: self.prefix_cache.pop(guild.id) await self.database.delete(self.tables["prefixes"], {"guild": guild.id}) + @DatabaseChecker.uses_database async def set_prefix(self, guild: discord.Guild, prefix: str) -> None: """ |coro| @@ -92,8 +95,6 @@ async def set_prefix(self, guild: discord.Guild, prefix: str) -> None: ) async def __get_prefix(self, bot, message: discord.Message) -> Tuple[str]: - self._check_database() - if not message.guild: return ( commands.when_mentioned_or(*self.default_prefixes)(bot, message) diff --git a/discordSuperUtils/punishments.py b/discordSuperUtils/punishments.py index 059b8f0..530509c 100644 --- a/discordSuperUtils/punishments.py +++ b/discordSuperUtils/punishments.py @@ -55,6 +55,8 @@ async def punish( self, ctx: commands.Context, member: discord.Member, punishment: Punishment ) -> None: """ + |coro| + The manager's punish function. :param ctx: The context of the punishments. diff --git a/discordSuperUtils/reactionroles.py b/discordSuperUtils/reactionroles.py index 55f39c5..f6319ef 100644 --- a/discordSuperUtils/reactionroles.py +++ b/discordSuperUtils/reactionroles.py @@ -35,9 +35,8 @@ def get_emoji_sql(emoji): return emoji_string + @DatabaseChecker.uses_database async def __handle_reactions(self, payload): - self._check_database() - if payload.user_id == self.bot.user.id: return @@ -48,7 +47,7 @@ async def __handle_reactions(self, payload): } reaction_role_data = await self.database.select( - self.tables["reaction_roles"], self.tables_column_data[0], database_checks + self.tables["reaction_roles"], [], database_checks ) if not reaction_role_data: @@ -76,11 +75,10 @@ async def __handle_reactions(self, payload): elif reaction_role_data["remove_on_reaction"] == 1: await member.remove_roles(role) + @DatabaseChecker.uses_database async def create_reaction( self, guild, message, role, emoji, remove_on_reaction: int ): - self._check_database() - await self.database.insertifnotexists( self.tables["reaction_roles"], dict( From 5e5412906ed2a24e1b1ccd3340bca5149d0fb22b Mon Sep 17 00:00:00 2001 From: adam757521 Date: Sun, 17 Oct 2021 23:10:45 +0300 Subject: [PATCH 07/21] Removed slash example --- examples/slash.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 examples/slash.py diff --git a/examples/slash.py b/examples/slash.py deleted file mode 100644 index 4ce43ad..0000000 --- a/examples/slash.py +++ /dev/null @@ -1,20 +0,0 @@ -import discord -from discord.ext import commands - -import discordSuperUtils - -bot = commands.Bot(command_prefix="-", intents=discord.Intents.all()) -slash = discordSuperUtils.SlashManager(bot) - - -@bot.event -async def on_ready(): - print("Slash manager is ready.", bot.user) - - -@slash.command() -async def ping(ctx): - await ctx.reply(f"Pong! ping is {bot.latency * 1000}ms") - - -bot.run("token") From 3f439f9cad9ad333903fa0fd43ff621edf9e919f Mon Sep 17 00:00:00 2001 From: adam757521 Date: Mon, 18 Oct 2021 08:19:50 +0300 Subject: [PATCH 08/21] Fixed commandhinter alias error --- discordSuperUtils/commandhinter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discordSuperUtils/commandhinter.py b/discordSuperUtils/commandhinter.py index 6684255..2ff9b0c 100644 --- a/discordSuperUtils/commandhinter.py +++ b/discordSuperUtils/commandhinter.py @@ -91,12 +91,12 @@ def command_names(self) -> List[str]: for command in self.bot.commands: if isinstance(command, commands.Group): - names += [command.name] + command.aliases + names += [command.name] + list(command.aliases) for inner_command in command.commands: - names += [inner_command.name] + inner_command.aliases + names += [inner_command.name] + list(inner_command.aliases) else: - names += [command.name] + command.aliases + names += [command.name] + list(command.aliases) return names From 8390de67c8a8f025da90a7e16ce72d479c637891 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Tue, 19 Oct 2021 18:08:35 +0300 Subject: [PATCH 09/21] Made almost all classes dataclasses Renamed infraction_id to id Made make_infraction_embed an async Made on_member_join in mute use the default function and not the decorator as it breaks the coro check Made paginator generate_embeds check if footer before setting the value --- discordSuperUtils/base.py | 29 ++++--- discordSuperUtils/birthday.py | 30 +++---- discordSuperUtils/economy.py | 20 +++-- discordSuperUtils/fivem.py | 67 +++------------- discordSuperUtils/infractions.py | 51 +++++------- discordSuperUtils/invitetracker.py | 12 +-- discordSuperUtils/leveling.py | 16 ++-- discordSuperUtils/music/lavalink/equalizer.py | 29 +++---- discordSuperUtils/music/playlist.py | 78 +++++-------------- discordSuperUtils/mute.py | 3 +- discordSuperUtils/paginator.py | 9 ++- discordSuperUtils/punishments.py | 22 +++--- examples/moderation.py | 20 ++--- 13 files changed, 141 insertions(+), 245 deletions(-) diff --git a/discordSuperUtils/base.py b/discordSuperUtils/base.py index 0b076ba..feec0f8 100644 --- a/discordSuperUtils/base.py +++ b/discordSuperUtils/base.py @@ -1,7 +1,9 @@ from __future__ import annotations import asyncio +import dataclasses import inspect +from dataclasses import dataclass from typing import ( List, Any, @@ -70,16 +72,17 @@ class DatabaseNotConnected(Exception): """Raises an error when the user tries to use a method of a manager without a database connected to it.""" +@dataclass class CacheBased: """ Represents a cache manager that manages member cache. """ - def __init__(self, bot: commands.Bot, wipe_cache_delay: timedelta): - self.wipe_cache_delay = wipe_cache_delay - self.bot = bot - self._cache = {} + bot: commands.Bot + wipe_cache_delay: timedelta + _cache: dict = dataclasses.field(default_factory=dict, init=False, repr=False) + def __post_init__(self): asyncio.get_event_loop().create_task(self.__wipe_cache()) async def __wipe_cache(self) -> None: @@ -242,13 +245,13 @@ def checks(msg): return answers, timed_out +@dataclass class EventManager: """ An event manager that manages events for managers. """ - def __init__(self): - self.events = {} + events: dict = dataclasses.field(default_factory=dict, init=False) async def call_event(self, name: str, *args, **kwargs) -> None: """ @@ -412,20 +415,16 @@ def decorator(func): return decorator +@dataclass class DatabaseChecker(EventManager): """ A database checker which makes sure the database is connected to a manager and handles the table creation. """ - def __init__( - self, tables_column_data: List[Dict[str, str]], table_identifiers: List[str] - ): - super().__init__() - - self.database: Optional[Database] = None - self.table_identifiers = table_identifiers - self.tables = {} - self.tables_column_data = tables_column_data + tables_column_data: List[Dict[str, str]] + table_identifiers: List[str] + database: Optional[Database] = dataclasses.field(default=None, init=False) + tables: Dict[str, str] = dataclasses.field(default_factory=dict, init=False) @staticmethod def uses_database(func): diff --git a/discordSuperUtils/birthday.py b/discordSuperUtils/birthday.py index c21ad85..d79d766 100644 --- a/discordSuperUtils/birthday.py +++ b/discordSuperUtils/birthday.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from dataclasses import dataclass from datetime import datetime, timedelta from typing import Dict, List, Optional, Any @@ -14,40 +15,27 @@ __all__ = ("PartialBirthdayMember", "BirthdayManager", "BirthdayMember") +@dataclass class PartialBirthdayMember: """ Represents a partial birthday member. """ - __slots__ = ("member", "birthday_date", "timezone") - - def __init__(self, member: discord.Member, birthday_date: datetime, timezone: str): - """ - :param discord.Member member: The member. - :param datetime birthday_date: The birthday date in a datetime. - :param str timezone: The timezone. - """ - - self.member = member - self.birthday_date = birthday_date - self.timezone = timezone + member: discord.Member + birthday_date: datetime + timezone: str +@dataclass class BirthdayMember: """ Represents a birthday member. """ - __slots__ = ("birthday_manager", "member", "table") - - def __init__(self, birthday_manager: BirthdayManager, member: discord.Member): - """ - :param BirthdayManager birthday_manager: The birthday manager. - :param discord.Member member: The member. - """ + birthday_manager: BirthdayManager + member: discord.Member - self.birthday_manager = birthday_manager - self.member = member + def __post_init__(self): self.table = self.birthday_manager.tables["birthdays"] @property diff --git a/discordSuperUtils/economy.py b/discordSuperUtils/economy.py index 148b6e1..1d10fec 100644 --- a/discordSuperUtils/economy.py +++ b/discordSuperUtils/economy.py @@ -1,18 +1,22 @@ +from dataclasses import dataclass + import discord from typing import List, Optional from .base import DatabaseChecker +from .database import Database +@dataclass class EconomyAccount: - def __init__(self, guild: int, member: int, database, table): - self.guild = guild - self.member = member - self.database = database - self.table = table - - def __str__(self): - return f"" + """ + Represents an EconomyAccount. + """ + + guild: int + member: int + database: Database + table: str @property def __checks(self): diff --git a/discordSuperUtils/fivem.py b/discordSuperUtils/fivem.py index 360c6b3..35dd847 100644 --- a/discordSuperUtils/fivem.py +++ b/discordSuperUtils/fivem.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass from typing import Dict, List, Optional import aiohttp @@ -13,31 +14,16 @@ class ServerNotFound(Exception): """Raises an error when a server is invalid or offline.""" +@dataclass class FiveMPlayer: """ Represents a FiveM player. """ - __slots__ = ("player_id", "id", "identifiers", "name", "ping") - - def __init__(self, id_: int, identifiers: Dict[str, str], name: str, ping: int): - """ - :param int id_: The player ID. - :param Dict[str, str] identifiers: The player identifiers. - :param str name: The player name. - :param int ping: The player ping. - """ - - self.id = id_ - self.identifiers = identifiers - self.name = name - self.ping = ping - - def __str__(self): - return f"" - - def __repr__(self): - return f"" + id: int + identifiers: Dict[str, str] + name: str + ping: int @classmethod def from_dict(cls, player_dict: dict) -> FiveMPlayer: @@ -55,46 +41,17 @@ def from_dict(cls, player_dict: dict) -> FiveMPlayer: ) +@dataclass class FiveMServer: """ Represents a FiveM server. """ - __slots__ = ("ip", "resources", "players", "name", "variables") - - def __init__( - self, - ip: str, - resources: List[str], - players: List[FiveMPlayer], - name: str, - variables: Dict[str, str], - ): - """ - :param str ip: The server IP. - :param List[str] resources: The server resources. - :param List[FiveMPlayer] players: The server players. - :param str name: The server name. - :param Dict[str, str] variables: The server variables. - """ - - self.ip = ip - self.resources = resources - self.players = players - self.name = name - self.variables = variables - - def __str__(self): - return f"" - - def __repr__(self): - return ( - f"" - ) + ip: str + resources: List[str] + players: List[FiveMPlayer] + name: str + variables: Dict[str, str] @classmethod async def fetch(cls, ip: str) -> Optional[FiveMServer]: diff --git a/discordSuperUtils/infractions.py b/discordSuperUtils/infractions.py index 961c7d4..0b17813 100644 --- a/discordSuperUtils/infractions.py +++ b/discordSuperUtils/infractions.py @@ -1,6 +1,7 @@ from __future__ import annotations import uuid +from dataclasses import dataclass from datetime import datetime from typing import List, TYPE_CHECKING, Optional, Dict, Union @@ -16,71 +17,59 @@ __all__ = ("PartialInfraction", "Infraction", "InfractionManager") +@dataclass class PartialInfraction: """ A partial infraction. """ - def __init__( - self, - member: discord.Member, - infraction_id: str, - reason: str, - date_of_infraction: datetime, - ): - self.member = member - self.infraction_id = infraction_id - self.reason = reason - self.date_of_infraction = date_of_infraction + member: discord.Member + id: str + reason: str + date_of_infraction: datetime +@dataclass class Infraction: """ An infraction object. """ - def __init__( - self, database, table: str, member: discord.Member, infraction_id: str - ): - self.member = member - self.database = database - self.table = table - self.infraction_id = infraction_id - - def __str__(self): - return f"" + infraction_manager: InfractionManager + member: discord.Member + id: str - def __repr__(self): - return f"" + def __post_init__(self): + self.table = self.infraction_manager.tables["infractions"] @property def __checks(self) -> Dict[str, int]: return { "guild": self.member.guild.id, "member": self.member.id, - "id": self.infraction_id, + "id": self.id, } async def datetime(self) -> Optional[datetime]: - timestamp_data = await self.database.select( + timestamp_data = await self.infraction_manager.database.select( self.table, ["timestamp"], self.__checks ) if timestamp_data: return datetime.utcfromtimestamp(timestamp_data["timestamp"]) async def reason(self) -> Optional[str]: - reason_data = await self.database.select(self.table, ["reason"], self.__checks) + reason_data = await self.infraction_manager.database.select(self.table, ["reason"], self.__checks) if reason_data: return reason_data["reason"] async def set_reason(self, new_reason: str) -> None: - await self.database.update(self.table, {"reason": new_reason}, self.__checks) + await self.infraction_manager.database.update(self.table, {"reason": new_reason}, self.__checks) async def delete(self) -> PartialInfraction: partial = PartialInfraction( - self.member, self.infraction_id, await self.reason(), await self.datetime() + self.member, self.id, await self.reason(), await self.datetime() ) - await self.database.delete(self.table, self.__checks) + await self.infraction_manager.database.delete(self.table, self.__checks) return partial @@ -126,7 +115,7 @@ async def warn( await punishment.punishment_manager.punish(ctx, member, punishment) return Infraction( - self.database, self.tables["infractions"], member, generated_id + self, member, generated_id ) async def punish( @@ -152,7 +141,7 @@ async def get_infractions( return [ Infraction( - self.database, self.tables["infractions"], member, infraction["id"] + self, member, infraction["id"] ) for infraction in warnings if infraction["timestamp"] > from_timestamp diff --git a/discordSuperUtils/invitetracker.py b/discordSuperUtils/invitetracker.py index df9bc5f..f5f93c1 100644 --- a/discordSuperUtils/invitetracker.py +++ b/discordSuperUtils/invitetracker.py @@ -8,6 +8,7 @@ from __future__ import annotations +from dataclasses import dataclass from typing import TYPE_CHECKING, Union, Optional from .base import DatabaseChecker @@ -17,13 +18,14 @@ from discord.ext import commands +@dataclass class InviteAccount: - def __init__(self, invite_tracker: InviteTracker, member: discord.Member): - self.invite_tracker = invite_tracker - self.member = member + """ + Represents an InviteAccount. + """ - def __str__(self): - return f"" + invite_tracker: InviteTracker + member: discord.Member async def get_invited_users(self): return await self.invite_tracker.get_members_invited( diff --git a/discordSuperUtils/leveling.py b/discordSuperUtils/leveling.py index f3f2ee8..ca7f9be 100644 --- a/discordSuperUtils/leveling.py +++ b/discordSuperUtils/leveling.py @@ -2,6 +2,7 @@ import math import time +from dataclasses import dataclass from typing import Iterable, TYPE_CHECKING, List from .base import DatabaseChecker @@ -10,14 +11,17 @@ import discord +@dataclass class LevelingAccount: - def __init__(self, leveling_manager: LevelingManager, member: discord.Member): - self.leveling_manager = leveling_manager - self.table = self.leveling_manager.tables["xp"] - self.member = member + """ + Represents a LevelingAccount. + """ + + leveling_manager: LevelingManager + member: discord.Member - def __str__(self): - return f"" + def __post_init__(self): + self.table = self.leveling_manager.tables["xp"] @property def __checks(self): diff --git a/discordSuperUtils/music/lavalink/equalizer.py b/discordSuperUtils/music/lavalink/equalizer.py index bee707f..8d81641 100644 --- a/discordSuperUtils/music/lavalink/equalizer.py +++ b/discordSuperUtils/music/lavalink/equalizer.py @@ -1,34 +1,29 @@ from __future__ import annotations -from typing import List +from dataclasses import dataclass +from typing import List, Dict, Any __all__ = ("Equalizer",) +@dataclass class Equalizer: """ Represents a Equalizer that supports different voice effects. """ - __slots__ = ("eq", "raw", "name") + raw: List[float] + name: str - def __init__(self, *, levels: List[float], name: str = "CustomEqualizer"): - self.eq = [{"band": index, "gain": gain} for index, gain in enumerate(levels)] - self.raw = levels - - self.name = name - - def __str__(self): - return self.name - - def __repr__(self): - return f"" + @property + def eq(self) -> List[Dict[str, Any]]: + return [{"band": index, "gain": gain} for index, gain in enumerate(self.raw)] @classmethod def flat(cls) -> Equalizer: levels = [0.0 for i in range(15)] - return cls(levels=levels, name="Flat") + return cls(levels, "Flat") @classmethod def boost(cls): @@ -50,7 +45,7 @@ def boost(cls): -0.14, ] - return cls(levels=levels, name="Boost") + return cls(levels, "Boost") @classmethod def metal(cls): @@ -72,7 +67,7 @@ def metal(cls): 0.0, ] - return cls(levels=levels, name="Metal") + return cls(levels, "Metal") @classmethod def piano(cls): @@ -93,4 +88,4 @@ def piano(cls): -0.025, ] - return cls(levels=levels, name="Piano") + return cls(levels, "Piano") diff --git a/discordSuperUtils/music/playlist.py b/discordSuperUtils/music/playlist.py index 9f65daf..aa888b9 100644 --- a/discordSuperUtils/music/playlist.py +++ b/discordSuperUtils/music/playlist.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass from typing import Dict, List, Any, TYPE_CHECKING, Union, Optional from .enums import PlaylistType @@ -10,23 +11,18 @@ __slots__ = ("SpotifyTrack", "YoutubeAuthor", "Playlist", "UserPlaylist") +@dataclass class SpotifyTrack: """ Represents a spotify track. """ - __slots__ = ("name", "authors") - - def __init__(self, name: str, authors: List[str]) -> None: - self.name = name - self.authors = authors + name: str + authors: List[str] def __str__(self): return f"{self.name} by {self.authors[0]}" - def __repr__(self): - return f"<{self.__class__.__name__} name={self.name}, authors={self.authors}>" - @classmethod def from_dict(cls, dictionary: Dict[str, Any]) -> SpotifyTrack: """ @@ -43,20 +39,14 @@ def from_dict(cls, dictionary: Dict[str, Any]) -> SpotifyTrack: ) +@dataclass class YoutubeAuthor: """ Represents a YouTube author / channel. """ - def __init__(self, name: str, id_: str) -> None: - self.id = id_ - self.name = name - - def __str__(self): - return f"<{self.__class__.__name__} name={self.name}>" - - def __repr__(self): - return f"<{self.__class__.__name__} name={self.name}, id={self.id}>" + name: str + id: str @classmethod def from_dict(cls, dictionary: Dict[str, str]) -> YoutubeAuthor: @@ -71,33 +61,18 @@ def from_dict(cls, dictionary: Dict[str, str]) -> YoutubeAuthor: return cls(dictionary["name"], dictionary["id"]) +@dataclass class Playlist: """ Represents a playlist. Supports Spotify and YouTube. """ - __slots__ = ("title", "author", "songs", "url", "type") - - def __init__( - self, - title: str, - author: Optional[YoutubeAuthor], - songs: List[Union[str, SpotifyTrack]], - url: str, - type_: PlaylistType, - ) -> None: - self.title = title - self.author = author - self.songs = songs - self.url = url - self.type = type_ - - def __str__(self): - return f"<{self.__class__.__name__} title={self.title}, author={self.author}>" - - def __repr__(self): - return f"<{self.__class__.__name__} title={self.title}, author={self.author}>, total_songs={len(self.songs)}>" + title: str + author: Optional[YoutubeAuthor] + songs: List[Union[str, SpotifyTrack]] + url: str + type: PlaylistType @classmethod def from_youtube_dict(cls, dictionary: Dict[str, Any]) -> Playlist: @@ -136,25 +111,16 @@ def from_spotify_dict(cls, dictionary: Dict[str, Any]) -> Playlist: ) +@dataclass class UserPlaylist: """ Represents a playlist stored in the database. """ - __slots__ = ("owner", "id", "playlist", "music_manager", "table") - - def __init__( - self, - music_manager: MusicManager, - owner: discord.User, - id_: str, - playlist: Playlist, - ) -> None: - self.owner = owner - self.id = id_ - self.playlist = playlist - self.music_manager = music_manager - self.table = music_manager.tables["playlists"] + music_manager: MusicManager + owner: discord.User + id: str + playlist: Playlist async def delete(self) -> None: """ @@ -167,11 +133,5 @@ async def delete(self) -> None: """ await self.music_manager.database.delete( - self.table, {"user": self.owner.id, "id": self.id} + self.music_manager.tables["playlists"], {"user": self.owner.id, "id": self.id} ) - - def __str__(self): - return f"<{self.__class__.__name__} owner={self.owner}>" - - def __repr__(self): - return f"<{self.__class__.__name__} owner={self.owner}, id={self.id}, playlist={self.playlist}>" diff --git a/discordSuperUtils/mute.py b/discordSuperUtils/mute.py index 016b219..07722bb 100644 --- a/discordSuperUtils/mute.py +++ b/discordSuperUtils/mute.py @@ -67,7 +67,6 @@ async def get_muted_members(self) -> List[Dict[str, Any]]: if x["timestamp_of_unmute"] <= datetime.utcnow().timestamp() ] - @DatabaseChecker.uses_database async def on_member_join(self, member: discord.Member) -> None: """ |coro| @@ -81,6 +80,8 @@ async def on_member_join(self, member: discord.Member) -> None: :rtype: None """ + self._check_database() # Not using the decorator as it breaks the coroutine check + muted_members = [ x for x in await self.database.select( diff --git a/discordSuperUtils/paginator.py b/discordSuperUtils/paginator.py index 0daceee..9ac7baf 100644 --- a/discordSuperUtils/paginator.py +++ b/discordSuperUtils/paginator.py @@ -39,18 +39,19 @@ def generate_embeds( if (index + 1) % fields == 0: embed_index += 1 - for embed in embeds: - embed.set_footer(text=footer) + if footer: + for embed in embeds: + embed.set_footer(text=footer) return embeds class ButtonError(Exception): - __slots__ = () + pass class EmojiError(Exception): - __slots__ = () + pass class ButtonsPageManager: diff --git a/discordSuperUtils/punishments.py b/discordSuperUtils/punishments.py index 530509c..102a687 100644 --- a/discordSuperUtils/punishments.py +++ b/discordSuperUtils/punishments.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from dataclasses import dataclass from datetime import timedelta from typing import Optional, List, TYPE_CHECKING @@ -9,26 +10,21 @@ from discord.ext import commands +@dataclass class Punishment: """ A punishment class that is used for punishing members. """ - def __init__( - self, - punishment_manager, - punish_after: int = 3, - punishment_reason: str = "No reason specified.", - punishment_time: timedelta = timedelta(days=1), - ): - self.punishment_manager = punishment_manager - self.punish_after = punish_after - self.punishment_reason = punishment_reason - self.punishment_time = punishment_time + punishment_manager: Punisher + punish_after: int = 3 + punishment_reason: str = "No reason specified." + punishment_time: timedelta = timedelta(days=1) - if not issubclass(type(punishment_manager), Punisher): + def __post_init__(self): + if not issubclass(type(self.punishment_manager), Punisher): raise TypeError( - f"Manager of type '{type(punishment_manager)} is not supported.'" + f"Manager of type '{type(self.punishment_manager)} is not supported.'" ) diff --git a/examples/moderation.py b/examples/moderation.py index c3670d1..23a37cb 100644 --- a/examples/moderation.py +++ b/examples/moderation.py @@ -26,30 +26,30 @@ def make_removed_embeds(removed_infractions, member): discordSuperUtils.generate_embeds( [ f"**Reason: **{infraction.reason}\n" - f"**ID: **{infraction.infraction_id}\n" + f"**ID: **{infraction.id}\n" f"**Date of Infraction: **{infraction.date_of_infraction}" for infraction in removed_infractions ], title=f"Removed Infractions", fields=25, description=f"List of infractions that were removed from {member.mention}.", - ), + ) ) -def make_infraction_embed(member_infractions, member): +async def make_infraction_embed(member_infractions, member): return ( discordSuperUtils.generate_embeds( [ f"**Reason: **{await infraction.reason()}\n" - f"**ID: **{infraction.infraction_id}\n" + f"**ID: **{infraction.id}\n" f"**Date of Infraction: **{await infraction.datetime()}" for infraction in member_infractions ], title=f"Infractions of {member}", fields=25, description=f"List of {member.mention}'s infractions.", - ), + ) ) @@ -126,7 +126,7 @@ async def infractions(ctx, member: discord.Member): await discordSuperUtils.PageManager( ctx, - make_infraction_embed(member_infractions, member), + await make_infraction_embed(member_infractions, member), ).run() @@ -137,7 +137,7 @@ async def add(ctx, member: discord.Member, reason: str = "No reason specified.") embed = discord.Embed(title=f"{member} has been warned.", color=0x00FF00) embed.add_field(name="Reason", value=await infraction.reason(), inline=False) - embed.add_field(name="Infraction ID", value=infraction.infraction_id, inline=False) + embed.add_field(name="Infraction ID", value=infraction.id, inline=False) embed.add_field( name="Date of Infraction", value=str(await infraction.datetime()), inline=False ) @@ -165,7 +165,7 @@ async def get(ctx, member: discord.Member, infraction_id: str): ) embed.add_field(name="Reason", value=await infraction.reason(), inline=False) - embed.add_field(name="Infraction ID", value=infraction.infraction_id, inline=False) + embed.add_field(name="Infraction ID", value=infraction.id, inline=False) embed.add_field( name="Date of Infraction", value=str(await infraction.datetime()), inline=False ) @@ -186,7 +186,7 @@ async def get_before( await discordSuperUtils.PageManager( ctx, - make_infraction_embed(member_infractions, member), + await make_infraction_embed(member_infractions, member), ).run() @@ -223,7 +223,7 @@ async def remove(ctx, member: discord.Member, infraction_id: str): embed.add_field(name="Reason", value=removed_infraction.reason, inline=False) embed.add_field( - name="Infraction ID", value=removed_infraction.infraction_id, inline=False + name="Infraction ID", value=removed_infraction.id, inline=False ) embed.add_field( name="Date of Infraction", From 544e93c0bf920c8b8861ad825a6c0c8d7abb2579 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Tue, 19 Oct 2021 21:40:30 +0300 Subject: [PATCH 10/21] Made LavalinkPlayer a dataclass without an initializer. --- discordSuperUtils/music/lavalink/player.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/discordSuperUtils/music/lavalink/player.py b/discordSuperUtils/music/lavalink/player.py index e220734..1b91549 100644 --- a/discordSuperUtils/music/lavalink/player.py +++ b/discordSuperUtils/music/lavalink/player.py @@ -1,13 +1,17 @@ +from dataclasses import dataclass + import wavelink from .equalizer import Equalizer +@dataclass(init=False) class LavalinkPlayer(wavelink.Player): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + """ + Represents a LavalinkPlayer. + """ - self.equalizer = None + equalizer: Equalizer = Equalizer.flat() async def set_eq(self, equalizer: Equalizer) -> None: await self.node._websocket.send( From 82fcda5645777a8fa06c1f08b59ec4de9e2199a8 Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Tue, 19 Oct 2021 23:14:54 +0300 Subject: [PATCH 11/21] Made music use all intents Compacted if statements --- discordSuperUtils/music/music.py | 31 +++++++++++++------------------ examples/lavalinkmusic.py | 2 +- examples/music.py | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/discordSuperUtils/music/music.py b/discordSuperUtils/music/music.py index 2a765b5..d463d2b 100644 --- a/discordSuperUtils/music/music.py +++ b/discordSuperUtils/music/music.py @@ -158,6 +158,7 @@ async def add_playlist( """ playlist = await get_playlist(self.spotify, self.youtube, url) + if not playlist: return @@ -190,17 +191,15 @@ async def get_playlist( self.tables["playlists"], [], {"user": user.id, "id": playlist_id} ) - if not playlist: - return - - return UserPlaylist( - self, - user, - playlist_id, - await get_playlist(self.spotify, self.youtube, playlist["playlist_url"]) - if not partial - else None, - ) + if playlist: + return UserPlaylist( + self, + user, + playlist_id, + await get_playlist(self.spotify, self.youtube, playlist["playlist_url"]) + if not partial + else None, + ) @DatabaseChecker.uses_database async def get_user_playlists( @@ -234,9 +233,8 @@ async def _on_voice_state_update(self, member, before, after): voice_client = member.guild.voice_client channel_change = before.channel != after.channel - if member == self.bot.user and channel_change: - if before.channel: - await self.cleanup(voice_client, member.guild) + if member == self.bot.user and channel_change and before.channel: + await self.cleanup(voice_client, member.guild) elif voice_client and channel_change: voice_members = list( @@ -266,10 +264,7 @@ async def ensure_activity(self, ctx: commands.Context) -> None: await asyncio.sleep(self.inactivity_timeout) - if not ctx.voice_client: - return - - if ctx.voice_client.is_connected() and not ctx.voice_client.is_playing(): + if ctx.voice_client and ctx.voice_client.is_connected() and not ctx.voice_client.is_playing(): await self.cleanup(ctx.voice_client, ctx.guild) await self.call_event("on_inactivity_disconnect", ctx) diff --git a/examples/lavalinkmusic.py b/examples/lavalinkmusic.py index d5acace..6ec94f6 100644 --- a/examples/lavalinkmusic.py +++ b/examples/lavalinkmusic.py @@ -8,7 +8,7 @@ client_id = "" client_secret = "" -bot = commands.Bot(command_prefix="-") +bot = commands.Bot(command_prefix="-", intents=discord.Intents.all()) # MusicManager = LavalinkMusicManager(bot, spotify_support=False) diff --git a/examples/music.py b/examples/music.py index 08f5f0a..c37762f 100644 --- a/examples/music.py +++ b/examples/music.py @@ -7,7 +7,7 @@ client_id = "" client_secret = "" -bot = commands.Bot(command_prefix="-") +bot = commands.Bot(command_prefix="-", intents=discord.Intents.all()) # MusicManager = MusicManager(bot, spotify_support=False) From 9762d1ff649b446ba700d11d8bb709df123819da Mon Sep 17 00:00:00 2001 From: adam757521 Date: Mon, 25 Oct 2021 18:05:54 +0300 Subject: [PATCH 12/21] Black formatted Added muted_role_name argument Made footer default to "" and added display_page_in_footer --- discordSuperUtils/infractions.py | 16 +++++----- discordSuperUtils/music/music.py | 6 +++- discordSuperUtils/music/playlist.py | 3 +- discordSuperUtils/mute.py | 11 ++++--- discordSuperUtils/paginator.py | 16 +++++----- examples/moderation.py | 48 +++++++++++++---------------- 6 files changed, 52 insertions(+), 48 deletions(-) diff --git a/discordSuperUtils/infractions.py b/discordSuperUtils/infractions.py index 0b17813..44e714f 100644 --- a/discordSuperUtils/infractions.py +++ b/discordSuperUtils/infractions.py @@ -58,12 +58,16 @@ async def datetime(self) -> Optional[datetime]: return datetime.utcfromtimestamp(timestamp_data["timestamp"]) async def reason(self) -> Optional[str]: - reason_data = await self.infraction_manager.database.select(self.table, ["reason"], self.__checks) + reason_data = await self.infraction_manager.database.select( + self.table, ["reason"], self.__checks + ) if reason_data: return reason_data["reason"] async def set_reason(self, new_reason: str) -> None: - await self.infraction_manager.database.update(self.table, {"reason": new_reason}, self.__checks) + await self.infraction_manager.database.update( + self.table, {"reason": new_reason}, self.__checks + ) async def delete(self) -> PartialInfraction: partial = PartialInfraction( @@ -114,9 +118,7 @@ async def warn( ): await punishment.punishment_manager.punish(ctx, member, punishment) - return Infraction( - self, member, generated_id - ) + return Infraction(self, member, generated_id) async def punish( self, ctx: commands.Context, member: discord.Member, punishment: Punishment @@ -140,9 +142,7 @@ async def get_infractions( ) return [ - Infraction( - self, member, infraction["id"] - ) + Infraction(self, member, infraction["id"]) for infraction in warnings if infraction["timestamp"] > from_timestamp ] diff --git a/discordSuperUtils/music/music.py b/discordSuperUtils/music/music.py index d463d2b..cb014b8 100644 --- a/discordSuperUtils/music/music.py +++ b/discordSuperUtils/music/music.py @@ -264,7 +264,11 @@ async def ensure_activity(self, ctx: commands.Context) -> None: await asyncio.sleep(self.inactivity_timeout) - if ctx.voice_client and ctx.voice_client.is_connected() and not ctx.voice_client.is_playing(): + if ( + ctx.voice_client + and ctx.voice_client.is_connected() + and not ctx.voice_client.is_playing() + ): await self.cleanup(ctx.voice_client, ctx.guild) await self.call_event("on_inactivity_disconnect", ctx) diff --git a/discordSuperUtils/music/playlist.py b/discordSuperUtils/music/playlist.py index aa888b9..094db0e 100644 --- a/discordSuperUtils/music/playlist.py +++ b/discordSuperUtils/music/playlist.py @@ -133,5 +133,6 @@ async def delete(self) -> None: """ await self.music_manager.database.delete( - self.music_manager.tables["playlists"], {"user": self.owner.id, "id": self.id} + self.music_manager.tables["playlists"], + {"user": self.owner.id, "id": self.id}, ) diff --git a/discordSuperUtils/mute.py b/discordSuperUtils/mute.py index 07722bb..9b4d61a 100644 --- a/discordSuperUtils/mute.py +++ b/discordSuperUtils/mute.py @@ -29,7 +29,7 @@ class MuteManager(DatabaseChecker, Punisher): __slots__ = ("bot",) - def __init__(self, bot: commands.Bot): + def __init__(self, bot: commands.Bot, muted_role_name: str = "Muted") -> None: super().__init__( [ { @@ -43,6 +43,7 @@ def __init__(self, bot: commands.Bot): ["mutes"], ) self.bot = bot + self.muted_role_name = muted_role_name self.add_event(self.on_database_connect) @@ -94,7 +95,9 @@ async def on_member_join(self, member: discord.Member) -> None: ] if any([muted_member["member"] == member.id for muted_member in muted_members]): - muted_role = discord.utils.get(member.guild.roles, name="Muted") + muted_role = discord.utils.get( + member.guild.roles, name=self.muted_role_name + ) if muted_role: await member.add_roles(muted_role) @@ -215,7 +218,7 @@ async def mute( :rtype: None """ - muted_role = discord.utils.get(member.guild.roles, name="Muted") + muted_role = discord.utils.get(member.guild.roles, name=self.muted_role_name) if not muted_role: muted_role = await member.guild.create_role( name="Muted", @@ -261,7 +264,7 @@ async def unmute(self, member: discord.Member) -> Optional[bool]: await self.database.delete( self.tables["mutes"], {"guild": member.guild.id, "member": member.id} ) - muted_role = discord.utils.get(member.guild.roles, name="Muted") + muted_role = discord.utils.get(member.guild.roles, name=self.muted_role_name) if not muted_role: return diff --git a/discordSuperUtils/paginator.py b/discordSuperUtils/paginator.py index 9ac7baf..3c7e35d 100644 --- a/discordSuperUtils/paginator.py +++ b/discordSuperUtils/paginator.py @@ -13,21 +13,27 @@ def generate_embeds( fields=25, color=0xFF0000, string_format="{}", - footer: str = None, + footer: str = "", + display_page_in_footer = False, ): num_of_embeds = ceil((len(list_to_generate) + 1) / fields) embeds = [ discord.Embed( - title=f"{title} (Page 1/{num_of_embeds})", + title=title if display_page_in_footer else f"{title} (Page 1/{num_of_embeds})", description=description, color=color, + footer=footer + f"(Page 1/{num_of_embeds})" if display_page_in_footer else footer ) ] for i in range(2, num_of_embeds + 1): embeds.append( - discord.Embed(title=f"{title} (Page {i}/{num_of_embeds})", color=color) + discord.Embed( + title=title if display_page_in_footer else f"{title} (Page {i}/{num_of_embeds})", + description=description, + color=color, + footer=footer + f"(Page {i}/{num_of_embeds})" if display_page_in_footer else footer) ) embed_index = 0 @@ -39,10 +45,6 @@ def generate_embeds( if (index + 1) % fields == 0: embed_index += 1 - if footer: - for embed in embeds: - embed.set_footer(text=footer) - return embeds diff --git a/examples/moderation.py b/examples/moderation.py index 23a37cb..f09a78e 100644 --- a/examples/moderation.py +++ b/examples/moderation.py @@ -22,34 +22,30 @@ def make_removed_embeds(removed_infractions, member): - return ( - discordSuperUtils.generate_embeds( - [ - f"**Reason: **{infraction.reason}\n" - f"**ID: **{infraction.id}\n" - f"**Date of Infraction: **{infraction.date_of_infraction}" - for infraction in removed_infractions - ], - title=f"Removed Infractions", - fields=25, - description=f"List of infractions that were removed from {member.mention}.", - ) + return discordSuperUtils.generate_embeds( + [ + f"**Reason: **{infraction.reason}\n" + f"**ID: **{infraction.id}\n" + f"**Date of Infraction: **{infraction.date_of_infraction}" + for infraction in removed_infractions + ], + title=f"Removed Infractions", + fields=25, + description=f"List of infractions that were removed from {member.mention}.", ) async def make_infraction_embed(member_infractions, member): - return ( - discordSuperUtils.generate_embeds( - [ - f"**Reason: **{await infraction.reason()}\n" - f"**ID: **{infraction.id}\n" - f"**Date of Infraction: **{await infraction.datetime()}" - for infraction in member_infractions - ], - title=f"Infractions of {member}", - fields=25, - description=f"List of {member.mention}'s infractions.", - ) + return discordSuperUtils.generate_embeds( + [ + f"**Reason: **{await infraction.reason()}\n" + f"**ID: **{infraction.id}\n" + f"**Date of Infraction: **{await infraction.datetime()}" + for infraction in member_infractions + ], + title=f"Infractions of {member}", + fields=25, + description=f"List of {member.mention}'s infractions.", ) @@ -222,9 +218,7 @@ async def remove(ctx, member: discord.Member, infraction_id: str): ) embed.add_field(name="Reason", value=removed_infraction.reason, inline=False) - embed.add_field( - name="Infraction ID", value=removed_infraction.id, inline=False - ) + embed.add_field(name="Infraction ID", value=removed_infraction.id, inline=False) embed.add_field( name="Date of Infraction", value=str(removed_infraction.date_of_infraction), From b5fd51635859c49e5930bdc74982f0193588475b Mon Sep 17 00:00:00 2001 From: adam757521 Date: Mon, 25 Oct 2021 18:07:20 +0300 Subject: [PATCH 13/21] a --- examples/leveling.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/leveling.py b/examples/leveling.py index fdd3062..4b6e82d 100644 --- a/examples/leveling.py +++ b/examples/leveling.py @@ -1,5 +1,6 @@ import discord from discord.ext import commands +import aiosqlite import discordSuperUtils @@ -12,7 +13,7 @@ @bot.event async def on_ready(): - database = discordSuperUtils.DatabaseManager.connect(...) + database = discordSuperUtils.DatabaseManager.connect(await aiosqlite.connect("main.sqlite")) await LevelingManager.connect_to_database(database, ["xp", "roles", "role_list"]) print("Leveling manager is ready.", bot.user) @@ -82,8 +83,9 @@ async def leaderboard(ctx): title="Leveling Leaderboard", fields=25, description=f"Leaderboard of {ctx.guild}", + display_page_in_footer=True, ), ).run() -bot.run("token") +bot.run("ODYzNzMwNDE5MjQ5ODQwMTI4.YOrJow.Nukkjn0p-OmuAh_hDY0rGEBJAn0") From 291b63c5cc3f9f328e8783cc67a338bb87fa2cfc Mon Sep 17 00:00:00 2001 From: adam7100 <73548219+adam757521@users.noreply.github.com> Date: Mon, 25 Oct 2021 18:08:21 +0300 Subject: [PATCH 14/21] Update leveling.py --- examples/leveling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/leveling.py b/examples/leveling.py index 4b6e82d..393e0be 100644 --- a/examples/leveling.py +++ b/examples/leveling.py @@ -88,4 +88,4 @@ async def leaderboard(ctx): ).run() -bot.run("ODYzNzMwNDE5MjQ5ODQwMTI4.YOrJow.Nukkjn0p-OmuAh_hDY0rGEBJAn0") +bot.run("token") From b20eca1903f7b5fed6c1441866cd0430fbafaaf0 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Mon, 25 Oct 2021 18:11:46 +0300 Subject: [PATCH 15/21] Made __handle_experience use the default function --- discordSuperUtils/leveling.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discordSuperUtils/leveling.py b/discordSuperUtils/leveling.py index ca7f9be..2d28648 100644 --- a/discordSuperUtils/leveling.py +++ b/discordSuperUtils/leveling.py @@ -184,8 +184,9 @@ async def on_database_connect(self): def generate_checks(member: discord.Member): return {"guild": member.guild.id, "member": member.id} - @DatabaseChecker.uses_database async def __handle_experience(self, message): + self._check_database() + if not message.guild or message.author.bot: return From 819dc20afb340f028b65e8fd59f95f75f2d408f0 Mon Sep 17 00:00:00 2001 From: adam757521 Date: Mon, 25 Oct 2021 18:21:00 +0300 Subject: [PATCH 16/21] Made paginator set the footer automatically Removed database connection from leveling.py --- discordSuperUtils/paginator.py | 21 ++++++++++++++++----- examples/leveling.py | 4 +--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/discordSuperUtils/paginator.py b/discordSuperUtils/paginator.py index 3c7e35d..e9b3bdc 100644 --- a/discordSuperUtils/paginator.py +++ b/discordSuperUtils/paginator.py @@ -14,26 +14,37 @@ def generate_embeds( color=0xFF0000, string_format="{}", footer: str = "", - display_page_in_footer = False, + display_page_in_footer=False, ): num_of_embeds = ceil((len(list_to_generate) + 1) / fields) embeds = [ discord.Embed( - title=title if display_page_in_footer else f"{title} (Page 1/{num_of_embeds})", + title=title + if display_page_in_footer + else f"{title} (Page 1/{num_of_embeds})", description=description, color=color, - footer=footer + f"(Page 1/{num_of_embeds})" if display_page_in_footer else footer + ).set_footer( + text=f"{footer} (Page 1/{num_of_embeds})" + if display_page_in_footer + else footer ) ] for i in range(2, num_of_embeds + 1): embeds.append( discord.Embed( - title=title if display_page_in_footer else f"{title} (Page {i}/{num_of_embeds})", + title=title + if display_page_in_footer + else f"{title} (Page {i}/{num_of_embeds})", description=description, color=color, - footer=footer + f"(Page {i}/{num_of_embeds})" if display_page_in_footer else footer) + ).set_footer( + text=f"{footer} (Page {i}/{num_of_embeds})" + if display_page_in_footer + else footer + ) ) embed_index = 0 diff --git a/examples/leveling.py b/examples/leveling.py index 393e0be..fdd3062 100644 --- a/examples/leveling.py +++ b/examples/leveling.py @@ -1,6 +1,5 @@ import discord from discord.ext import commands -import aiosqlite import discordSuperUtils @@ -13,7 +12,7 @@ @bot.event async def on_ready(): - database = discordSuperUtils.DatabaseManager.connect(await aiosqlite.connect("main.sqlite")) + database = discordSuperUtils.DatabaseManager.connect(...) await LevelingManager.connect_to_database(database, ["xp", "roles", "role_list"]) print("Leveling manager is ready.", bot.user) @@ -83,7 +82,6 @@ async def leaderboard(ctx): title="Leveling Leaderboard", fields=25, description=f"Leaderboard of {ctx.guild}", - display_page_in_footer=True, ), ).run() From dcfad702f5be71217b0cdbf712ee7f8a777e5893 Mon Sep 17 00:00:00 2001 From: TFAM_IS_LUV <53284350+popop098@users.noreply.github.com> Date: Wed, 27 Oct 2021 20:40:40 +0900 Subject: [PATCH 17/21] Update invitetracker.py (#27) solve '__initialize_cache' discord.Forbidden error --- discordSuperUtils/invitetracker.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/discordSuperUtils/invitetracker.py b/discordSuperUtils/invitetracker.py index f5f93c1..5079fe3 100644 --- a/discordSuperUtils/invitetracker.py +++ b/discordSuperUtils/invitetracker.py @@ -124,10 +124,17 @@ async def __initialize_cache(self) -> None: await self.bot.wait_until_ready() for guild in self.bot.guilds: - self.cache[guild.id] = await guild.invites() + try: + self.cache[guild.id] = await guild.invites() + except discord.Foridden: + pass + async def __update_guild_cache(self, guild: discord.Guild) -> None: - self.cache[guild.id] = await guild.invites() + try: + self.cache[guild.id] = await guild.invites() + except discord.Foridden: + pass async def __track_invite(self, invite: discord.Invite) -> None: self.cache[invite.guild.id].append(invite) From 0d6c16671c9cb75cf78db98bb372cdfa907ea2a6 Mon Sep 17 00:00:00 2001 From: TFAM_IS_LUV <53284350+popop098@users.noreply.github.com> Date: Wed, 27 Oct 2021 20:50:07 +0900 Subject: [PATCH 18/21] edit typo (#28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Foridden` to `Forbidden` mistake 😅 --- discordSuperUtils/invitetracker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discordSuperUtils/invitetracker.py b/discordSuperUtils/invitetracker.py index 5079fe3..62594e0 100644 --- a/discordSuperUtils/invitetracker.py +++ b/discordSuperUtils/invitetracker.py @@ -126,14 +126,14 @@ async def __initialize_cache(self) -> None: for guild in self.bot.guilds: try: self.cache[guild.id] = await guild.invites() - except discord.Foridden: + except discord.Forbidden: pass async def __update_guild_cache(self, guild: discord.Guild) -> None: try: self.cache[guild.id] = await guild.invites() - except discord.Foridden: + except discord.Forbidden: pass async def __track_invite(self, invite: discord.Invite) -> None: From 6b06d7e5f4ea4dbea35eff1a2d77426e2cf3e5bc Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Wed, 27 Oct 2021 14:58:54 +0300 Subject: [PATCH 19/21] Fixed __handle_reactions database Fixed imports in invite_tracker Black format --- discordSuperUtils/invitetracker.py | 3 +-- discordSuperUtils/reactionroles.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/discordSuperUtils/invitetracker.py b/discordSuperUtils/invitetracker.py index 62594e0..0164218 100644 --- a/discordSuperUtils/invitetracker.py +++ b/discordSuperUtils/invitetracker.py @@ -10,11 +10,11 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Union, Optional +import discord from .base import DatabaseChecker if TYPE_CHECKING: - import discord from discord.ext import commands @@ -128,7 +128,6 @@ async def __initialize_cache(self) -> None: self.cache[guild.id] = await guild.invites() except discord.Forbidden: pass - async def __update_guild_cache(self, guild: discord.Guild) -> None: try: diff --git a/discordSuperUtils/reactionroles.py b/discordSuperUtils/reactionroles.py index f6319ef..8193b7a 100644 --- a/discordSuperUtils/reactionroles.py +++ b/discordSuperUtils/reactionroles.py @@ -35,8 +35,9 @@ def get_emoji_sql(emoji): return emoji_string - @DatabaseChecker.uses_database async def __handle_reactions(self, payload): + self._check_database() + if payload.user_id == self.bot.user.id: return From 700feacadc25a19e300f25c33579be0624c4873f Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Wed, 27 Oct 2021 21:23:02 +0300 Subject: [PATCH 20/21] Added timestamp argument to generate_embeds --- discordSuperUtils/paginator.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/discordSuperUtils/paginator.py b/discordSuperUtils/paginator.py index e9b3bdc..e042b3f 100644 --- a/discordSuperUtils/paginator.py +++ b/discordSuperUtils/paginator.py @@ -1,8 +1,14 @@ +from __future__ import annotations + import asyncio from math import ceil +from typing import TYPE_CHECKING import discord +if TYPE_CHECKING: + from datetime import datetime + __all__ = ("generate_embeds", "EmojiError", "PageManager", "ButtonsPageManager") @@ -15,6 +21,7 @@ def generate_embeds( string_format="{}", footer: str = "", display_page_in_footer=False, + timestamp: datetime = discord.Embed.Empty, ): num_of_embeds = ceil((len(list_to_generate) + 1) / fields) @@ -25,6 +32,7 @@ def generate_embeds( else f"{title} (Page 1/{num_of_embeds})", description=description, color=color, + timestamp=timestamp, ).set_footer( text=f"{footer} (Page 1/{num_of_embeds})" if display_page_in_footer @@ -40,6 +48,7 @@ def generate_embeds( else f"{title} (Page {i}/{num_of_embeds})", description=description, color=color, + timestamp=timestamp, ).set_footer( text=f"{footer} (Page {i}/{num_of_embeds})" if display_page_in_footer From bd6d597563d1d28dac82dcafd605cd589af8b37c Mon Sep 17 00:00:00 2001 From: adam757521 <73548219+adam757521@users.noreply.github.com> Date: Wed, 27 Oct 2021 21:40:35 +0300 Subject: [PATCH 21/21] v0.2.9 --- discordSuperUtils/__init__.py | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/discordSuperUtils/__init__.py b/discordSuperUtils/__init__.py index de27465..d86e6ea 100644 --- a/discordSuperUtils/__init__.py +++ b/discordSuperUtils/__init__.py @@ -32,6 +32,6 @@ from .youtube import YoutubeClient __title__ = "discordSuperUtils" -__version__ = "0.2.8" +__version__ = "0.2.9" __author__ = "Koyashie07 & Adam7100" __license__ = "MIT" diff --git a/setup.py b/setup.py index bd8885b..e18e6c6 100644 --- a/setup.py +++ b/setup.py @@ -13,14 +13,14 @@ "discordSuperUtils.music.lavalink": ["*"], }, include_package_data=True, - version="0.2.8", + version="0.2.9", license="MIT", description="Discord Bot Development made easy!", long_description=README, long_description_content_type="text/markdown", author="koyashie07 and adam7100", url="https://github.com/discordsuperutils/discord-super-utils", - download_url="https://github.com/discordsuperutils/discord-super-utils/archive/refs/tags/v0.2.8.tar.gz", + download_url="https://github.com/discordsuperutils/discord-super-utils/archive/refs/tags/v0.2.9.tar.gz", keywords=[ "discord", "easy",