├── db.sqlite3
├── static_files
├── img
│ ├── logo.png
│ └── youtube.png
├── css
│ └── main.css
└── js
│ ├── app.js
│ └── particles.min.js
├── requirements.txt
├── YouTubeDownloader
├── forms.py
├── urls.py
├── wsgi.py
├── views.py
└── settings.py
├── vercel.json
├── manage.py
├── LICENSE
├── templates
├── base.html
└── home.html
└── readme.md
/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hi-im-gabriel/django-ytdownloader/HEAD/db.sqlite3
--------------------------------------------------------------------------------
/static_files/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hi-im-gabriel/django-ytdownloader/HEAD/static_files/img/logo.png
--------------------------------------------------------------------------------
/static_files/img/youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hi-im-gabriel/django-ytdownloader/HEAD/static_files/img/youtube.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | django>=3.2.9
2 | django-crispy-forms>=1.13.0
3 | pytz>=2021.3
4 | pytube==11.0.2
5 | requests==2.26.0
6 | whitenoise>=5.3.0
--------------------------------------------------------------------------------
/YouTubeDownloader/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | class DownloadForm(forms.Form):
4 | url = forms.CharField(widget=forms.TextInput(attrs={ 'placeholder': 'Enter video url' }), label=False)
--------------------------------------------------------------------------------
/YouTubeDownloader/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from django.views.generic import RedirectView
3 | from .views import download_video
4 |
5 |
6 | urlpatterns = [
7 | path('', download_video),
8 | path('', RedirectView.as_view(url='/')) # Exceptions handling
9 | ]
10 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "builds": [{
3 | "src": "YouTubeDownloader/wsgi.py",
4 | "use": "@ardnt/vercel-python-wsgi",
5 | "config": { "maxLambdaSize": "15mb" }
6 | }],
7 | "routes": [
8 | {
9 | "src": "/(.*)",
10 | "dest": "YouTubeDownloader/wsgi.py"
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/YouTubeDownloader/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for YTVDownloader project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'YouTubeDownloader.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/static_files/css/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin-top: 40px;
5 | background-color: #181818;
6 | color: #ffffff;
7 | }
8 |
9 | canvas {
10 | display: block;
11 | }
12 |
13 | #particles-js {
14 | background-color: #181818;
15 | position: fixed;
16 | top: 0;
17 | right: 0;
18 | bottom: 0;
19 | left: 0;
20 | z-index: 0;
21 | }
22 |
23 | ::-webkit-scrollbar {
24 | display: none;
25 | }
26 |
27 | ul {
28 | list-style: none;
29 | }
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'YouTubeDownloader.settings')
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Gabriel Tavares
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | YouTube Downloader
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {% block content %} {% endblock %}
25 |
26 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | (DEPRECATED)YouTube Downloader
Updated version: https://github.com/gabzin/downtube-nextjs
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
⭐️ Star this project ⭐️
10 |
11 | https://downtube.vercel.app/
12 |
13 |
14 |
15 |
16 | # Updates
17 | > # v2021.12
18 | >
19 | > #### Added:
20 | >
21 | > - YouTube API to get likes/favs count
22 | > - Visual enhancement
23 | > - Particles JS
24 | > - Bootstrap icons
25 | >
26 | > #### Bug fixes:
27 | >
28 | > - Opening video/audio links instead of downloading
29 | >
30 | > # v2021.11
31 | >
32 | > #### Added:
33 | >
34 | > - Migration from pafy/youtube-dl to pytube (Performance reasons)
35 | > - Visual enhancement
36 | >
37 | > #### Bug fixes:
38 | >
39 | > - Error 404 handling
40 | >
41 | > #### Future updates:
42 | >
43 | > - 1080p60+ downloads
44 | > - Dark/Light mode option
45 |
46 |
47 | # Requirements
48 | - Python3
49 | - Pip3
50 | - Git
51 | - YouTube V3 API KEY (otherwise, likes count aren't avalible with pytube anymore):
52 |
53 | https://developers.google.com/youtube/v3
54 |
55 |
56 |
57 |
58 |
59 |
60 | # Installation
61 |
62 | ```bash
63 | git clone https://github.com/gabzin/django-ytdownloader
64 | cd django-ytdownloader
65 | pip3 install -r requirements.txt
66 | ```
67 |
68 | # Starting server
69 |
70 | ```bash
71 | python manage.py runserver
72 | ```
73 |
--------------------------------------------------------------------------------
/static_files/js/app.js:
--------------------------------------------------------------------------------
1 | /* -----------------------------------------------
2 | /* How to use? : Check the GitHub README
3 | /* ----------------------------------------------- */
4 |
5 | /* To load a config file (particles.json) you need to host this demo (MAMP/WAMP/local)... */
6 | /*
7 | particlesJS.load('particles-js', 'particles.json', function() {
8 | console.log('particles.js loaded - callback');
9 | });
10 | */
11 |
12 | /* Otherwise just put the config content (json): */
13 |
14 | particlesJS('particles-js',
15 |
16 | {
17 | "particles": {
18 | "number": {
19 | "value": 80,
20 | "density": {
21 | "enable": true,
22 | "value_area": 800
23 | }
24 | },
25 | "color": {
26 | "value": "#ffffff"
27 | },
28 | "shape": {
29 | "type": "circle",
30 | "stroke": {
31 | "width": 0,
32 | "color": "#000000"
33 | },
34 | "polygon": {
35 | "nb_sides": 0
36 | },
37 | "image": {
38 | "src": "img/github.svg",
39 | "width": 100,
40 | "height": 100
41 | }
42 | },
43 | "opacity": {
44 | "value": 1,
45 | "random": false,
46 | "anim": {
47 | "enable": false,
48 | "speed": 1,
49 | "opacity_min": 0.1,
50 | "sync": false
51 | }
52 | },
53 | "size": {
54 | "value": 0,
55 | "random": true,
56 | "anim": {
57 | "enable": false,
58 | "speed": 40,
59 | "size_min": 0.1,
60 | "sync": false
61 | }
62 | },
63 | "line_linked": {
64 | "enable": true,
65 | "distance": 150,
66 | "color": "#ffffff",
67 | "opacity": 0.4,
68 | "width": 1
69 | },
70 | "move": {
71 | "enable": true,
72 | "speed": 6,
73 | "direction": "none",
74 | "random": false,
75 | "straight": false,
76 | "out_mode": "out",
77 | "attract": {
78 | "enable": false,
79 | "rotateX": 600,
80 | "rotateY": 1200
81 | }
82 | }
83 | },
84 | "interactivity": {
85 | "detect_on": "canvas",
86 | "events": {
87 | "onhover": {
88 | "enable": false,
89 | "mode": "repulse"
90 | },
91 | "onclick": {
92 | "enable": false,
93 | "mode": "push"
94 | },
95 | "resize": true
96 | },
97 | "modes": {
98 | "grab": {
99 | "distance": 400,
100 | "line_linked": {
101 | "opacity": 1
102 | }
103 | },
104 | "bubble": {
105 | "distance": 400,
106 | "size": 40,
107 | "duration": 2,
108 | "opacity": 8,
109 | "speed": 3
110 | },
111 | "repulse": {
112 | "distance": 200
113 | },
114 | "push": {
115 | "particles_nb": 4
116 | },
117 | "remove": {
118 | "particles_nb": 2
119 | }
120 | }
121 | },
122 | "retina_detect": true,
123 | "config_demo": {
124 | "hide_card": false,
125 | "background_color": "#b61924",
126 | "background_image": "",
127 | "background_position": "50% 50%",
128 | "background_repeat": "no-repeat",
129 | "background_size": "cover"
130 | }
131 | }
132 |
133 | );
--------------------------------------------------------------------------------
/YouTubeDownloader/views.py:
--------------------------------------------------------------------------------
1 | #Imports
2 | from django.http.response import HttpResponse
3 | from django.shortcuts import render
4 | from django.contrib import messages
5 | from .forms import DownloadForm
6 | from pytube import YouTube
7 | from math import pow, floor, log
8 | from datetime import timedelta
9 | from requests import get
10 |
11 | # Your YouTube V3 Api Key
12 | KEY = ""
13 |
14 | # Convert from bytes
15 | def convertsize(size_bytes):
16 | if size_bytes == 0:
17 | return "0B"
18 | size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
19 | i = int(floor(log(size_bytes, 1024)))
20 | p = pow(1024, i)
21 | s = round(size_bytes / p, 2)
22 | return "%s %s" % (s, size_name[i])
23 |
24 | # Convert long numbers
25 | def humanformat(number):
26 | units = ['', 'K', 'M', 'B', 'T', 'Q']
27 | k = 1000.0
28 | magnitude = int(floor(log(number, k)))
29 | return '%.2f%s' % (number / k**magnitude, units[magnitude])
30 |
31 | #When click search button
32 | def download_video(request, string=""):
33 | global video_url
34 | form = DownloadForm(request.POST or None)
35 | if form.is_valid():
36 | video_url = form.cleaned_data.get("url")
37 | try:
38 | yt_obj = YouTube(video_url)
39 | videos = yt_obj.streams.filter(is_dash=False).desc()
40 | audios = yt_obj.streams.filter(only_audio=True).order_by('abr').desc()
41 | except Exception as e:
42 | #messages.error(request, 'Invalid URL.')
43 | messages.error(request, e)
44 | return render(request, 'home.html',{ 'form': form })
45 |
46 | video_audio_streams = []
47 | audio_streams = []
48 | try:
49 | url = f"https://www.googleapis.com/youtube/v3/videos?id={yt_obj.video_id}&key={KEY}&part=statistics"
50 | video_stats = get(url).json()
51 | video_likes = video_stats['items'][0]['statistics']['likeCount']
52 | video_favs = video_stats['items'][0]['statistics']['favoriteCount']
53 | except:
54 | video_likes = 0
55 | # List of video streams dictionaries
56 | for s in videos:
57 | video_audio_streams.append({
58 | 'resolution' : s.resolution,
59 | 'extension' : s.mime_type.replace('video/',''),
60 | 'file_size' : convertsize(s.filesize),
61 | 'video_url' : s.url, 'file_name' : yt_obj.title + '.' + s.mime_type.replace('video/','')
62 | })
63 |
64 | # List of audio streams dictionaries
65 | for s in audios:
66 | audio_streams.append({
67 | 'resolution' : s.abr,
68 | 'extension' : s.mime_type.replace('audio/',''),
69 | 'file_size' : convertsize(s.filesize),
70 | 'video_url' : s.url, 'file_name' : yt_obj.title + '.' + s.mime_type.replace('video/','')
71 | })
72 |
73 | if yt_obj.rating == None:
74 | rating = 5
75 | else:
76 | rating = yt_obj.rating
77 |
78 | # Full content to render
79 | context = {
80 | 'form' : form,'title' : yt_obj.title,
81 | 'rating': humanformat(int(video_likes)),
82 | 'thumb' : yt_obj.thumbnail_url, 'author' : yt_obj.author,
83 | 'author_url' : yt_obj.channel_url,
84 | 'duration' : str(timedelta(seconds=yt_obj.length)), 'views' : humanformat(yt_obj.views) if yt_obj.views >= 1000 else yt_obj.views,
85 | 'stream_audio' : audio_streams, 'streams' : video_audio_streams
86 | }
87 |
88 | return render(request, 'home.html', context)
89 |
90 | return render(request, 'home.html',{ 'form': form })
--------------------------------------------------------------------------------
/YouTubeDownloader/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for YouTubeDownloader project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.1.3.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.1/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 | # Quick-start development settings - unsuitable for production
19 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
20 |
21 | # SECURITY WARNING: keep the secret key used in production secret!
22 | SECRET_KEY = ';ycuh^rx/b(]]/?!6za.833=yu$d~4,[rzh$febpvy6c6=sjrr'
23 |
24 | # SECURITY WARNING: don't run with debug turned on in production!
25 | DEBUG = True
26 |
27 | ALLOWED_HOSTS = ['*'] #.vercel.app
28 |
29 | # Application definition
30 |
31 | INSTALLED_APPS = [
32 | 'django.contrib.auth',
33 | 'django.contrib.contenttypes',
34 | 'django.contrib.sessions',
35 | 'django.contrib.messages',
36 | 'django.contrib.staticfiles',
37 | 'crispy_forms',
38 | ]
39 |
40 | MIDDLEWARE = [
41 | 'django.middleware.security.SecurityMiddleware',
42 | 'django.contrib.sessions.middleware.SessionMiddleware',
43 | 'django.middleware.common.CommonMiddleware',
44 | 'django.middleware.csrf.CsrfViewMiddleware',
45 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
46 | 'django.contrib.messages.middleware.MessageMiddleware',
47 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
48 | ]
49 |
50 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
51 |
52 | ROOT_URLCONF = 'YouTubeDownloader.urls'
53 |
54 | TEMPLATES = [
55 | {
56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
57 | 'DIRS': [os.path.join(BASE_DIR, 'templates')]
58 | ,
59 | 'APP_DIRS': True,
60 | 'OPTIONS': {
61 | 'context_processors': [
62 | 'django.template.context_processors.debug',
63 | 'django.template.context_processors.request',
64 | 'django.contrib.auth.context_processors.auth',
65 | 'django.contrib.messages.context_processors.messages',
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = 'YouTubeDownloader.wsgi.application'
72 |
73 | # Database
74 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
75 |
76 | DATABASES = {}
77 |
78 | # Password validation
79 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
80 |
81 | AUTH_PASSWORD_VALIDATORS = [
82 | {
83 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
84 | },
85 | {
86 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
87 | },
88 | {
89 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
90 | },
91 | {
92 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
93 | },
94 | ]
95 |
96 | # Internationalization
97 | # https://docs.djangoproject.com/en/2.1/topics/i18n/
98 |
99 | LANGUAGE_CODE = 'pt-br'
100 |
101 | TIME_ZONE = 'UTC'
102 |
103 | USE_I18N = True
104 |
105 | USE_L10N = True
106 |
107 | USE_TZ = True
108 |
109 | # Static files (CSS, JavaScript, Images)
110 | # https://docs.djangoproject.com/en/2.1/howto/static-files/
111 |
112 |
113 | STATIC_URL = '/static/'
114 | STATIC_ROOT = os.path.join(BASE_DIR, 'static_root')
115 |
116 | STATICFILES_DIRS = [os.path.join(BASE_DIR, "static_files")]
117 |
118 | CRISPY_TEMPLATE_PACK = 'bootstrap4'
119 |
--------------------------------------------------------------------------------
/templates/home.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %} {% load crispy_forms_tags %} {% load static %} {% block content %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 | {% if messages %}
20 |
21 | {% for message in messages %}
22 | - {{ message }}
23 | {% endfor %}
24 |
25 | {% endif %}
26 |
27 |
28 |
29 |
30 |
35 |
36 | {% if title %}
37 |
38 |
39 |
40 |
{{ title }}
41 |
42 |
43 |
44 |
45 |

46 |
47 |
48 |
49 |
50 | -
51 |
: {{ duration }}
52 |
53 | -
54 |
: {{ views }}
55 |
56 | -
57 |
: {{ rating }}
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Video
66 |
67 |
68 |
69 | |
70 | |
71 | |
72 | |
73 |
74 |
75 |
76 | {% for stream in streams %}
77 |
78 | {% if stream.resolution == '720p' %}
79 | | {{ stream.resolution }} |
80 | {% else %}
81 | {{ stream.resolution }} |
82 | {% endif %}
83 | {{ stream.file_size }} |
84 | {{ stream.extension }} |
85 |
86 | Download
87 | |
88 |
89 | {% endfor %}
90 |
91 |
92 |
93 |
Audio Only
94 |
95 |
96 |
97 | |
98 | |
99 | |
100 | |
101 |
102 |
103 |
104 | {% for stream in stream_audio %}
105 |
106 | | {{ stream.resolution }} |
107 | {{ stream.file_size }} |
108 | {{ stream.extension }} |
109 |
110 | Download
111 | |
112 |
113 | {% endfor %}
114 |
115 |
116 |
117 |
118 | {% endif %}
119 | {% endblock %}
120 |
--------------------------------------------------------------------------------
/static_files/js/particles.min.js:
--------------------------------------------------------------------------------
1 | /* -----------------------------------------------
2 | /* Author : Vincent Garreau - vincentgarreau.com
3 | /* MIT license: http://opensource.org/licenses/MIT
4 | /* Demo / Generator : vincentgarreau.com/particles.js
5 | /* GitHub : github.com/VincentGarreau/particles.js
6 | /* How to use? : Check the GitHub README
7 | /* v2.0.0
8 | /* ----------------------------------------------- */
9 | function hexToRgb(e) {
10 | var a = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
11 | e = e.replace(a, function (e, a, t, i) {
12 | return a + a + t + t + i + i
13 | });
14 | var t = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);
15 | return t ? {
16 | r: parseInt(t[1], 16),
17 | g: parseInt(t[2], 16),
18 | b: parseInt(t[3], 16)
19 | } : null
20 | }
21 |
22 | function clamp(e, a, t) {
23 | return Math.min(Math.max(e, a), t)
24 | }
25 |
26 | function isInArray(e, a) {
27 | return a.indexOf(e) > -1
28 | }
29 | var pJS = function (e, a) {
30 | var t = document.querySelector("#" + e + " > .particles-js-canvas-el");
31 | this.pJS = {
32 | canvas: {
33 | el: t,
34 | w: t.offsetWidth,
35 | h: t.offsetHeight
36 | },
37 | particles: {
38 | number: {
39 | value: 400,
40 | density: {
41 | enable: !0,
42 | value_area: 800
43 | }
44 | },
45 | color: {
46 | value: "#fff"
47 | },
48 | shape: {
49 | type: "circle",
50 | stroke: {
51 | width: 0,
52 | color: "#ff0000"
53 | },
54 | polygon: {
55 | nb_sides: 5
56 | },
57 | image: {
58 | src: "",
59 | width: 100,
60 | height: 100
61 | }
62 | },
63 | opacity: {
64 | value: 1,
65 | random: !1,
66 | anim: {
67 | enable: !1,
68 | speed: 2,
69 | opacity_min: 0,
70 | sync: !1
71 | }
72 | },
73 | size: {
74 | value: 20,
75 | random: !1,
76 | anim: {
77 | enable: !1,
78 | speed: 20,
79 | size_min: 0,
80 | sync: !1
81 | }
82 | },
83 | line_linked: {
84 | enable: !0,
85 | distance: 100,
86 | color: "#fff",
87 | opacity: 1,
88 | width: 1
89 | },
90 | move: {
91 | enable: !0,
92 | speed: 2,
93 | direction: "none",
94 | random: !1,
95 | straight: !1,
96 | out_mode: "out",
97 | bounce: !1,
98 | attract: {
99 | enable: !1,
100 | rotateX: 3e3,
101 | rotateY: 3e3
102 | }
103 | },
104 | array: []
105 | },
106 | interactivity: {
107 | detect_on: "canvas",
108 | events: {
109 | onhover: {
110 | enable: !0,
111 | mode: "grab"
112 | },
113 | onclick: {
114 | enable: !0,
115 | mode: "push"
116 | },
117 | resize: !0
118 | },
119 | modes: {
120 | grab: {
121 | distance: 100,
122 | line_linked: {
123 | opacity: 1
124 | }
125 | },
126 | bubble: {
127 | distance: 200,
128 | size: 80,
129 | duration: .4
130 | },
131 | repulse: {
132 | distance: 200,
133 | duration: .4
134 | },
135 | push: {
136 | particles_nb: 4
137 | },
138 | remove: {
139 | particles_nb: 2
140 | }
141 | },
142 | mouse: {}
143 | },
144 | retina_detect: !1,
145 | fn: {
146 | interact: {},
147 | modes: {},
148 | vendors: {}
149 | },
150 | tmp: {}
151 | };
152 | var i = this.pJS;
153 | a && Object.deepExtend(i, a), i.tmp.obj = {
154 | size_value: i.particles.size.value,
155 | size_anim_speed: i.particles.size.anim.speed,
156 | move_speed: i.particles.move.speed,
157 | line_linked_distance: i.particles.line_linked.distance,
158 | line_linked_width: i.particles.line_linked.width,
159 | mode_grab_distance: i.interactivity.modes.grab.distance,
160 | mode_bubble_distance: i.interactivity.modes.bubble.distance,
161 | mode_bubble_size: i.interactivity.modes.bubble.size,
162 | mode_repulse_distance: i.interactivity.modes.repulse.distance
163 | }, i.fn.retinaInit = function () {
164 | i.retina_detect && window.devicePixelRatio > 1 ? (i.canvas.pxratio = window.devicePixelRatio, i.tmp.retina = !0) : (i.canvas.pxratio = 1, i.tmp.retina = !1), i.canvas.w = i.canvas.el.offsetWidth * i.canvas.pxratio, i.canvas.h = i.canvas.el.offsetHeight * i.canvas.pxratio, i.particles.size.value = i.tmp.obj.size_value * i.canvas.pxratio, i.particles.size.anim.speed = i.tmp.obj.size_anim_speed * i.canvas.pxratio, i.particles.move.speed = i.tmp.obj.move_speed * i.canvas.pxratio, i.particles.line_linked.distance = i.tmp.obj.line_linked_distance * i.canvas.pxratio, i.interactivity.modes.grab.distance = i.tmp.obj.mode_grab_distance * i.canvas.pxratio, i.interactivity.modes.bubble.distance = i.tmp.obj.mode_bubble_distance * i.canvas.pxratio, i.particles.line_linked.width = i.tmp.obj.line_linked_width * i.canvas.pxratio, i.interactivity.modes.bubble.size = i.tmp.obj.mode_bubble_size * i.canvas.pxratio, i.interactivity.modes.repulse.distance = i.tmp.obj.mode_repulse_distance * i.canvas.pxratio
165 | }, i.fn.canvasInit = function () {
166 | i.canvas.ctx = i.canvas.el.getContext("2d")
167 | }, i.fn.canvasSize = function () {
168 | i.canvas.el.width = i.canvas.w, i.canvas.el.height = i.canvas.h, i && i.interactivity.events.resize && window.addEventListener("resize", function () {
169 | i.canvas.w = i.canvas.el.offsetWidth, i.canvas.h = i.canvas.el.offsetHeight, i.tmp.retina && (i.canvas.w *= i.canvas.pxratio, i.canvas.h *= i.canvas.pxratio), i.canvas.el.width = i.canvas.w, i.canvas.el.height = i.canvas.h, i.particles.move.enable || (i.fn.particlesEmpty(), i.fn.particlesCreate(), i.fn.particlesDraw(), i.fn.vendors.densityAutoParticles()), i.fn.vendors.densityAutoParticles()
170 | })
171 | }, i.fn.canvasPaint = function () {
172 | i.canvas.ctx.fillRect(0, 0, i.canvas.w, i.canvas.h)
173 | }, i.fn.canvasClear = function () {
174 | i.canvas.ctx.clearRect(0, 0, i.canvas.w, i.canvas.h)
175 | }, i.fn.particle = function (e, a, t) {
176 | if (this.radius = (i.particles.size.random ? Math.random() : 1) * i.particles.size.value, i.particles.size.anim.enable && (this.size_status = !1, this.vs = i.particles.size.anim.speed / 100, i.particles.size.anim.sync || (this.vs = this.vs * Math.random())), this.x = t ? t.x : Math.random() * i.canvas.w, this.y = t ? t.y : Math.random() * i.canvas.h, this.x > i.canvas.w - 2 * this.radius ? this.x = this.x - this.radius : this.x < 2 * this.radius && (this.x = this.x + this.radius), this.y > i.canvas.h - 2 * this.radius ? this.y = this.y - this.radius : this.y < 2 * this.radius && (this.y = this.y + this.radius), i.particles.move.bounce && i.fn.vendors.checkOverlap(this, t), this.color = {}, "object" == typeof e.value)
177 | if (e.value instanceof Array) {
178 | var s = e.value[Math.floor(Math.random() * i.particles.color.value.length)];
179 | this.color.rgb = hexToRgb(s)
180 | } else void 0 != e.value.r && void 0 != e.value.g && void 0 != e.value.b && (this.color.rgb = {
181 | r: e.value.r,
182 | g: e.value.g,
183 | b: e.value.b
184 | }), void 0 != e.value.h && void 0 != e.value.s && void 0 != e.value.l && (this.color.hsl = {
185 | h: e.value.h,
186 | s: e.value.s,
187 | l: e.value.l
188 | });
189 | else "random" == e.value ? this.color.rgb = {
190 | r: Math.floor(256 * Math.random()) + 0,
191 | g: Math.floor(256 * Math.random()) + 0,
192 | b: Math.floor(256 * Math.random()) + 0
193 | } : "string" == typeof e.value && (this.color = e, this.color.rgb = hexToRgb(this.color.value));
194 | this.opacity = (i.particles.opacity.random ? Math.random() : 1) * i.particles.opacity.value, i.particles.opacity.anim.enable && (this.opacity_status = !1, this.vo = i.particles.opacity.anim.speed / 100, i.particles.opacity.anim.sync || (this.vo = this.vo * Math.random()));
195 | var n = {};
196 | switch (i.particles.move.direction) {
197 | case "top":
198 | n = {
199 | x: 0,
200 | y: -1
201 | };
202 | break;
203 | case "top-right":
204 | n = {
205 | x: .5,
206 | y: -.5
207 | };
208 | break;
209 | case "right":
210 | n = {
211 | x: 1,
212 | y: -0
213 | };
214 | break;
215 | case "bottom-right":
216 | n = {
217 | x: .5,
218 | y: .5
219 | };
220 | break;
221 | case "bottom":
222 | n = {
223 | x: 0,
224 | y: 1
225 | };
226 | break;
227 | case "bottom-left":
228 | n = {
229 | x: -.5,
230 | y: 1
231 | };
232 | break;
233 | case "left":
234 | n = {
235 | x: -1,
236 | y: 0
237 | };
238 | break;
239 | case "top-left":
240 | n = {
241 | x: -.5,
242 | y: -.5
243 | };
244 | break;
245 | default:
246 | n = {
247 | x: 0,
248 | y: 0
249 | }
250 | }
251 | i.particles.move.straight ? (this.vx = n.x, this.vy = n.y, i.particles.move.random && (this.vx = this.vx * Math.random(), this.vy = this.vy * Math.random())) : (this.vx = n.x + Math.random() - .5, this.vy = n.y + Math.random() - .5), this.vx_i = this.vx, this.vy_i = this.vy;
252 | var r = i.particles.shape.type;
253 | if ("object" == typeof r) {
254 | if (r instanceof Array) {
255 | var c = r[Math.floor(Math.random() * r.length)];
256 | this.shape = c
257 | }
258 | } else this.shape = r;
259 | if ("image" == this.shape) {
260 | var o = i.particles.shape;
261 | this.img = {
262 | src: o.image.src,
263 | ratio: o.image.width / o.image.height
264 | }, this.img.ratio || (this.img.ratio = 1), "svg" == i.tmp.img_type && void 0 != i.tmp.source_svg && (i.fn.vendors.createSvgImg(this), i.tmp.pushing && (this.img.loaded = !1))
265 | }
266 | }, i.fn.particle.prototype.draw = function () {
267 | function e() {
268 | i.canvas.ctx.drawImage(r, a.x - t, a.y - t, 2 * t, 2 * t / a.img.ratio)
269 | }
270 | var a = this;
271 | if (void 0 != a.radius_bubble) var t = a.radius_bubble;
272 | else var t = a.radius;
273 | if (void 0 != a.opacity_bubble) var s = a.opacity_bubble;
274 | else var s = a.opacity;
275 | if (a.color.rgb) var n = "rgba(" + a.color.rgb.r + "," + a.color.rgb.g + "," + a.color.rgb.b + "," + s + ")";
276 | else var n = "hsla(" + a.color.hsl.h + "," + a.color.hsl.s + "%," + a.color.hsl.l + "%," + s + ")";
277 | switch (i.canvas.ctx.fillStyle = n, i.canvas.ctx.beginPath(), a.shape) {
278 | case "circle":
279 | i.canvas.ctx.arc(a.x, a.y, t, 0, 2 * Math.PI, !1);
280 | break;
281 | case "edge":
282 | i.canvas.ctx.rect(a.x - t, a.y - t, 2 * t, 2 * t);
283 | break;
284 | case "triangle":
285 | i.fn.vendors.drawShape(i.canvas.ctx, a.x - t, a.y + t / 1.66, 2 * t, 3, 2);
286 | break;
287 | case "polygon":
288 | i.fn.vendors.drawShape(i.canvas.ctx, a.x - t / (i.particles.shape.polygon.nb_sides / 3.5), a.y - t / .76, 2.66 * t / (i.particles.shape.polygon.nb_sides / 3), i.particles.shape.polygon.nb_sides, 1);
289 | break;
290 | case "star":
291 | i.fn.vendors.drawShape(i.canvas.ctx, a.x - 2 * t / (i.particles.shape.polygon.nb_sides / 4), a.y - t / 1.52, 2 * t * 2.66 / (i.particles.shape.polygon.nb_sides / 3), i.particles.shape.polygon.nb_sides, 2);
292 | break;
293 | case "image":
294 | if ("svg" == i.tmp.img_type) var r = a.img.obj;
295 | else var r = i.tmp.img_obj;
296 | r && e()
297 | }
298 | i.canvas.ctx.closePath(), i.particles.shape.stroke.width > 0 && (i.canvas.ctx.strokeStyle = i.particles.shape.stroke.color, i.canvas.ctx.lineWidth = i.particles.shape.stroke.width, i.canvas.ctx.stroke()), i.canvas.ctx.fill()
299 | }, i.fn.particlesCreate = function () {
300 | for (var e = 0; e < i.particles.number.value; e++) i.particles.array.push(new i.fn.particle(i.particles.color, i.particles.opacity.value))
301 | }, i.fn.particlesUpdate = function () {
302 | for (var e = 0; e < i.particles.array.length; e++) {
303 | var a = i.particles.array[e];
304 | if (i.particles.move.enable) {
305 | var t = i.particles.move.speed / 2;
306 | a.x += a.vx * t, a.y += a.vy * t
307 | }
308 | if (i.particles.opacity.anim.enable && (1 == a.opacity_status ? (a.opacity >= i.particles.opacity.value && (a.opacity_status = !1), a.opacity += a.vo) : (a.opacity <= i.particles.opacity.anim.opacity_min && (a.opacity_status = !0), a.opacity -= a.vo), a.opacity < 0 && (a.opacity = 0)), i.particles.size.anim.enable && (1 == a.size_status ? (a.radius >= i.particles.size.value && (a.size_status = !1), a.radius += a.vs) : (a.radius <= i.particles.size.anim.size_min && (a.size_status = !0), a.radius -= a.vs), a.radius < 0 && (a.radius = 0)), "bounce" == i.particles.move.out_mode) var s = {
309 | x_left: a.radius,
310 | x_right: i.canvas.w,
311 | y_top: a.radius,
312 | y_bottom: i.canvas.h
313 | };
314 | else var s = {
315 | x_left: -a.radius,
316 | x_right: i.canvas.w + a.radius,
317 | y_top: -a.radius,
318 | y_bottom: i.canvas.h + a.radius
319 | };
320 | switch (a.x - a.radius > i.canvas.w ? (a.x = s.x_left, a.y = Math.random() * i.canvas.h) : a.x + a.radius < 0 && (a.x = s.x_right, a.y = Math.random() * i.canvas.h), a.y - a.radius > i.canvas.h ? (a.y = s.y_top, a.x = Math.random() * i.canvas.w) : a.y + a.radius < 0 && (a.y = s.y_bottom, a.x = Math.random() * i.canvas.w), i.particles.move.out_mode) {
321 | case "bounce":
322 | a.x + a.radius > i.canvas.w ? a.vx = -a.vx : a.x - a.radius < 0 && (a.vx = -a.vx), a.y + a.radius > i.canvas.h ? a.vy = -a.vy : a.y - a.radius < 0 && (a.vy = -a.vy)
323 | }
324 | if (isInArray("grab", i.interactivity.events.onhover.mode) && i.fn.modes.grabParticle(a), (isInArray("bubble", i.interactivity.events.onhover.mode) || isInArray("bubble", i.interactivity.events.onclick.mode)) && i.fn.modes.bubbleParticle(a), (isInArray("repulse", i.interactivity.events.onhover.mode) || isInArray("repulse", i.interactivity.events.onclick.mode)) && i.fn.modes.repulseParticle(a), i.particles.line_linked.enable || i.particles.move.attract.enable)
325 | for (var n = e + 1; n < i.particles.array.length; n++) {
326 | var r = i.particles.array[n];
327 | i.particles.line_linked.enable && i.fn.interact.linkParticles(a, r), i.particles.move.attract.enable && i.fn.interact.attractParticles(a, r), i.particles.move.bounce && i.fn.interact.bounceParticles(a, r)
328 | }
329 | }
330 | }, i.fn.particlesDraw = function () {
331 | i.canvas.ctx.clearRect(0, 0, i.canvas.w, i.canvas.h), i.fn.particlesUpdate();
332 | for (var e = 0; e < i.particles.array.length; e++) {
333 | var a = i.particles.array[e];
334 | a.draw()
335 | }
336 | }, i.fn.particlesEmpty = function () {
337 | i.particles.array = []
338 | }, i.fn.particlesRefresh = function () {
339 | cancelRequestAnimFrame(i.fn.checkAnimFrame), cancelRequestAnimFrame(i.fn.drawAnimFrame), i.tmp.source_svg = void 0, i.tmp.img_obj = void 0, i.tmp.count_svg = 0, i.fn.particlesEmpty(), i.fn.canvasClear(), i.fn.vendors.start()
340 | }, i.fn.interact.linkParticles = function (e, a) {
341 | var t = e.x - a.x,
342 | s = e.y - a.y,
343 | n = Math.sqrt(t * t + s * s);
344 | if (n <= i.particles.line_linked.distance) {
345 | var r = i.particles.line_linked.opacity - n / (1 / i.particles.line_linked.opacity) / i.particles.line_linked.distance;
346 | if (r > 0) {
347 | var c = i.particles.line_linked.color_rgb_line;
348 | i.canvas.ctx.strokeStyle = "rgba(" + c.r + "," + c.g + "," + c.b + "," + r + ")", i.canvas.ctx.lineWidth = i.particles.line_linked.width, i.canvas.ctx.beginPath(), i.canvas.ctx.moveTo(e.x, e.y), i.canvas.ctx.lineTo(a.x, a.y), i.canvas.ctx.stroke(), i.canvas.ctx.closePath()
349 | }
350 | }
351 | }, i.fn.interact.attractParticles = function (e, a) {
352 | var t = e.x - a.x,
353 | s = e.y - a.y,
354 | n = Math.sqrt(t * t + s * s);
355 | if (n <= i.particles.line_linked.distance) {
356 | var r = t / (1e3 * i.particles.move.attract.rotateX),
357 | c = s / (1e3 * i.particles.move.attract.rotateY);
358 | e.vx -= r, e.vy -= c, a.vx += r, a.vy += c
359 | }
360 | }, i.fn.interact.bounceParticles = function (e, a) {
361 | var t = e.x - a.x,
362 | i = e.y - a.y,
363 | s = Math.sqrt(t * t + i * i),
364 | n = e.radius + a.radius;
365 | n >= s && (e.vx = -e.vx, e.vy = -e.vy, a.vx = -a.vx, a.vy = -a.vy)
366 | }, i.fn.modes.pushParticles = function (e, a) {
367 | i.tmp.pushing = !0;
368 | for (var t = 0; e > t; t++) i.particles.array.push(new i.fn.particle(i.particles.color, i.particles.opacity.value, {
369 | x: a ? a.pos_x : Math.random() * i.canvas.w,
370 | y: a ? a.pos_y : Math.random() * i.canvas.h
371 | })), t == e - 1 && (i.particles.move.enable || i.fn.particlesDraw(), i.tmp.pushing = !1)
372 | }, i.fn.modes.removeParticles = function (e) {
373 | i.particles.array.splice(0, e), i.particles.move.enable || i.fn.particlesDraw()
374 | }, i.fn.modes.bubbleParticle = function (e) {
375 | function a() {
376 | e.opacity_bubble = e.opacity, e.radius_bubble = e.radius
377 | }
378 |
379 | function t(a, t, s, n, c) {
380 | if (a != t)
381 | if (i.tmp.bubble_duration_end) {
382 | if (void 0 != s) {
383 | var o = n - p * (n - a) / i.interactivity.modes.bubble.duration,
384 | l = a - o;
385 | d = a + l, "size" == c && (e.radius_bubble = d), "opacity" == c && (e.opacity_bubble = d)
386 | }
387 | } else if (r <= i.interactivity.modes.bubble.distance) {
388 | if (void 0 != s) var v = s;
389 | else var v = n;
390 | if (v != a) {
391 | var d = n - p * (n - a) / i.interactivity.modes.bubble.duration;
392 | "size" == c && (e.radius_bubble = d), "opacity" == c && (e.opacity_bubble = d)
393 | }
394 | } else "size" == c && (e.radius_bubble = void 0), "opacity" == c && (e.opacity_bubble = void 0)
395 | }
396 | if (i.interactivity.events.onhover.enable && isInArray("bubble", i.interactivity.events.onhover.mode)) {
397 | var s = e.x - i.interactivity.mouse.pos_x,
398 | n = e.y - i.interactivity.mouse.pos_y,
399 | r = Math.sqrt(s * s + n * n),
400 | c = 1 - r / i.interactivity.modes.bubble.distance;
401 | if (r <= i.interactivity.modes.bubble.distance) {
402 | if (c >= 0 && "mousemove" == i.interactivity.status) {
403 | if (i.interactivity.modes.bubble.size != i.particles.size.value)
404 | if (i.interactivity.modes.bubble.size > i.particles.size.value) {
405 | var o = e.radius + i.interactivity.modes.bubble.size * c;
406 | o >= 0 && (e.radius_bubble = o)
407 | } else {
408 | var l = e.radius - i.interactivity.modes.bubble.size,
409 | o = e.radius - l * c;
410 | o > 0 ? e.radius_bubble = o : e.radius_bubble = 0
411 | } if (i.interactivity.modes.bubble.opacity != i.particles.opacity.value)
412 | if (i.interactivity.modes.bubble.opacity > i.particles.opacity.value) {
413 | var v = i.interactivity.modes.bubble.opacity * c;
414 | v > e.opacity && v <= i.interactivity.modes.bubble.opacity && (e.opacity_bubble = v)
415 | } else {
416 | var v = e.opacity - (i.particles.opacity.value - i.interactivity.modes.bubble.opacity) * c;
417 | v < e.opacity && v >= i.interactivity.modes.bubble.opacity && (e.opacity_bubble = v)
418 | }
419 | }
420 | } else a();
421 | "mouseleave" == i.interactivity.status && a()
422 | } else if (i.interactivity.events.onclick.enable && isInArray("bubble", i.interactivity.events.onclick.mode)) {
423 | if (i.tmp.bubble_clicking) {
424 | var s = e.x - i.interactivity.mouse.click_pos_x,
425 | n = e.y - i.interactivity.mouse.click_pos_y,
426 | r = Math.sqrt(s * s + n * n),
427 | p = ((new Date).getTime() - i.interactivity.mouse.click_time) / 1e3;
428 | p > i.interactivity.modes.bubble.duration && (i.tmp.bubble_duration_end = !0), p > 2 * i.interactivity.modes.bubble.duration && (i.tmp.bubble_clicking = !1, i.tmp.bubble_duration_end = !1)
429 | }
430 | i.tmp.bubble_clicking && (t(i.interactivity.modes.bubble.size, i.particles.size.value, e.radius_bubble, e.radius, "size"), t(i.interactivity.modes.bubble.opacity, i.particles.opacity.value, e.opacity_bubble, e.opacity, "opacity"))
431 | }
432 | }, i.fn.modes.repulseParticle = function (e) {
433 | function a() {
434 | var a = Math.atan2(d, p);
435 | if (e.vx = u * Math.cos(a), e.vy = u * Math.sin(a), "bounce" == i.particles.move.out_mode) {
436 | var t = {
437 | x: e.x + e.vx,
438 | y: e.y + e.vy
439 | };
440 | t.x + e.radius > i.canvas.w ? e.vx = -e.vx : t.x - e.radius < 0 && (e.vx = -e.vx), t.y + e.radius > i.canvas.h ? e.vy = -e.vy : t.y - e.radius < 0 && (e.vy = -e.vy)
441 | }
442 | }
443 | if (i.interactivity.events.onhover.enable && isInArray("repulse", i.interactivity.events.onhover.mode) && "mousemove" == i.interactivity.status) {
444 | var t = e.x - i.interactivity.mouse.pos_x,
445 | s = e.y - i.interactivity.mouse.pos_y,
446 | n = Math.sqrt(t * t + s * s),
447 | r = {
448 | x: t / n,
449 | y: s / n
450 | },
451 | c = i.interactivity.modes.repulse.distance,
452 | o = 100,
453 | l = clamp(1 / c * (-1 * Math.pow(n / c, 2) + 1) * c * o, 0, 50),
454 | v = {
455 | x: e.x + r.x * l,
456 | y: e.y + r.y * l
457 | };
458 | "bounce" == i.particles.move.out_mode ? (v.x - e.radius > 0 && v.x + e.radius < i.canvas.w && (e.x = v.x), v.y - e.radius > 0 && v.y + e.radius < i.canvas.h && (e.y = v.y)) : (e.x = v.x, e.y = v.y)
459 | } else if (i.interactivity.events.onclick.enable && isInArray("repulse", i.interactivity.events.onclick.mode))
460 | if (i.tmp.repulse_finish || (i.tmp.repulse_count++, i.tmp.repulse_count == i.particles.array.length && (i.tmp.repulse_finish = !0)), i.tmp.repulse_clicking) {
461 | var c = Math.pow(i.interactivity.modes.repulse.distance / 6, 3),
462 | p = i.interactivity.mouse.click_pos_x - e.x,
463 | d = i.interactivity.mouse.click_pos_y - e.y,
464 | m = p * p + d * d,
465 | u = -c / m * 1;
466 | c >= m && a()
467 | } else 0 == i.tmp.repulse_clicking && (e.vx = e.vx_i, e.vy = e.vy_i)
468 | }, i.fn.modes.grabParticle = function (e) {
469 | if (i.interactivity.events.onhover.enable && "mousemove" == i.interactivity.status) {
470 | var a = e.x - i.interactivity.mouse.pos_x,
471 | t = e.y - i.interactivity.mouse.pos_y,
472 | s = Math.sqrt(a * a + t * t);
473 | if (s <= i.interactivity.modes.grab.distance) {
474 | var n = i.interactivity.modes.grab.line_linked.opacity - s / (1 / i.interactivity.modes.grab.line_linked.opacity) / i.interactivity.modes.grab.distance;
475 | if (n > 0) {
476 | var r = i.particles.line_linked.color_rgb_line;
477 | i.canvas.ctx.strokeStyle = "rgba(" + r.r + "," + r.g + "," + r.b + "," + n + ")", i.canvas.ctx.lineWidth = i.particles.line_linked.width, i.canvas.ctx.beginPath(), i.canvas.ctx.moveTo(e.x, e.y), i.canvas.ctx.lineTo(i.interactivity.mouse.pos_x, i.interactivity.mouse.pos_y), i.canvas.ctx.stroke(), i.canvas.ctx.closePath()
478 | }
479 | }
480 | }
481 | }, i.fn.vendors.eventsListeners = function () {
482 | "window" == i.interactivity.detect_on ? i.interactivity.el = window : i.interactivity.el = i.canvas.el, (i.interactivity.events.onhover.enable || i.interactivity.events.onclick.enable) && (i.interactivity.el.addEventListener("mousemove", function (e) {
483 | if (i.interactivity.el == window) var a = e.clientX,
484 | t = e.clientY;
485 | else var a = e.offsetX || e.clientX,
486 | t = e.offsetY || e.clientY;
487 | i.interactivity.mouse.pos_x = a, i.interactivity.mouse.pos_y = t, i.tmp.retina && (i.interactivity.mouse.pos_x *= i.canvas.pxratio, i.interactivity.mouse.pos_y *= i.canvas.pxratio), i.interactivity.status = "mousemove"
488 | }), i.interactivity.el.addEventListener("mouseleave", function (e) {
489 | i.interactivity.mouse.pos_x = null, i.interactivity.mouse.pos_y = null, i.interactivity.status = "mouseleave"
490 | })), i.interactivity.events.onclick.enable && i.interactivity.el.addEventListener("click", function () {
491 | if (i.interactivity.mouse.click_pos_x = i.interactivity.mouse.pos_x, i.interactivity.mouse.click_pos_y = i.interactivity.mouse.pos_y, i.interactivity.mouse.click_time = (new Date).getTime(), i.interactivity.events.onclick.enable) switch (i.interactivity.events.onclick.mode) {
492 | case "push":
493 | i.particles.move.enable ? i.fn.modes.pushParticles(i.interactivity.modes.push.particles_nb, i.interactivity.mouse) : 1 == i.interactivity.modes.push.particles_nb ? i.fn.modes.pushParticles(i.interactivity.modes.push.particles_nb, i.interactivity.mouse) : i.interactivity.modes.push.particles_nb > 1 && i.fn.modes.pushParticles(i.interactivity.modes.push.particles_nb);
494 | break;
495 | case "remove":
496 | i.fn.modes.removeParticles(i.interactivity.modes.remove.particles_nb);
497 | break;
498 | case "bubble":
499 | i.tmp.bubble_clicking = !0;
500 | break;
501 | case "repulse":
502 | i.tmp.repulse_clicking = !0, i.tmp.repulse_count = 0, i.tmp.repulse_finish = !1, setTimeout(function () {
503 | i.tmp.repulse_clicking = !1
504 | }, 1e3 * i.interactivity.modes.repulse.duration)
505 | }
506 | })
507 | }, i.fn.vendors.densityAutoParticles = function () {
508 | if (i.particles.number.density.enable) {
509 | var e = i.canvas.el.width * i.canvas.el.height / 1e3;
510 | i.tmp.retina && (e /= 2 * i.canvas.pxratio);
511 | var a = e * i.particles.number.value / i.particles.number.density.value_area,
512 | t = i.particles.array.length - a;
513 | 0 > t ? i.fn.modes.pushParticles(Math.abs(t)) : i.fn.modes.removeParticles(t)
514 | }
515 | }, i.fn.vendors.checkOverlap = function (e, a) {
516 | for (var t = 0; t < i.particles.array.length; t++) {
517 | var s = i.particles.array[t],
518 | n = e.x - s.x,
519 | r = e.y - s.y,
520 | c = Math.sqrt(n * n + r * r);
521 | c <= e.radius + s.radius && (e.x = a ? a.x : Math.random() * i.canvas.w, e.y = a ? a.y : Math.random() * i.canvas.h, i.fn.vendors.checkOverlap(e))
522 | }
523 | }, i.fn.vendors.createSvgImg = function (e) {
524 | var a = i.tmp.source_svg,
525 | t = /#([0-9A-F]{3,6})/gi,
526 | s = a.replace(t, function (a, t, i, s) {
527 | if (e.color.rgb) var n = "rgba(" + e.color.rgb.r + "," + e.color.rgb.g + "," + e.color.rgb.b + "," + e.opacity + ")";
528 | else var n = "hsla(" + e.color.hsl.h + "," + e.color.hsl.s + "%," + e.color.hsl.l + "%," + e.opacity + ")";
529 | return n
530 | }),
531 | n = new Blob([s], {
532 | type: "image/svg+xml;charset=utf-8"
533 | }),
534 | r = window.URL || window.webkitURL || window,
535 | c = r.createObjectURL(n),
536 | o = new Image;
537 | o.addEventListener("load", function () {
538 | e.img.obj = o, e.img.loaded = !0, r.revokeObjectURL(c), i.tmp.count_svg++
539 | }), o.src = c
540 | }, i.fn.vendors.destroypJS = function () {
541 | cancelAnimationFrame(i.fn.drawAnimFrame), t.remove(), pJSDom = null
542 | }, i.fn.vendors.drawShape = function (e, a, t, i, s, n) {
543 | var r = s * n,
544 | c = s / n,
545 | o = 180 * (c - 2) / c,
546 | l = Math.PI - Math.PI * o / 180;
547 | e.save(), e.beginPath(), e.translate(a, t), e.moveTo(0, 0);
548 | for (var v = 0; r > v; v++) e.lineTo(i, 0), e.translate(i, 0), e.rotate(l);
549 | e.fill(), e.restore()
550 | }, i.fn.vendors.exportImg = function () {
551 | window.open(i.canvas.el.toDataURL("image/png"), "_blank")
552 | }, i.fn.vendors.loadImg = function (e) {
553 | if (i.tmp.img_error = void 0, "" != i.particles.shape.image.src)
554 | if ("svg" == e) {
555 | var a = new XMLHttpRequest;
556 | a.open("GET", i.particles.shape.image.src), a.onreadystatechange = function (e) {
557 | 4 == a.readyState && (200 == a.status ? (i.tmp.source_svg = e.currentTarget.response, i.fn.vendors.checkBeforeDraw()) : (console.log("Error pJS - Image not found"), i.tmp.img_error = !0))
558 | }, a.send()
559 | } else {
560 | var t = new Image;
561 | t.addEventListener("load", function () {
562 | i.tmp.img_obj = t, i.fn.vendors.checkBeforeDraw()
563 | }), t.src = i.particles.shape.image.src
564 | }
565 | else console.log("Error pJS - No image.src"), i.tmp.img_error = !0
566 | }, i.fn.vendors.draw = function () {
567 | "image" == i.particles.shape.type ? "svg" == i.tmp.img_type ? i.tmp.count_svg >= i.particles.number.value ? (i.fn.particlesDraw(), i.particles.move.enable ? i.fn.drawAnimFrame = requestAnimFrame(i.fn.vendors.draw) : cancelRequestAnimFrame(i.fn.drawAnimFrame)) : i.tmp.img_error || (i.fn.drawAnimFrame = requestAnimFrame(i.fn.vendors.draw)) : void 0 != i.tmp.img_obj ? (i.fn.particlesDraw(), i.particles.move.enable ? i.fn.drawAnimFrame = requestAnimFrame(i.fn.vendors.draw) : cancelRequestAnimFrame(i.fn.drawAnimFrame)) : i.tmp.img_error || (i.fn.drawAnimFrame = requestAnimFrame(i.fn.vendors.draw)) : (i.fn.particlesDraw(), i.particles.move.enable ? i.fn.drawAnimFrame = requestAnimFrame(i.fn.vendors.draw) : cancelRequestAnimFrame(i.fn.drawAnimFrame))
568 | }, i.fn.vendors.checkBeforeDraw = function () {
569 | "image" == i.particles.shape.type ? "svg" == i.tmp.img_type && void 0 == i.tmp.source_svg ? i.tmp.checkAnimFrame = requestAnimFrame(check) : (cancelRequestAnimFrame(i.tmp.checkAnimFrame), i.tmp.img_error || (i.fn.vendors.init(), i.fn.vendors.draw())) : (i.fn.vendors.init(), i.fn.vendors.draw())
570 | }, i.fn.vendors.init = function () {
571 | i.fn.retinaInit(), i.fn.canvasInit(), i.fn.canvasSize(), i.fn.canvasPaint(), i.fn.particlesCreate(), i.fn.vendors.densityAutoParticles(), i.particles.line_linked.color_rgb_line = hexToRgb(i.particles.line_linked.color)
572 | }, i.fn.vendors.start = function () {
573 | isInArray("image", i.particles.shape.type) ? (i.tmp.img_type = i.particles.shape.image.src.substr(i.particles.shape.image.src.length - 3), i.fn.vendors.loadImg(i.tmp.img_type)) : i.fn.vendors.checkBeforeDraw()
574 | }, i.fn.vendors.eventsListeners(), i.fn.vendors.start()
575 | };
576 | Object.deepExtend = function (e, a) {
577 | for (var t in a) a[t] && a[t].constructor && a[t].constructor === Object ? (e[t] = e[t] || {}, arguments.callee(e[t], a[t])) : e[t] = a[t];
578 | return e
579 | }, window.requestAnimFrame = function () {
580 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (e) {
581 | window.setTimeout(e, 1e3 / 60)
582 | }
583 | }(), window.cancelRequestAnimFrame = function () {
584 | return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout
585 | }(), window.pJSDom = [], window.particlesJS = function (e, a) {
586 | "string" != typeof e && (a = e, e = "particles-js"), e || (e = "particles-js");
587 | var t = document.getElementById(e),
588 | i = "particles-js-canvas-el",
589 | s = t.getElementsByClassName(i);
590 | if (s.length)
591 | for (; s.length > 0;) t.removeChild(s[0]);
592 | var n = document.createElement("canvas");
593 | n.className = i, n.style.width = "100%", n.style.height = "100%";
594 | var r = document.getElementById(e).appendChild(n);
595 | null != r && pJSDom.push(new pJS(e, a))
596 | }, window.particlesJS.load = function (e, a, t) {
597 | var i = new XMLHttpRequest;
598 | i.open("GET", a), i.onreadystatechange = function (a) {
599 | if (4 == i.readyState)
600 | if (200 == i.status) {
601 | var s = JSON.parse(a.currentTarget.response);
602 | window.particlesJS(e, s), t && t()
603 | } else console.log("Error pJS - XMLHttpRequest status: " + i.status), console.log("Error pJS - File config not found")
604 | }, i.send()
605 | };
--------------------------------------------------------------------------------