Work on big refactor
This commit is contained in:
parent
ae77251883
commit
1d5c7ea24b
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
File diff suppressed because it is too large
Load Diff
1007
.idea/workspace.xml
1007
.idea/workspace.xml
File diff suppressed because it is too large
Load Diff
@ -28,11 +28,13 @@ DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
SESSION_COOKIE_AGE = 3600 * 30 # one month
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'YtManagerApp.apps.YtManagerAppConfig',
|
||||
'crispy_forms',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
@ -116,7 +118,6 @@ USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||
|
||||
@ -124,19 +125,6 @@ STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = 'D:\\Dev\\youtube-channel-manager\\temp\\media'
|
||||
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
|
||||
# Application settings
|
||||
# These should be moved to an ini file or something
|
||||
DOWNLOADER_PATH = "D:\\Dev\\youtube-channel-manager\\temp\\download"
|
||||
DOWNLOADER_FILE_PATTERN = "{channel}/{playlist}/S01E{playlist_index} - {title} [{id}].{ext}"
|
||||
DOWNLOADER_FORMAT = "bestvideo+bestaudio"
|
||||
DOWNLOADER_SUBTITLES = True
|
||||
DOWNLOADER_SUBTITLES_LANGS = None
|
||||
DOWNLOADER_AUTOGENERATED_SUBTITLES = False
|
||||
DOWNLOADER_DEFAULT_DOWNLOAD_ENABLED = True
|
||||
DOWNLOADER_DEFAULT_DOWNLOAD_LIMIT = 5 # -1 = no limit (all)
|
||||
DOWNLOADER_DEFAULT_ORDER = 'playlist_index' # publish_date, playlist_index
|
||||
MANAGER_MARK_DELETED_AS_WATCHED = True
|
||||
MANAGER_DEFAULT_DELETE_AFTER_WATCHED = True
|
||||
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
@ -13,21 +13,11 @@ Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
|
||||
from YtManagerApp import views
|
||||
from django.urls import path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('ajax/get_children', views.ajax_get_children, name='ajax_get_children'),
|
||||
path('ajax/get_folders', views.ajax_get_folders, name='ajax_get_folders'),
|
||||
path('ajax/edit_folder', views.ajax_edit_folder, name='ajax_edit_folder'),
|
||||
path('ajax/delete_folder/<int:fid>/', views.ajax_delete_folder, name='ajax_delete_folder'),
|
||||
path('ajax/edit_subscription', views.ajax_edit_subscription, name='ajax_edit_subscription'),
|
||||
path('ajax/delete_subscription/<int:sid>/', views.ajax_delete_subscription, name='ajax_delete_subscription'),
|
||||
path('ajax/list_videos', views.ajax_list_videos, name='ajax_list_videos'),
|
||||
path(r'', views.index, name='home')
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
path('', include('YtManagerApp.urls')),
|
||||
]
|
||||
|
@ -3,106 +3,90 @@ import os
|
||||
import os.path
|
||||
from shutil import copyfile
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf import settings as dj_settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import UserSettings
|
||||
from .utils.customconfigparser import ConfigParserWithEnv
|
||||
|
||||
__SETTINGS_FILE = 'config.ini'
|
||||
__LOG_FILE = 'log.log'
|
||||
__LOG_FORMAT = '%(asctime)s|%(process)d|%(thread)d|%(name)s|%(filename)s|%(lineno)d|%(levelname)s|%(message)s'
|
||||
|
||||
class AppConfig(object):
|
||||
__SETTINGS_FILE = 'config.ini'
|
||||
__LOG_FILE = 'log.log'
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
'global': {
|
||||
'YouTubeApiKey': 'AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8',
|
||||
'SynchronizationSchedule': '0 * * * * *',
|
||||
'SchedulerConcurrency': '2',
|
||||
},
|
||||
'user': {
|
||||
'MarkDeletedAsWatched': 'True',
|
||||
'DeleteWatched': 'True',
|
||||
'AutoDownload': 'True',
|
||||
'DownloadMaxAttempts': '3',
|
||||
'DownloadGlobalLimit': '',
|
||||
'DownloadSubscriptionLimit': '5',
|
||||
'DownloadOrder': 'playlist_index',
|
||||
'DownloadPath': '${env:USERPROFILE}${env:HOME}/Downloads',
|
||||
'DownloadFilePattern': '${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]',
|
||||
'DownloadFormat': 'bestvideo+bestaudio',
|
||||
'DownloadSubtitles': 'True',
|
||||
'DownloadAutogeneratedSubtitles': 'False',
|
||||
'DownloadSubtitlesAll': 'False',
|
||||
'DownloadSubtitlesLangs': 'en,ro',
|
||||
'DownloadSubtitlesFormat': '',
|
||||
}
|
||||
__DEFAULT_SETTINGS = {
|
||||
'global': {
|
||||
'YouTubeApiKey': 'AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8',
|
||||
'SynchronizationSchedule': '0 * * * *',
|
||||
'SchedulerConcurrency': '2',
|
||||
},
|
||||
'user': {
|
||||
'MarkDeletedAsWatched': 'True',
|
||||
'DeleteWatched': 'True',
|
||||
'AutoDownload': 'True',
|
||||
'DownloadMaxAttempts': '3',
|
||||
'DownloadGlobalLimit': '',
|
||||
'DownloadSubscriptionLimit': '5',
|
||||
'DownloadOrder': 'playlist_index',
|
||||
'DownloadPath': '${env:USERPROFILE}${env:HOME}/Downloads',
|
||||
'DownloadFilePattern': '${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]',
|
||||
'DownloadFormat': 'bestvideo+bestaudio',
|
||||
'DownloadSubtitles': 'True',
|
||||
'DownloadAutogeneratedSubtitles': 'False',
|
||||
'DownloadSubtitlesAll': 'False',
|
||||
'DownloadSubtitlesLangs': 'en,ro',
|
||||
'DownloadSubtitlesFormat': '',
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.log_path = os.path.join(settings.BASE_DIR, 'config', AppConfig.__LOG_FILE)
|
||||
self.settings_path = os.path.join(settings.BASE_DIR, 'config', AppConfig.__SETTINGS_FILE)
|
||||
|
||||
self.settings = ConfigParserWithEnv(defaults=AppConfig.DEFAULT_SETTINGS, allow_no_value=True)
|
||||
self.load_settings()
|
||||
|
||||
def load_settings(self):
|
||||
if os.path.exists(self.settings_path):
|
||||
with open(self.settings_path, 'r') as f:
|
||||
self.settings.read_file(f)
|
||||
|
||||
def save_settings(self):
|
||||
if os.path.exists(self.settings_path):
|
||||
# Create a backup
|
||||
copyfile(self.settings_path, self.settings_path + ".backup")
|
||||
else:
|
||||
# Ensure directory exists
|
||||
settings_dir = os.path.dirname(self.settings_path)
|
||||
os.makedirs(settings_dir, exist_ok=True)
|
||||
|
||||
with open(self.settings_path, 'w') as f:
|
||||
self.settings.write(f)
|
||||
|
||||
def get_user_config(self, user: User) -> ConfigParserWithEnv:
|
||||
user_settings = UserSettings.find_by_user(user)
|
||||
if user_settings is not None:
|
||||
user_config = ConfigParserWithEnv(defaults=self.settings, allow_no_value=True)
|
||||
user_config.read_dict({
|
||||
'user': user_settings.to_dict()
|
||||
})
|
||||
return user_config
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
instance: AppConfig = None
|
||||
log_path = os.path.join(dj_settings.BASE_DIR, 'config', __LOG_FILE)
|
||||
settings_path = os.path.join(dj_settings.BASE_DIR, 'config', __SETTINGS_FILE)
|
||||
settings = ConfigParserWithEnv(defaults=__DEFAULT_SETTINGS, allow_no_value=True)
|
||||
|
||||
|
||||
def __initialize_logger():
|
||||
# Parse log level
|
||||
log_level_str = instance.settings.get('global', 'LogLevel', fallback='INFO')
|
||||
levels = {
|
||||
'NOTSET': logging.NOTSET,
|
||||
'DEBUG': logging.DEBUG,
|
||||
'INFO': logging.INFO,
|
||||
'WARNING': logging.WARNING,
|
||||
'ERROR': logging.ERROR,
|
||||
'CRITICAL': logging.CRITICAL
|
||||
}
|
||||
if log_level_str.upper() not in levels:
|
||||
log_level_str = 'INFO'
|
||||
log_level_str = settings.get('global', 'LogLevel', fallback='INFO')
|
||||
|
||||
# Init
|
||||
logging.basicConfig(filename=instance.log_path, level=levels[log_level_str])
|
||||
try:
|
||||
log_level = getattr(logging, log_level_str)
|
||||
logging.basicConfig(filename=log_path, level=log_level, format=__LOG_FORMAT)
|
||||
|
||||
except AttributeError:
|
||||
logging.basicConfig(filename=log_path, level=logging.INFO, format=__LOG_FORMAT)
|
||||
logging.warning('Invalid log level "%s" in config file.', log_level_str)
|
||||
|
||||
|
||||
def initialize_config():
|
||||
global instance
|
||||
instance = AppConfig()
|
||||
|
||||
# Load settings
|
||||
instance.load_settings()
|
||||
|
||||
# Initialize logger
|
||||
def initialize_app_config():
|
||||
load_settings()
|
||||
__initialize_logger()
|
||||
logging.info('Application started!')
|
||||
|
||||
|
||||
def load_settings():
|
||||
if os.path.exists(settings_path):
|
||||
with open(settings_path, 'r') as f:
|
||||
settings.read_file(f)
|
||||
|
||||
|
||||
def save_settings():
|
||||
if os.path.exists(settings_path):
|
||||
# Create a backup
|
||||
copyfile(settings_path, settings_path + ".backup")
|
||||
else:
|
||||
# Ensure directory exists
|
||||
settings_dir = os.path.dirname(settings_path)
|
||||
os.makedirs(settings_dir, exist_ok=True)
|
||||
|
||||
with open(settings_path, 'w') as f:
|
||||
settings.write(f)
|
||||
|
||||
|
||||
def get_user_config(user: User) -> ConfigParserWithEnv:
|
||||
user_settings = UserSettings.find_by_user(user)
|
||||
if user_settings is not None:
|
||||
user_config = ConfigParserWithEnv(defaults=settings, allow_no_value=True)
|
||||
user_config.read_dict({
|
||||
'user': user_settings.to_dict()
|
||||
})
|
||||
return user_config
|
||||
|
||||
return settings
|
||||
|
@ -1,11 +1,11 @@
|
||||
from .appconfig import initialize_config
|
||||
from .appconfig import initialize_app_config
|
||||
from .scheduler import initialize_scheduler
|
||||
from .management import setup_synchronization_job
|
||||
from .management.jobs.synchronize import schedule_synchronize
|
||||
import logging
|
||||
|
||||
|
||||
def main():
|
||||
initialize_config()
|
||||
initialize_app_config()
|
||||
initialize_scheduler()
|
||||
setup_synchronization_job()
|
||||
schedule_synchronize()
|
||||
logging.info('Initialization complete.')
|
||||
|
@ -1,4 +1,4 @@
|
||||
from YtManagerApp.appconfig import instance as app_config
|
||||
from YtManagerApp import appconfig
|
||||
from YtManagerApp.management.jobs.download_video import schedule_download_video
|
||||
from YtManagerApp.models import Video, Subscription
|
||||
from django.conf import settings
|
||||
@ -12,7 +12,7 @@ log = logging.getLogger('downloader')
|
||||
|
||||
|
||||
def __get_subscription_config(sub: Subscription):
|
||||
user_config = app_config.get_user_config(sub.user)
|
||||
user_config = appconfig.get_user_config(sub.user)
|
||||
|
||||
enabled = sub.auto_download
|
||||
if enabled is None:
|
||||
|
@ -0,0 +1,37 @@
|
||||
from YtManagerApp.models import SubscriptionFolder, Subscription
|
||||
from typing import Callable, Union, Any, Optional
|
||||
from django.contrib.auth.models import User
|
||||
import logging
|
||||
|
||||
|
||||
def traverse_tree(root_folder_id: Optional[int], user: User, visit_func: Callable[[Union[SubscriptionFolder, Subscription]], Any]):
|
||||
data_collected = []
|
||||
|
||||
def collect(data):
|
||||
if data is not None:
|
||||
data_collected.append(data)
|
||||
|
||||
# Visit root
|
||||
if root_folder_id is not None:
|
||||
root_folder = SubscriptionFolder.objects.get(id = root_folder_id)
|
||||
collect(visit_func(root_folder))
|
||||
|
||||
queue = [root_folder_id]
|
||||
visited = []
|
||||
|
||||
while len(queue) > 0:
|
||||
folder_id = queue.pop()
|
||||
|
||||
if folder_id in visited:
|
||||
logging.error('Found folder tree cycle for folder id %d.', folder_id)
|
||||
continue
|
||||
visited.append(folder_id)
|
||||
|
||||
for folder in SubscriptionFolder.objects.filter(parent_id=folder_id, user=user).order_by('name'):
|
||||
collect(visit_func(folder))
|
||||
queue.append(folder.id)
|
||||
|
||||
for subscription in Subscription.objects.filter(parent_folder_id=folder_id, user=user).order_by('name'):
|
||||
collect(visit_func(subscription))
|
||||
|
||||
return data_collected
|
@ -1,6 +1,6 @@
|
||||
from YtManagerApp.models import Video
|
||||
from YtManagerApp.scheduler import instance as scheduler
|
||||
from YtManagerApp.appconfig import instance as app_config
|
||||
from YtManagerApp import scheduler
|
||||
from YtManagerApp.appconfig import get_user_config
|
||||
import os
|
||||
import youtube_dl
|
||||
import logging
|
||||
@ -60,7 +60,7 @@ def download_video(video: Video, attempt: int = 1):
|
||||
|
||||
log.info('Downloading video %d [%s %s]', video.id, video.video_id, video.name)
|
||||
|
||||
user_config = app_config.get_user_config(video.subscription.user)
|
||||
user_config = get_user_config(video.subscription.user)
|
||||
max_attempts = user_config.getint('user', 'DownloadMaxAttempts', fallback=3)
|
||||
|
||||
youtube_dl_params, output_path = __build_youtube_dl_params(video, user_config)
|
||||
@ -76,7 +76,7 @@ def download_video(video: Video, attempt: int = 1):
|
||||
|
||||
elif attempt <= max_attempts:
|
||||
log.warning('Re-enqueueing video (attempt %d/%d)', attempt, max_attempts)
|
||||
scheduler.add_job(download_video, args=[video, attempt + 1])
|
||||
scheduler.instance.add_job(download_video, args=[video, attempt + 1])
|
||||
|
||||
else:
|
||||
log.error('Multiple attempts to download video %d [%s %s] failed!', video.id, video.video_id, video.name)
|
||||
@ -90,4 +90,4 @@ def schedule_download_video(video: Video):
|
||||
:param video:
|
||||
:return:
|
||||
"""
|
||||
scheduler.add_job(download_video, args=[video, 1])
|
||||
scheduler.instance.add_job(download_video, args=[video, 1])
|
||||
|
@ -1,46 +1,68 @@
|
||||
from YtManagerApp.models import *
|
||||
import logging
|
||||
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
from YtManagerApp.appconfig import settings
|
||||
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all
|
||||
from YtManagerApp.management.videos import create_video
|
||||
from YtManagerApp.models import *
|
||||
from YtManagerApp import scheduler
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI
|
||||
|
||||
log = logging.getLogger('sync')
|
||||
|
||||
|
||||
def __synchronize_sub(subscription: Subscription, yt_api: YoutubeAPI):
|
||||
# Get list of videos
|
||||
for video in yt_api.list_playlist_videos(subscription.playlist_id):
|
||||
results = Video.objects.filter(video_id=video.getVideoId(), subscription=subscription)
|
||||
if len(results) == 0:
|
||||
log.info('New video for subscription "', subscription, '": ', video.getVideoId(), video.getTitle())
|
||||
create_video(video, subscription)
|
||||
|
||||
|
||||
def __fetch_thumbnails_obj(iterable, obj_type, id_attr):
|
||||
for obj in iterable:
|
||||
if obj.icon_default.startswith("http"):
|
||||
obj.icon_default = fetch_thumbnail(obj.icon_default, obj_type, getattr(obj, id_attr), 'default')
|
||||
if obj.icon_best.startswith("http"):
|
||||
obj.icon_best = fetch_thumbnail(obj.icon_best, obj_type, getattr(obj, id_attr), 'best')
|
||||
obj.save()
|
||||
|
||||
|
||||
def __fetch_thumbnails():
|
||||
# Fetch thumbnails
|
||||
log.info("Fetching channel thumbnails... ")
|
||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_default__istartswith='http'), 'channel', 'channel_id')
|
||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_best__istartswith='http'), 'channel', 'channel_id')
|
||||
|
||||
log.info("Fetching subscription thumbnails... ")
|
||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_default__istartswith='http'), 'sub', 'playlist_id')
|
||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_best__istartswith='http'), 'sub', 'playlist_id')
|
||||
|
||||
log.info("Fetching video thumbnails... ")
|
||||
__fetch_thumbnails_obj(Video.objects.filter(icon_default__istartswith='http'), 'video', 'video_id')
|
||||
__fetch_thumbnails_obj(Video.objects.filter(icon_best__istartswith='http'), 'video', 'video_id')
|
||||
|
||||
|
||||
def synchronize():
|
||||
logger = logging.getLogger('sync')
|
||||
|
||||
logger.info("Running scheduled synchronization... ")
|
||||
log.info("Running scheduled synchronization... ")
|
||||
|
||||
# Sync subscribed playlists/channels
|
||||
log.info("Sync - checking for new videos")
|
||||
yt_api = YoutubeAPI.build_public()
|
||||
for subscription in Subscription.objects.all():
|
||||
SubscriptionManager.__synchronize(subscription, yt_api)
|
||||
__synchronize_sub(subscription, yt_api)
|
||||
|
||||
# Fetch thumbnails
|
||||
logger.info("Fetching channel thumbnails... ")
|
||||
for ch in Channel.objects.filter(icon_default__istartswith='http'):
|
||||
ch.icon_default = SubscriptionManager.__fetch_thumbnail(ch.icon_default, 'channel', ch.channel_id, 'default')
|
||||
ch.save()
|
||||
log.info("Sync - checking for videos to download")
|
||||
downloader_process_all()
|
||||
|
||||
for ch in Channel.objects.filter(icon_best__istartswith='http'):
|
||||
ch.icon_best = SubscriptionManager.__fetch_thumbnail(ch.icon_best, 'channel', ch.channel_id, 'best')
|
||||
ch.save()
|
||||
log.info("Sync - fetching missing thumbnails")
|
||||
__fetch_thumbnails()
|
||||
|
||||
logger.info("Fetching subscription thumbnails... ")
|
||||
for sub in Subscription.objects.filter(icon_default__istartswith='http'):
|
||||
sub.icon_default = SubscriptionManager.__fetch_thumbnail(sub.icon_default, 'sub', sub.playlist_id, 'default')
|
||||
sub.save()
|
||||
log.info("Synchronization finished.")
|
||||
|
||||
for sub in Subscription.objects.filter(icon_best__istartswith='http'):
|
||||
sub.icon_best = SubscriptionManager.__fetch_thumbnail(sub.icon_best, 'sub', sub.playlist_id, 'best')
|
||||
sub.save()
|
||||
|
||||
logger.info("Fetching video thumbnails... ")
|
||||
for vid in Video.objects.filter(icon_default__istartswith='http'):
|
||||
vid.icon_default = SubscriptionManager.__fetch_thumbnail(vid.icon_default, 'video', vid.video_id, 'default')
|
||||
vid.save()
|
||||
|
||||
for vid in Video.objects.filter(icon_best__istartswith='http'):
|
||||
vid.icon_best = SubscriptionManager.__fetch_thumbnail(vid.icon_best, 'video', vid.video_id, 'best')
|
||||
vid.save()
|
||||
|
||||
print("Downloading videos...")
|
||||
Downloader.download_all()
|
||||
|
||||
print("Synchronization finished.")
|
||||
def schedule_synchronize():
|
||||
trigger = CronTrigger.from_crontab(settings.get('global', 'SynchronizationSchedule'))
|
||||
scheduler.instance.add_job(synchronize, trigger, max_instances=1)
|
||||
|
@ -1,17 +1,7 @@
|
||||
from YtManagerApp.models import SubscriptionFolder, Subscription, Video, Channel
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo, YoutubePlaylistItem
|
||||
from django.conf import settings
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
import os
|
||||
import os.path
|
||||
import requests
|
||||
from urllib.parse import urljoin
|
||||
import mimetypes
|
||||
import youtube_dl
|
||||
|
||||
from YtManagerApp.scheduler import instance as scheduler
|
||||
from YtManagerApp.appconfig import instance as app_config
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from YtManagerApp.models import SubscriptionFolder, Subscription, Video, Channel
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo
|
||||
|
||||
|
||||
class FolderManager(object):
|
||||
@ -172,107 +162,3 @@ class SubscriptionManager(object):
|
||||
channel.icon_best = yt_info.getBestThumbnailUrl()
|
||||
channel.upload_playlist_id = yt_info.getUploadsPlaylist()
|
||||
channel.save()
|
||||
|
||||
@staticmethod
|
||||
def __create_video(yt_video: YoutubePlaylistItem, subscription: Subscription):
|
||||
video = Video()
|
||||
video.video_id = yt_video.getVideoId()
|
||||
video.name = yt_video.getTitle()
|
||||
video.description = yt_video.getDescription()
|
||||
video.watched = False
|
||||
video.downloaded_path = None
|
||||
video.subscription = subscription
|
||||
video.playlist_index = yt_video.getPlaylistIndex()
|
||||
video.publish_date = yt_video.getPublishDate()
|
||||
video.icon_default = yt_video.getDefaultThumbnailUrl()
|
||||
video.icon_best = yt_video.getBestThumbnailUrl()
|
||||
video.save()
|
||||
|
||||
@staticmethod
|
||||
def __synchronize(subscription: Subscription, yt_api: YoutubeAPI):
|
||||
# Get list of videos
|
||||
for video in yt_api.list_playlist_videos(subscription.playlist_id):
|
||||
results = Video.objects.filter(video_id=video.getVideoId(), subscription=subscription)
|
||||
if len(results) == 0:
|
||||
print('New video for subscription "', subscription, '": ', video.getVideoId(), video.getTitle())
|
||||
SubscriptionManager.__create_video(video, subscription)
|
||||
|
||||
@staticmethod
|
||||
def __synchronize_all():
|
||||
print("Running scheduled synchronization... ")
|
||||
|
||||
# Sync subscribed playlists/channels
|
||||
yt_api = YoutubeAPI.build_public()
|
||||
for subscription in Subscription.objects.all():
|
||||
SubscriptionManager.__synchronize(subscription, yt_api)
|
||||
|
||||
# Fetch thumbnails
|
||||
print("Fetching channel thumbnails... ")
|
||||
for ch in Channel.objects.filter(icon_default__istartswith='http'):
|
||||
ch.icon_default = SubscriptionManager.__fetch_thumbnail(ch.icon_default, 'channel', ch.channel_id, 'default')
|
||||
ch.save()
|
||||
|
||||
for ch in Channel.objects.filter(icon_best__istartswith='http'):
|
||||
ch.icon_best = SubscriptionManager.__fetch_thumbnail(ch.icon_best, 'channel', ch.channel_id, 'best')
|
||||
ch.save()
|
||||
|
||||
print("Fetching subscription thumbnails... ")
|
||||
for sub in Subscription.objects.filter(icon_default__istartswith='http'):
|
||||
sub.icon_default = SubscriptionManager.__fetch_thumbnail(sub.icon_default, 'sub', sub.playlist_id, 'default')
|
||||
sub.save()
|
||||
|
||||
for sub in Subscription.objects.filter(icon_best__istartswith='http'):
|
||||
sub.icon_best = SubscriptionManager.__fetch_thumbnail(sub.icon_best, 'sub', sub.playlist_id, 'best')
|
||||
sub.save()
|
||||
|
||||
print("Fetching video thumbnails... ")
|
||||
for vid in Video.objects.filter(icon_default__istartswith='http'):
|
||||
vid.icon_default = SubscriptionManager.__fetch_thumbnail(vid.icon_default, 'video', vid.video_id, 'default')
|
||||
vid.save()
|
||||
|
||||
for vid in Video.objects.filter(icon_best__istartswith='http'):
|
||||
vid.icon_best = SubscriptionManager.__fetch_thumbnail(vid.icon_best, 'video', vid.video_id, 'best')
|
||||
vid.save()
|
||||
|
||||
print("Downloading videos...")
|
||||
Downloader.download_all()
|
||||
|
||||
print("Synchronization finished.")
|
||||
|
||||
@staticmethod
|
||||
def __fetch_thumbnail(url, object_type, identifier, quality):
|
||||
|
||||
# Make request to obtain mime type
|
||||
response = requests.get(url, stream=True)
|
||||
ext = mimetypes.guess_extension(response.headers['Content-Type'])
|
||||
|
||||
# Build file path
|
||||
file_name = f"{identifier}-{quality}{ext}"
|
||||
abs_path_dir = os.path.join(settings.MEDIA_ROOT, "thumbs", object_type)
|
||||
abs_path = os.path.join(abs_path_dir, file_name)
|
||||
|
||||
# Store image
|
||||
os.makedirs(abs_path_dir, exist_ok=True)
|
||||
with open(abs_path, "wb") as f:
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
|
||||
# Return
|
||||
media_url = urljoin(settings.MEDIA_URL, f"thumbs/{object_type}/{file_name}")
|
||||
return media_url
|
||||
|
||||
@staticmethod
|
||||
def start_scheduler():
|
||||
SubscriptionManager.__scheduler.add_job(SubscriptionManager.__synchronize_all, 'cron',
|
||||
hour='*', minute=38, max_instances=1)
|
||||
SubscriptionManager.__scheduler.start()
|
||||
|
||||
|
||||
def setup_synchronization_job():
|
||||
trigger = CronTrigger.from_crontab(app_config.get('global', 'SynchronizationSchedule'))
|
||||
scheduler.add_job(synchronize_all, trigger, max_instances=1)
|
||||
|
||||
|
||||
def synchronize_all():
|
||||
pass
|
72
YtManagerApp/management/videos.py
Normal file
72
YtManagerApp/management/videos.py
Normal file
@ -0,0 +1,72 @@
|
||||
from YtManagerApp.models import Subscription, Video
|
||||
from YtManagerApp.utils.youtube import YoutubePlaylistItem
|
||||
from typing import Optional
|
||||
import re
|
||||
from django.db.models import Q
|
||||
from YtManagerApp.management.folders import traverse_tree
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
def create_video(yt_video: YoutubePlaylistItem, subscription: Subscription):
|
||||
video = Video()
|
||||
video.video_id = yt_video.getVideoId()
|
||||
video.name = yt_video.getTitle()
|
||||
video.description = yt_video.getDescription()
|
||||
video.watched = False
|
||||
video.downloaded_path = None
|
||||
video.subscription = subscription
|
||||
video.playlist_index = yt_video.getPlaylistIndex()
|
||||
video.publish_date = yt_video.getPublishDate()
|
||||
video.icon_default = yt_video.getDefaultThumbnailUrl()
|
||||
video.icon_best = yt_video.getBestThumbnailUrl()
|
||||
video.save()
|
||||
|
||||
|
||||
def get_videos(user: User,
|
||||
sort_order: Optional[str],
|
||||
query: Optional[str] = None,
|
||||
subscription_id: Optional[int] = None,
|
||||
folder_id: Optional[int] = None,
|
||||
only_watched: Optional[bool] = None,
|
||||
only_downloaded: Optional[bool] = None,
|
||||
):
|
||||
|
||||
filter_args = []
|
||||
filter_kwargs = {
|
||||
'subscription__user': user
|
||||
}
|
||||
|
||||
# Process query string - basically, we break it down into words,
|
||||
# and then search for the given text in the name, description, uploader name and subscription name
|
||||
if query is not None:
|
||||
for match in re.finditer(r'\w+', query):
|
||||
word = match[0]
|
||||
filter_args.append(Q(name__icontains=word)
|
||||
| Q(description__icontains=word)
|
||||
| Q(uploader_name__icontains=word)
|
||||
| Q(subscription__name__icontains=word))
|
||||
|
||||
# Subscription id
|
||||
if subscription_id is not None:
|
||||
filter_kwargs['subscription_id'] = subscription_id
|
||||
|
||||
# Folder id
|
||||
if folder_id is not None:
|
||||
# Visit function - returns only the subscription IDs
|
||||
def visit(node):
|
||||
if isinstance(node, Subscription):
|
||||
return node.id
|
||||
return None
|
||||
filter_kwargs['subscription_id__in'] = traverse_tree(folder_id, user, visit)
|
||||
|
||||
# Only watched
|
||||
if only_watched is not None:
|
||||
filter_kwargs['watched'] = only_watched
|
||||
|
||||
# Only downloaded
|
||||
# - not downloaded (False) -> is null (True)
|
||||
# - downloaded (True) -> is not null (False)
|
||||
if only_downloaded is not None:
|
||||
filter_kwargs['downloaded_path__isnull'] = not only_downloaded
|
||||
|
||||
return Video.objects.filter(*filter_args, **filter_kwargs).order_by(sort_order)
|
@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-10-03 09:12
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.1.2 on 2018-10-11 00:19
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@ -11,14 +10,38 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Channel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('channel_id', models.TextField(unique=True)),
|
||||
('username', models.TextField(null=True, unique=True)),
|
||||
('custom_url', models.TextField(null=True, unique=True)),
|
||||
('name', models.TextField()),
|
||||
('description', models.TextField()),
|
||||
('icon_default', models.TextField()),
|
||||
('icon_best', models.TextField()),
|
||||
('upload_playlist_id', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Subscription',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField()),
|
||||
('playlist_id', models.TextField(unique=True)),
|
||||
('description', models.TextField()),
|
||||
('icon_default', models.TextField()),
|
||||
('icon_best', models.TextField()),
|
||||
('auto_download', models.BooleanField(null=True)),
|
||||
('download_limit', models.IntegerField(null=True)),
|
||||
('download_order', models.TextField(null=True)),
|
||||
('manager_delete_after_watched', models.BooleanField(null=True)),
|
||||
('channel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.Channel')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -26,12 +49,54 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField()),
|
||||
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.SubscriptionFolder')),
|
||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.SubscriptionFolder')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('mark_deleted_as_watched', models.BooleanField(null=True)),
|
||||
('delete_watched', models.BooleanField(null=True)),
|
||||
('auto_download', models.BooleanField(null=True)),
|
||||
('download_global_limit', models.IntegerField(null=True)),
|
||||
('download_subscription_limit', models.IntegerField(null=True)),
|
||||
('download_order', models.TextField(null=True)),
|
||||
('download_path', models.TextField(null=True)),
|
||||
('download_file_pattern', models.TextField(null=True)),
|
||||
('download_format', models.TextField(null=True)),
|
||||
('download_subtitles', models.BooleanField(null=True)),
|
||||
('download_autogenerated_subtitles', models.BooleanField(null=True)),
|
||||
('download_subtitles_all', models.BooleanField(null=True)),
|
||||
('download_subtitles_langs', models.TextField(null=True)),
|
||||
('download_subtitles_format', models.TextField(null=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Video',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('video_id', models.TextField()),
|
||||
('name', models.TextField()),
|
||||
('description', models.TextField()),
|
||||
('watched', models.BooleanField(default=False)),
|
||||
('downloaded_path', models.TextField(blank=True, null=True)),
|
||||
('playlist_index', models.IntegerField()),
|
||||
('publish_date', models.DateTimeField()),
|
||||
('icon_default', models.TextField()),
|
||||
('icon_best', models.TextField()),
|
||||
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.Subscription')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='parent_folder',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.SubscriptionFolder'),
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='YtManagerApp.SubscriptionFolder'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
|
@ -1,47 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-10-03 09:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Video',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField()),
|
||||
('ytid', models.TextField()),
|
||||
('downloaded_path', models.TextField(null=True)),
|
||||
('watched', models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='url',
|
||||
field=models.TextField(default='', unique=True),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscription',
|
||||
name='parent_folder',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='YtManagerApp.SubscriptionFolder'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscriptionfolder',
|
||||
name='parent',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.SubscriptionFolder'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='subscription',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.Subscription'),
|
||||
),
|
||||
]
|
22
YtManagerApp/migrations/0002_subscriptionfolder_user.py
Normal file
22
YtManagerApp/migrations/0002_subscriptionfolder_user.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-11 18:16
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('YtManagerApp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscriptionfolder',
|
||||
name='user',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-10-03 18:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0002_auto_20181003_0923'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subscription',
|
||||
name='parent_folder',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='YtManagerApp.SubscriptionFolder'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscriptionfolder',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.SubscriptionFolder'),
|
||||
),
|
||||
]
|
29
YtManagerApp/migrations/0003_auto_20181013_2018.py
Normal file
29
YtManagerApp/migrations/0003_auto_20181013_2018.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-13 17:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0002_subscriptionfolder_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='rating',
|
||||
field=models.FloatField(default=0.5),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='uploader_name',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='views',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-05 13:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0003_auto_20181003_1825'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='video',
|
||||
name='downloaded_path',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
]
|
@ -1,57 +0,0 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-07 17:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0004_auto_20181005_1626'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Channel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('channel_id', models.TextField(unique=True)),
|
||||
('username', models.TextField(null=True, unique=True)),
|
||||
('custom_url', models.TextField(null=True, unique=True)),
|
||||
('name', models.TextField()),
|
||||
('description', models.TextField()),
|
||||
('icon_default', models.TextField()),
|
||||
('icon_best', models.TextField()),
|
||||
('upload_playlist_id', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='subscription',
|
||||
old_name='url',
|
||||
new_name='playlist_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='description',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='icon_best',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='icon_default',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='channel',
|
||||
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='YtManagerApp.Channel'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,49 +0,0 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-07 21:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0005_auto_20181007_2015'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='video',
|
||||
old_name='ytid',
|
||||
new_name='video_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='description',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='icon_best',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='icon_default',
|
||||
field=models.TextField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='playlist_index',
|
||||
field=models.IntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='video',
|
||||
name='publish_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,33 +0,0 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-08 23:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0006_auto_20181008_0037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='downloader_enabled',
|
||||
field=models.BooleanField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='downloader_limit',
|
||||
field=models.IntegerField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='downloader_order',
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='manager_delete_after_watched',
|
||||
field=models.BooleanField(null=True),
|
||||
),
|
||||
]
|
@ -1,6 +1,10 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
# help_text = user shown text
|
||||
# verbose_name = user shown name
|
||||
# null = nullable, blank = user is allowed to set value to empty
|
||||
|
||||
|
||||
class UserSettings(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
@ -21,7 +25,7 @@ class UserSettings(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def find_by_user(user: User):
|
||||
result = UserSettings.objects.filter(user=user)
|
||||
result = UserSettings.objects.filter2(user=user)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
@ -67,6 +71,7 @@ class UserSettings(models.Model):
|
||||
class SubscriptionFolder(models.Model):
|
||||
name = models.TextField(null=False)
|
||||
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -138,6 +143,9 @@ class Video(models.Model):
|
||||
publish_date = models.DateTimeField(null=False)
|
||||
icon_default = models.TextField()
|
||||
icon_best = models.TextField()
|
||||
uploader_name = models.TextField(null=False)
|
||||
views = models.IntegerField(null=False, default=0)
|
||||
rating = models.FloatField(null=False, default=0.5)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
@ -1,7 +1,7 @@
|
||||
import logging
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
from .appconfig import instance as app_config
|
||||
from .appconfig import settings
|
||||
|
||||
instance: BackgroundScheduler = None
|
||||
|
||||
@ -12,7 +12,7 @@ def initialize_scheduler():
|
||||
executors = {
|
||||
'default': {
|
||||
'type': 'threadpool',
|
||||
'max_workers': int(app_config.get('global', 'SchedulerConcurrency'))
|
||||
'max_workers': settings.getint('global', 'SchedulerConcurrency')
|
||||
}
|
||||
}
|
||||
|
||||
|
9
YtManagerApp/static/YtManagerApp/css/login.css
Normal file
9
YtManagerApp/static/YtManagerApp/css/login.css
Normal file
@ -0,0 +1,9 @@
|
||||
.login-card {
|
||||
width: 26rem;
|
||||
margin: 2rem 0; }
|
||||
|
||||
.register-card {
|
||||
max-width: 35rem;
|
||||
margin: 2rem 0; }
|
||||
|
||||
/*# sourceMappingURL=login.css.map */
|
7
YtManagerApp/static/YtManagerApp/css/login.css.map
Normal file
7
YtManagerApp/static/YtManagerApp/css/login.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA,WAAY;EACR,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,MAAM;;AAGlB,cAAe;EACX,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM",
|
||||
"sources": ["login.scss"],
|
||||
"names": [],
|
||||
"file": "login.css"
|
||||
}
|
9
YtManagerApp/static/YtManagerApp/css/login.scss
Normal file
9
YtManagerApp/static/YtManagerApp/css/login.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.login-card {
|
||||
max-width: 26rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.register-card {
|
||||
max-width: 35rem;
|
||||
margin: 2rem 0;
|
||||
}
|
@ -51,4 +51,11 @@
|
||||
.video-gallery .video-icon-no {
|
||||
color: #cccccc; }
|
||||
|
||||
.alert-card {
|
||||
max-width: 35rem;
|
||||
margin: 2rem 0; }
|
||||
|
||||
.no-asterisk .asteriskField {
|
||||
display: none; }
|
||||
|
||||
/*# sourceMappingURL=style.css.map */
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAEA,gCAAgC;AAChC,wBAAyB;EACrB,OAAO,EAAE,OAAO;;AAGpB,wBAAyB;EACrB,OAAO,EAAE,OAAO;;AAGpB,uBAAuB;AACvB,kBAAmB;EACf,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAGhB,wBAAyB;EACrB,OAAO,EAAE,GAAG;EACZ,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,GAAG;EACX,aAAa,EAAE,GAAG;EAClB,MAAM,EAAE,iBAAuB;EAC/B,YAAY,EAAE,uCAAmD;EACjE,SAAS,EAAE,sCAAsC;;AAGrD,4BAOC;EANG,EAAG;IACC,SAAS,EAAE,YAAY;EAE3B,IAAK;IACD,SAAS,EAAE,cAAc;AAK7B,4BAAc;EACV,OAAO,EAAE,MAAM;EACf,aAAa,EAAE,KAAK;AAGpB,+BAAW;EACP,OAAO,EAAE,MAAM;AAEnB,+BAAW;EACP,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;AAExB,gCAAY;EACR,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;EAEpB,uCAAO;IACH,SAAS,EAAE,GAAG;AAGtB,iCAAa;EACT,OAAO,EAAE,YAAY;AAGzB,+BAAW;EACP,YAAY,EAAE,QAAQ;EACtB,qCAAQ;IACJ,eAAe,EAAE,IAAI;AAKjC,8BAAgB;EACZ,KAAK,EAAE,OAAO;AAElB,6BAAe;EACX,KAAK,EAAE,OAAO",
|
||||
"mappings": "AAEA,gCAAgC;AAChC,wBAAyB;EACrB,OAAO,EAAE,OAAO;;AAGpB,wBAAyB;EACrB,OAAO,EAAE,OAAO;;AAGpB,uBAAuB;AACvB,kBAAmB;EACf,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAGhB,wBAAyB;EACrB,OAAO,EAAE,GAAG;EACZ,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,GAAG;EACX,aAAa,EAAE,GAAG;EAClB,MAAM,EAAE,iBAAuB;EAC/B,YAAY,EAAE,uCAAmD;EACjE,SAAS,EAAE,sCAAsC;;AAGrD,4BAOC;EANG,EAAG;IACC,SAAS,EAAE,YAAY;EAE3B,IAAK;IACD,SAAS,EAAE,cAAc;AAK7B,4BAAc;EACV,OAAO,EAAE,MAAM;EACf,aAAa,EAAE,KAAK;AAGpB,+BAAW;EACP,OAAO,EAAE,MAAM;AAEnB,+BAAW;EACP,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;AAExB,gCAAY;EACR,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;EAEpB,uCAAO;IACH,SAAS,EAAE,GAAG;AAGtB,iCAAa;EACT,OAAO,EAAE,YAAY;AAGzB,+BAAW;EACP,YAAY,EAAE,QAAQ;EACtB,qCAAQ;IACJ,eAAe,EAAE,IAAI;AAKjC,8BAAgB;EACZ,KAAK,EAAE,OAAO;AAElB,6BAAe;EACX,KAAK,EAAE,OAAO;;AAItB,WAAY;EACR,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM;;AAId,2BAAe;EACX,OAAO,EAAE,IAAI",
|
||||
"sources": ["style.scss"],
|
||||
"names": [],
|
||||
"file": "style.css"
|
||||
|
@ -76,5 +76,15 @@ $accent-color: #007bff;
|
||||
.video-icon-no {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-card {
|
||||
max-width: 35rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.no-asterisk {
|
||||
.asteriskField {
|
||||
display: none;
|
||||
}
|
||||
}
|
20
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.babelrc.js
vendored
Normal file
20
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.babelrc.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
loose: true,
|
||||
modules: false,
|
||||
exclude: ['transform-typeof-symbol']
|
||||
}
|
||||
]
|
||||
],
|
||||
plugins: [
|
||||
'@babel/proposal-object-rest-spread'
|
||||
],
|
||||
env: {
|
||||
test: {
|
||||
plugins: [ 'istanbul' ]
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
# https://github.com/browserslist/browserslist#readme
|
||||
|
||||
>= 1%
|
||||
last 1 major version
|
||||
not dead
|
||||
Chrome >= 45
|
||||
Firefox >= 38
|
||||
Edge >= 12
|
||||
Explorer >= 10
|
||||
iOS >= 9
|
||||
Safari >= 9
|
||||
Android >= 4.4
|
||||
Opera >= 30
|
@ -0,0 +1,14 @@
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
@ -0,0 +1,5 @@
|
||||
**/*.min.js
|
||||
**/dist/
|
||||
**/vendor/
|
||||
/_gh_pages/
|
||||
/package.js
|
@ -0,0 +1,233 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:compat/recommended"],
|
||||
"rules": {
|
||||
// Possible Errors
|
||||
"no-await-in-loop": "error",
|
||||
"no-extra-parens": "error",
|
||||
"no-prototype-builtins": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"valid-jsdoc": "error",
|
||||
|
||||
// Best Practices
|
||||
"accessor-pairs": "error",
|
||||
"array-callback-return": "error",
|
||||
"block-scoped-var": "error",
|
||||
"class-methods-use-this": "off",
|
||||
"complexity": "error",
|
||||
"consistent-return": "error",
|
||||
"curly": "error",
|
||||
"default-case": "error",
|
||||
"dot-location": ["error", "property"],
|
||||
"dot-notation": "error",
|
||||
"eqeqeq": "error",
|
||||
"guard-for-in": "error",
|
||||
"no-alert": "error",
|
||||
"no-caller": "error",
|
||||
"no-div-regex": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty-function": "error",
|
||||
"no-eq-null": "error",
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-implicit-globals": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-invalid-this": "off",
|
||||
"no-iterator": "error",
|
||||
"no-labels": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-magic-numbers": ["error", {
|
||||
"ignore": [-1, 0, 1],
|
||||
"ignoreArrayIndexes": true
|
||||
}
|
||||
],
|
||||
"no-multi-spaces": ["error", {
|
||||
"ignoreEOLComments": true,
|
||||
"exceptions": {
|
||||
"AssignmentExpression": true,
|
||||
"ArrowFunctionExpression": true,
|
||||
"CallExpression": true,
|
||||
"VariableDeclarator": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"no-multi-str": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": "off",
|
||||
"no-proto": "error",
|
||||
"no-restricted-properties": "error",
|
||||
"no-return-assign": "error",
|
||||
"no-return-await": "error",
|
||||
"no-script-url": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-return": "error",
|
||||
"no-void": "error",
|
||||
"no-warning-comments": "off",
|
||||
"no-with": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"radix": "error",
|
||||
"require-await": "error",
|
||||
"vars-on-top": "error",
|
||||
"wrap-iife": "error",
|
||||
"yoda": "error",
|
||||
|
||||
// Strict Mode
|
||||
"strict": "error",
|
||||
|
||||
// Variables
|
||||
"init-declarations": "off",
|
||||
"no-catch-shadow": "error",
|
||||
"no-label-var": "error",
|
||||
"no-restricted-globals": "error",
|
||||
"no-shadow": "off",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undefined": "error",
|
||||
"no-use-before-define": "off",
|
||||
|
||||
// Node.js and CommonJS
|
||||
"callback-return": "off",
|
||||
"global-require": "error",
|
||||
"handle-callback-err": "error",
|
||||
"no-mixed-requires": "error",
|
||||
"no-new-require": "error",
|
||||
"no-path-concat": "error",
|
||||
"no-process-env": "error",
|
||||
"no-process-exit": "error",
|
||||
"no-restricted-modules": "error",
|
||||
"no-sync": "error",
|
||||
|
||||
// Stylistic Issues
|
||||
"array-bracket-spacing": "error",
|
||||
"block-spacing": "error",
|
||||
"brace-style": "error",
|
||||
"camelcase": "error",
|
||||
"capitalized-comments": "off",
|
||||
"comma-dangle": "error",
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"computed-property-spacing": "error",
|
||||
"consistent-this": "error",
|
||||
"eol-last": "error",
|
||||
"func-call-spacing": "error",
|
||||
"func-name-matching": "error",
|
||||
"func-names": "off",
|
||||
"func-style": ["error", "declaration"],
|
||||
"id-blacklist": "error",
|
||||
"id-length": "off",
|
||||
"id-match": "error",
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"jsx-quotes": "error",
|
||||
"key-spacing": "off",
|
||||
"keyword-spacing": "error",
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"line-comment-position": "off",
|
||||
"lines-around-comment": "off",
|
||||
"lines-around-directive": "error",
|
||||
"max-depth": ["error", 10],
|
||||
"max-len": "off",
|
||||
"max-lines": "off",
|
||||
"max-nested-callbacks": "error",
|
||||
"max-params": "off",
|
||||
"max-statements": "off",
|
||||
"max-statements-per-line": "error",
|
||||
"multiline-ternary": "off",
|
||||
"new-cap": ["error", { "capIsNewExceptionPattern": "$.*" }],
|
||||
"newline-after-var": "off",
|
||||
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 5 }],
|
||||
"new-parens": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-bitwise": "error",
|
||||
"no-continue": "off",
|
||||
"no-inline-comments": "off",
|
||||
"no-lonely-if": "error",
|
||||
"no-mixed-operators": "off",
|
||||
"no-multi-assign": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"nonblock-statement-body-position": "error",
|
||||
"no-negated-condition": "off",
|
||||
"no-nested-ternary": "error",
|
||||
"no-new-object": "error",
|
||||
"no-plusplus": "off",
|
||||
"no-restricted-syntax": "error",
|
||||
"no-tabs": "error",
|
||||
"no-ternary": "off",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-whitespace-before-property": "error",
|
||||
"object-curly-newline": ["error", { "minProperties": 1 }],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"object-property-newline": "error",
|
||||
"one-var": ["error", "never"],
|
||||
"one-var-declaration-per-line": "error",
|
||||
"operator-assignment": "error",
|
||||
"operator-linebreak": "error",
|
||||
"padded-blocks": ["error", "never"],
|
||||
"padding-line-between-statements": "off",
|
||||
"quote-props": ["error", "as-needed"],
|
||||
"quotes": ["error", "single"],
|
||||
"require-jsdoc": "off",
|
||||
"semi": ["error", "never"],
|
||||
"semi-spacing": "error",
|
||||
"sort-keys": "off",
|
||||
"sort-vars": "error",
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "always",
|
||||
"named": "never"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"template-tag-spacing": "error",
|
||||
"unicode-bom": "error",
|
||||
"wrap-regex": "off",
|
||||
|
||||
// ECMAScript 6
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"arrow-parens": "error",
|
||||
"arrow-spacing": "error",
|
||||
"generator-star-spacing": "error",
|
||||
"no-confusing-arrow": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-restricted-imports": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-rename": "error",
|
||||
"no-var": "error",
|
||||
"object-shorthand": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-numeric-literals": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"rest-spread-spacing": "error",
|
||||
"sort-imports": "error",
|
||||
"symbol-description": "error",
|
||||
"template-curly-spacing": "error",
|
||||
"yield-star-spacing": "error"
|
||||
}
|
||||
}
|
18
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitattributes
vendored
Normal file
18
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitattributes
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Enforce Unix newlines
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.md text eol=lf
|
||||
*.rb text eol=lf
|
||||
*.scss text eol=lf
|
||||
*.svg text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.yml text eol=lf
|
||||
|
||||
# Don't diff or textually merge source maps
|
||||
*.map binary
|
||||
|
||||
bootstrap.css linguist-vendored=false
|
||||
bootstrap.js linguist-vendored=false
|
243
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/CONTRIBUTING.md
vendored
Normal file
243
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
# Contributing to Bootstrap
|
||||
|
||||
Looking to contribute something to Bootstrap? **Here's how you can help.**
|
||||
|
||||
Please take a moment to review this document in order to make the contribution
|
||||
process easy and effective for everyone involved.
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue or assessing
|
||||
patches and features.
|
||||
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The [issue tracker](https://github.com/twbs/bootstrap/issues) is
|
||||
the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests)
|
||||
and [submitting pull requests](#pull-requests), but please respect the following
|
||||
restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests. Stack
|
||||
Overflow ([`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4) tag), [Slack](https://bootstrap-slack.herokuapp.com/) or [IRC](README.md#community) are better places to get help.
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
* Please **do not** post comments consisting solely of "+1" or ":thumbsup:".
|
||||
Use [GitHub's "reactions" feature](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/)
|
||||
instead. We reserve the right to delete comments which violate this rule.
|
||||
|
||||
* Please **do not** open issues regarding the official themes offered on <https://themes.getbootstrap.com/>.
|
||||
Instead, please email any questions or feedback regarding those themes to `themes AT getbootstrap DOT com`.
|
||||
|
||||
|
||||
## Issues and labels
|
||||
|
||||
Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them:
|
||||
|
||||
- `browser bug` - Issues that are reported to us, but actually are the result of a browser-specific bug. These are diagnosed with reduced test cases and result in an issue opened on that browser's own bug tracker.
|
||||
- `confirmed` - Issues that have been confirmed with a reduced test case and identify a bug in Bootstrap.
|
||||
- `css` - Issues stemming from our compiled CSS or source Sass files.
|
||||
- `docs` - Issues for improving or updating our documentation.
|
||||
- `examples` - Issues involving the example templates included in our docs.
|
||||
- `feature` - Issues asking for a new feature to be added, or an existing one to be extended or modified. New features require a minor version bump (e.g., `v3.0.0` to `v3.1.0`).
|
||||
- `build` - Issues with our build system, which is used to run all our tests, concatenate and compile source files, and more.
|
||||
- `help wanted` - Issues we need or would love help from the community to resolve.
|
||||
- `js` - Issues stemming from our compiled or source JavaScript files.
|
||||
- `meta` - Issues with the project itself or our GitHub repository.
|
||||
|
||||
For a complete look at our labels, see the [project labels page](https://github.com/twbs/bootstrap/labels).
|
||||
|
||||
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful, so thanks!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
0. **Validate and lint your code** — [validate your HTML](https://html5.validator.nu/)
|
||||
and [lint your HTML](https://github.com/twbs/bootlint) to ensure your
|
||||
problem isn't caused by a simple error in your own code.
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
|
||||
3. **Isolate the problem** — ideally create a [reduced test
|
||||
case](https://css-tricks.com/reduced-test-cases/) and a live example.
|
||||
[This JS Bin](https://jsbin.com/lolome/edit?html,output) is a helpful template.
|
||||
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What browser(s) and OS
|
||||
experience the problem? Do other browsers show the bug differently? What
|
||||
would you expect to be the outcome? All these details will help people to fix
|
||||
any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
### Reporting upstream browser bugs
|
||||
|
||||
Sometimes bugs reported to us are actually caused by bugs in the browser(s) themselves, not bugs in Bootstrap per se.
|
||||
When feasible, we aim to report such upstream bugs to the relevant browser vendor(s), and then list them on our [Wall of Browser Bugs](https://getbootstrap.com/browser-bugs/) and [document them in MDN](https://developer.mozilla.org/en-US/docs/Web).
|
||||
|
||||
| Vendor(s) | Browser(s) | Rendering engine | Bug reporting website(s) | Notes |
|
||||
| ------------- | ---------------------------- | ---------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| Mozilla | Firefox | Gecko | https://bugzilla.mozilla.org/enter_bug.cgi | "Core" is normally the right product option to choose. |
|
||||
| Apple | Safari | WebKit | https://bugs.webkit.org/enter_bug.cgi?product=WebKit <br> https://bugreport.apple.com/ | In Apple's bug reporter, choose "Safari" as the product. |
|
||||
| Google, Opera | Chrome, Chromium, Opera v15+ | Blink | https://bugs.chromium.org/p/chromium/issues/list | Click the "New issue" button. |
|
||||
| Microsoft | Edge | EdgeHTML | https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/ | |
|
||||
|
||||
### Issues bots
|
||||
|
||||
[@twbs-lmvtfy](https://github.com/twbs-lmvtfy) is a Bootstrap bot that hangs out in our GitHub issue tracker and automatically checks for HTML validation errors in live examples (e.g. jsFiddles, JS Bins, Bootplys, Plunks, CodePens, etc.) posted in issue comments. If it finds any errors, it will post a follow-up comment on the issue and point out the errors. If this happens with an example you've posted, please fix the errors and post an updated live example. If you opened a bug report, please check whether the bug still occurs with your revised, valid live example. If the bug no longer occurs, it was probably due to your invalid HTML rather than something in Bootstrap and we'd appreciate it if you could close out the GitHub issue.
|
||||
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
## Pull requests
|
||||
|
||||
Good pull requests—patches, improvements, new features—are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code, porting to a different language),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
Please adhere to the [coding guidelines](#code-guidelines) used throughout the
|
||||
project (indentation, accurate comments, etc.) and any other requirements
|
||||
(such as test coverage).
|
||||
|
||||
**Do not edit `bootstrap.css`, or `bootstrap.js`
|
||||
directly!** Those files are automatically generated. You should edit the
|
||||
source files in [`/bootstrap/scss/`](https://github.com/twbs/bootstrap/tree/master/scss)
|
||||
and/or [`/bootstrap/js/`](https://github.com/twbs/bootstrap/tree/master/js) instead.
|
||||
|
||||
Similarly, when contributing to Bootstrap's documentation, you should edit the
|
||||
documentation source files in
|
||||
[the `/bootstrap/docs/` directory of the `master` branch](https://github.com/twbs/bootstrap/tree/master/docs).
|
||||
**Do not edit the `gh-pages` branch.** That branch is generated from the
|
||||
documentation source files and is managed separately by the Bootstrap Core Team.
|
||||
|
||||
Adhering to the following process is the best way to get your work
|
||||
included in the project:
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork,
|
||||
and configure the remotes:
|
||||
|
||||
```bash
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/bootstrap.git
|
||||
# Navigate to the newly cloned directory
|
||||
cd bootstrap
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/twbs/bootstrap.git
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
```
|
||||
|
||||
3. Create a new topic branch (off the main project development branch) to
|
||||
contain your feature, change, or fix:
|
||||
|
||||
```bash
|
||||
git checkout -b <topic-branch-name>
|
||||
```
|
||||
|
||||
4. Commit your changes in logical chunks. Please adhere to these [git commit
|
||||
message guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
or your code is unlikely be merged into the main project. Use Git's
|
||||
[interactive rebase](https://help.github.com/articles/about-git-rebase/)
|
||||
feature to tidy up your commits before making them public.
|
||||
|
||||
5. Locally merge (or rebase) the upstream development branch into your topic branch:
|
||||
|
||||
```bash
|
||||
git pull [--rebase] upstream master
|
||||
```
|
||||
|
||||
6. Push your topic branch up to your fork:
|
||||
|
||||
```bash
|
||||
git push origin <topic-branch-name>
|
||||
```
|
||||
|
||||
7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/)
|
||||
with a clear title and description against the `master` branch.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to allow the project owners to
|
||||
license your work under the terms of the [MIT License](LICENSE) (if it
|
||||
includes code changes) and under the terms of the
|
||||
[Creative Commons Attribution 3.0 Unported License](docs/LICENSE)
|
||||
(if it includes documentation changes).
|
||||
|
||||
|
||||
## Code guidelines
|
||||
|
||||
### HTML
|
||||
|
||||
[Adhere to the Code Guide.](http://codeguide.co/#html)
|
||||
|
||||
- Use tags and elements appropriate for an HTML5 doctype (e.g., self-closing tags).
|
||||
- Use CDNs and HTTPS for third-party JS when possible. We don't use protocol-relative URLs in this case because they break when viewing the page locally via `file://`.
|
||||
- Use [WAI-ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) attributes in documentation examples to promote accessibility.
|
||||
|
||||
### CSS
|
||||
|
||||
[Adhere to the Code Guide.](http://codeguide.co/#css)
|
||||
|
||||
- When feasible, default color palettes should comply with [WCAG color contrast guidelines](https://www.w3.org/TR/WCAG20/#visual-audio-contrast).
|
||||
- Except in rare cases, don't remove default `:focus` styles (via e.g. `outline: none;`) without providing alternative styles. See [this A11Y Project post](https://a11yproject.com/posts/never-remove-css-outlines/) for more details.
|
||||
|
||||
### JS
|
||||
|
||||
- No semicolons (in client-side JS)
|
||||
- 2 spaces (no tabs)
|
||||
- strict mode
|
||||
- "Attractive"
|
||||
- Don't use [jQuery event alias convenience methods](https://github.com/jquery/jquery/blob/master/src/event/alias.js) (such as `$().focus()`). Instead, use [`$().trigger(eventType, ...)`](https://api.jquery.com/trigger/) or [`$().on(eventType, ...)`](https://api.jquery.com/on/), depending on whether you're firing an event or listening for an event. (For example, `$().trigger('focus')` or `$().on('focus', function (event) { /* handle focus event */ })`) We do this to be compatible with custom builds of jQuery where the event aliases module has been excluded.
|
||||
|
||||
### Checking coding style
|
||||
|
||||
Run `npm run test` before committing to ensure your changes follow our coding standards.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
By contributing your code, you agree to license your contribution under the [MIT License](LICENSE).
|
||||
By contributing to the documentation, you agree to license your contribution under the [Creative Commons Attribution 3.0 Unported License](docs/LICENSE).
|
||||
|
||||
Prior to v3.1.0, Bootstrap's code was released under the Apache License v2.0.
|
11
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
11
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
Before opening:
|
||||
|
||||
- [Search for duplicate or closed issues](https://github.com/twbs/bootstrap/issues?utf8=%E2%9C%93&q=is%3Aissue)
|
||||
- [Validate](https://html5.validator.nu/) and [lint](https://github.com/twbs/bootlint#in-the-browser) any HTML to avoid common problems
|
||||
- Read the [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
|
||||
Bug reports must include:
|
||||
|
||||
- Operating system and version (Windows, macOS, Android, iOS, Win10 Mobile)
|
||||
- Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera 15+, Android Browser)
|
||||
- [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/)
|
17
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Tell us about a bug you may have identified in Bootstrap.
|
||||
|
||||
---
|
||||
|
||||
Before opening:
|
||||
|
||||
- [Search for duplicate or closed issues](https://github.com/twbs/bootstrap/issues?utf8=%E2%9C%93&q=is%3Aissue)
|
||||
- [Validate](https://html5.validator.nu/) and [lint](https://github.com/twbs/bootlint#in-the-browser) any HTML to avoid common problems
|
||||
- Read the [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
|
||||
Bug reports must include:
|
||||
|
||||
- Operating system and version (Windows, macOS, Android, iOS, Win10 Mobile)
|
||||
- Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera 15+, Android Browser)
|
||||
- [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/)
|
9
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
9
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Before opening:
|
||||
|
||||
- [Search for duplicate or closed issues](https://github.com/twbs/bootstrap/issues?utf8=%E2%9C%93&q=is%3Aissue)
|
||||
- Read the [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
|
||||
Feature requests must include:
|
||||
|
||||
- As much detail as possible for what we should add and why it's important to Bootstrap
|
||||
- Relevant links to prior art, screenshots, or live demos whenever possible
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for a new feature in Bootstrap.
|
||||
|
||||
---
|
||||
|
||||
Before opening:
|
||||
|
||||
- [Search for duplicate or closed issues](https://github.com/twbs/bootstrap/issues?utf8=%E2%9C%93&q=is%3Aissue)
|
||||
- Read the [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
|
||||
Feature requests must include:
|
||||
|
||||
- As much detail as possible for what we should add and why it's important to Bootstrap
|
||||
- Relevant links to prior art, screenshots, or live demos whenever possible
|
11
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/SUPPORT.md
vendored
Normal file
11
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.github/SUPPORT.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
### Bug reports
|
||||
|
||||
See the [contributing guidelines](CONTRIBUTING.md) for sharing bug reports.
|
||||
|
||||
### How-to
|
||||
|
||||
For general troubleshooting or help getting started:
|
||||
|
||||
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
|
||||
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
|
||||
- Ask and explore Stack Overflow with the [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4) tag.
|
48
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitignore
vendored
Normal file
48
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitignore
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
# Ignore docs files
|
||||
_gh_pages
|
||||
_site
|
||||
site/docs/4.1/dist/
|
||||
|
||||
# Ignore ruby files
|
||||
.ruby-version
|
||||
.bundle
|
||||
vendor/cache
|
||||
vendor/bundle
|
||||
|
||||
# Numerous always-ignore extensions
|
||||
*.diff
|
||||
*.err
|
||||
*.log
|
||||
*.orig
|
||||
*.rej
|
||||
*.swo
|
||||
*.swp
|
||||
*.vi
|
||||
*.zip
|
||||
*~
|
||||
|
||||
# OS or Editor folders
|
||||
._*
|
||||
.cache
|
||||
.DS_Store
|
||||
.idea
|
||||
.project
|
||||
.settings
|
||||
.tmproj
|
||||
*.esproj
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
nbproject
|
||||
Thumbs.db
|
||||
|
||||
# Komodo
|
||||
.komodotools
|
||||
*.komodoproject
|
||||
|
||||
# Jekyll metadata and extra config file for `github` script
|
||||
docs/.jekyll-metadata
|
||||
twbsconfig.yml
|
||||
|
||||
# Folders to ignore
|
||||
node_modules
|
||||
js/coverage
|
@ -0,0 +1,4 @@
|
||||
**/*.min.css
|
||||
**/dist/
|
||||
**/vendor/
|
||||
/_gh_pages/
|
@ -0,0 +1,274 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"],
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-empty-line-before": null,
|
||||
"at-rule-name-space-after": "always",
|
||||
"at-rule-no-vendor-prefix": true,
|
||||
"at-rule-semicolon-space-before": "never",
|
||||
"block-closing-brace-empty-line-before": null,
|
||||
"block-closing-brace-newline-after": null,
|
||||
"block-opening-brace-space-before": null,
|
||||
"color-named": "never",
|
||||
"declaration-block-semicolon-newline-after": "always-multi-line",
|
||||
"declaration-block-semicolon-newline-before": "never-multi-line",
|
||||
"declaration-block-semicolon-space-after": "always-single-line",
|
||||
"declaration-empty-line-before": null,
|
||||
"declaration-no-important": true,
|
||||
"font-family-name-quotes": "always-where-recommended",
|
||||
"font-weight-notation": "numeric",
|
||||
"function-url-no-scheme-relative": true,
|
||||
"function-url-quotes": "always",
|
||||
"length-zero-no-unit": true,
|
||||
"max-empty-lines": 2,
|
||||
"max-line-length": null,
|
||||
"media-feature-name-no-vendor-prefix": true,
|
||||
"media-feature-parentheses-space-inside": "never",
|
||||
"media-feature-range-operator-space-after": "always",
|
||||
"media-feature-range-operator-space-before": "never",
|
||||
"no-descending-specificity": null,
|
||||
"no-duplicate-selectors": true,
|
||||
"number-leading-zero": "never",
|
||||
"media-feature-name-no-unknown": [true, {
|
||||
"ignoreMediaFeatureNames": ["prefers-reduced-motion"]
|
||||
}],
|
||||
"order/properties-order": [
|
||||
"position",
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left",
|
||||
"z-index",
|
||||
"box-sizing",
|
||||
"display",
|
||||
"flex",
|
||||
"flex-align",
|
||||
"flex-basis",
|
||||
"flex-direction",
|
||||
"flex-wrap",
|
||||
"flex-flow",
|
||||
"flex-shrink",
|
||||
"flex-grow",
|
||||
"flex-order",
|
||||
"flex-pack",
|
||||
"align-content",
|
||||
"align-items",
|
||||
"align-self",
|
||||
"justify-content",
|
||||
"order",
|
||||
"float",
|
||||
"width",
|
||||
"min-width",
|
||||
"max-width",
|
||||
"height",
|
||||
"min-height",
|
||||
"max-height",
|
||||
"padding",
|
||||
"padding-top",
|
||||
"padding-right",
|
||||
"padding-bottom",
|
||||
"padding-left",
|
||||
"margin",
|
||||
"margin-top",
|
||||
"margin-right",
|
||||
"margin-bottom",
|
||||
"margin-left",
|
||||
"overflow",
|
||||
"overflow-x",
|
||||
"overflow-y",
|
||||
"-webkit-overflow-scrolling",
|
||||
"-ms-overflow-x",
|
||||
"-ms-overflow-y",
|
||||
"-ms-overflow-style",
|
||||
"columns",
|
||||
"column-count",
|
||||
"column-fill",
|
||||
"column-gap",
|
||||
"column-rule",
|
||||
"column-rule-width",
|
||||
"column-rule-style",
|
||||
"column-rule-color",
|
||||
"column-span",
|
||||
"column-width",
|
||||
"orphans",
|
||||
"widows",
|
||||
"clip",
|
||||
"clear",
|
||||
"font",
|
||||
"font-family",
|
||||
"font-size",
|
||||
"font-style",
|
||||
"font-weight",
|
||||
"font-variant",
|
||||
"font-size-adjust",
|
||||
"font-stretch",
|
||||
"font-effect",
|
||||
"font-emphasize",
|
||||
"font-emphasize-position",
|
||||
"font-emphasize-style",
|
||||
"font-smooth",
|
||||
"src",
|
||||
"hyphens",
|
||||
"line-height",
|
||||
"color",
|
||||
"text-align",
|
||||
"text-align-last",
|
||||
"text-emphasis",
|
||||
"text-emphasis-color",
|
||||
"text-emphasis-style",
|
||||
"text-emphasis-position",
|
||||
"text-decoration",
|
||||
"text-indent",
|
||||
"text-justify",
|
||||
"text-outline",
|
||||
"-ms-text-overflow",
|
||||
"text-overflow",
|
||||
"text-overflow-ellipsis",
|
||||
"text-overflow-mode",
|
||||
"text-shadow",
|
||||
"text-transform",
|
||||
"text-wrap",
|
||||
"-webkit-text-size-adjust",
|
||||
"-ms-text-size-adjust",
|
||||
"letter-spacing",
|
||||
"-ms-word-break",
|
||||
"word-break",
|
||||
"word-spacing",
|
||||
"-ms-word-wrap",
|
||||
"word-wrap",
|
||||
"overflow-wrap",
|
||||
"tab-size",
|
||||
"white-space",
|
||||
"vertical-align",
|
||||
"direction",
|
||||
"unicode-bidi",
|
||||
"list-style",
|
||||
"list-style-position",
|
||||
"list-style-type",
|
||||
"list-style-image",
|
||||
"pointer-events",
|
||||
"-ms-touch-action",
|
||||
"touch-action",
|
||||
"cursor",
|
||||
"visibility",
|
||||
"zoom",
|
||||
"table-layout",
|
||||
"empty-cells",
|
||||
"caption-side",
|
||||
"border-spacing",
|
||||
"border-collapse",
|
||||
"content",
|
||||
"quotes",
|
||||
"counter-reset",
|
||||
"counter-increment",
|
||||
"resize",
|
||||
"user-select",
|
||||
"nav-index",
|
||||
"nav-up",
|
||||
"nav-right",
|
||||
"nav-down",
|
||||
"nav-left",
|
||||
"background",
|
||||
"background-color",
|
||||
"background-image",
|
||||
"filter",
|
||||
"background-repeat",
|
||||
"background-attachment",
|
||||
"background-position",
|
||||
"background-position-x",
|
||||
"background-position-y",
|
||||
"background-clip",
|
||||
"background-origin",
|
||||
"background-size",
|
||||
"border",
|
||||
"border-color",
|
||||
"border-style",
|
||||
"border-width",
|
||||
"border-top",
|
||||
"border-top-color",
|
||||
"border-top-style",
|
||||
"border-top-width",
|
||||
"border-right",
|
||||
"border-right-color",
|
||||
"border-right-style",
|
||||
"border-right-width",
|
||||
"border-bottom",
|
||||
"border-bottom-color",
|
||||
"border-bottom-style",
|
||||
"border-bottom-width",
|
||||
"border-left",
|
||||
"border-left-color",
|
||||
"border-left-style",
|
||||
"border-left-width",
|
||||
"border-radius",
|
||||
"border-top-left-radius",
|
||||
"border-top-right-radius",
|
||||
"border-bottom-right-radius",
|
||||
"border-bottom-left-radius",
|
||||
"border-image",
|
||||
"border-image-source",
|
||||
"border-image-slice",
|
||||
"border-image-width",
|
||||
"border-image-outset",
|
||||
"border-image-repeat",
|
||||
"outline",
|
||||
"outline-width",
|
||||
"outline-style",
|
||||
"outline-color",
|
||||
"outline-offset",
|
||||
"box-shadow",
|
||||
"opacity",
|
||||
"-ms-interpolation-mode",
|
||||
"page-break-after",
|
||||
"page-break-before",
|
||||
"page-break-inside",
|
||||
"transition",
|
||||
"transition-delay",
|
||||
"transition-timing-function",
|
||||
"transition-duration",
|
||||
"transition-property",
|
||||
"transform",
|
||||
"transform-origin",
|
||||
"perspective",
|
||||
"appearance",
|
||||
"animation",
|
||||
"animation-name",
|
||||
"animation-duration",
|
||||
"animation-play-state",
|
||||
"animation-timing-function",
|
||||
"animation-delay",
|
||||
"animation-iteration-count",
|
||||
"animation-direction",
|
||||
"animation-fill-mode",
|
||||
"fill",
|
||||
"stroke"
|
||||
],
|
||||
"property-no-vendor-prefix": true,
|
||||
"rule-empty-line-before": null,
|
||||
"scss/dollar-variable-default": [true, { "ignore": "local" }],
|
||||
"selector-attribute-quotes": "always",
|
||||
"selector-list-comma-newline-after": "always",
|
||||
"selector-list-comma-newline-before": "never-multi-line",
|
||||
"selector-list-comma-space-after": "always-single-line",
|
||||
"selector-list-comma-space-before": "never-single-line",
|
||||
"selector-max-attribute": 2,
|
||||
"selector-max-class": 4,
|
||||
"selector-max-combinators": 4,
|
||||
"selector-max-compound-selectors": 4,
|
||||
"selector-max-empty-lines": 1,
|
||||
"selector-max-id": 0,
|
||||
"selector-max-specificity": null,
|
||||
"selector-max-type": 2,
|
||||
"selector-max-universal": 1,
|
||||
"selector-no-qualifying-type": true,
|
||||
"selector-no-vendor-prefix": true,
|
||||
"string-quotes": "double",
|
||||
"value-keyword-case": "lower",
|
||||
"value-list-comma-newline-after": "never-multi-line",
|
||||
"value-list-comma-newline-before": "never-multi-line",
|
||||
"value-list-comma-space-after": "always",
|
||||
"value-no-vendor-prefix": true
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
chrome: stable
|
||||
language: node_js
|
||||
git:
|
||||
depth: 3
|
||||
node_js:
|
||||
- "6"
|
||||
- "8"
|
||||
before_install:
|
||||
- if [[ `npm -v` != 5* ]]; then npm install -g npm@5; fi
|
||||
install:
|
||||
- bundle install --deployment --jobs=3 --retry=3
|
||||
- npm install
|
||||
script:
|
||||
- npm test
|
||||
- if [ "$TRAVIS_NODE_VERSION" = "8" ]; then npm run check-broken-links; fi
|
||||
after_success:
|
||||
- if [ "$TRAVIS_NODE_VERSION" = "8" ]; then npm run coveralls; fi
|
||||
stages:
|
||||
- test
|
||||
- name: browser
|
||||
if: type = push
|
||||
jobs:
|
||||
include:
|
||||
- stage: browser
|
||||
node_js: 8
|
||||
script: if ! git log --format=%B --no-merges -n 1 | grep '\[skip browser\]'; then npm test && npm run js-test-cloud; fi
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- vendor/bundle
|
@ -0,0 +1 @@
|
||||
getbootstrap.com
|
@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mdo@getbootstrap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html][version]
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org/
|
||||
[version]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
@ -0,0 +1,8 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
group :development, :test do
|
||||
gem 'jekyll', '~> 3.8.3'
|
||||
gem 'jekyll-redirect-from', '~> 0.14.0'
|
||||
gem 'jekyll-sitemap', '~> 1.2.0'
|
||||
gem 'jekyll-toc', '~> 0.6.0'
|
||||
end
|
@ -0,0 +1,80 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.0.5)
|
||||
em-websocket (0.5.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
eventmachine (1.2.7)
|
||||
eventmachine (1.2.7-x64-mingw32)
|
||||
ffi (1.9.25)
|
||||
ffi (1.9.25-x64-mingw32)
|
||||
forwardable-extended (2.6.0)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.8.3)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 0.7)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
kramdown (~> 1.14)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 1.7, < 4)
|
||||
safe_yaml (~> 1.0)
|
||||
jekyll-redirect-from (0.14.0)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-sitemap (1.2.0)
|
||||
jekyll (~> 3.3)
|
||||
jekyll-toc (0.6.0)
|
||||
nokogiri (~> 1.7)
|
||||
jekyll-watch (2.0.0)
|
||||
listen (~> 3.0)
|
||||
kramdown (1.17.0)
|
||||
liquid (4.0.0)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.3.0)
|
||||
nokogiri (1.8.4)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogiri (1.8.4-x64-mingw32)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
pathutil (0.16.1)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (3.0.2)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rouge (3.1.1)
|
||||
ruby_dep (1.5.0)
|
||||
safe_yaml (1.0.4)
|
||||
sass (3.5.6)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x64-mingw32
|
||||
|
||||
DEPENDENCIES
|
||||
jekyll (~> 3.8.3)
|
||||
jekyll-redirect-from (~> 0.14.0)
|
||||
jekyll-sitemap (~> 1.2.0)
|
||||
jekyll-toc (~> 0.6.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2018 Twitter, Inc.
|
||||
Copyright (c) 2011-2018 The Bootstrap Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,177 @@
|
||||
<p align="center">
|
||||
<a href="https://getbootstrap.com/">
|
||||
<img src="https://getbootstrap.com/docs/4.1/assets/brand/bootstrap-solid.svg" alt="Bootstrap logo" width=72 height=72>
|
||||
</a>
|
||||
|
||||
<h3 align="center">Bootstrap</h3>
|
||||
|
||||
<p align="center">
|
||||
Sleek, intuitive, and powerful front-end framework for faster and easier web development.
|
||||
<br>
|
||||
<a href="https://getbootstrap.com/docs/4.1/"><strong>Explore Bootstrap docs »</strong></a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://github.com/twbs/bootstrap/issues/new?template=bug.md">Report bug</a>
|
||||
·
|
||||
<a href="https://github.com/twbs/bootstrap/issues/new?template=feature.md&labels=feature">Request feature</a>
|
||||
·
|
||||
<a href="https://themes.getbootstrap.com/">Themes</a>
|
||||
·
|
||||
<a href="https://jobs.getbootstrap.com/">Jobs</a>
|
||||
·
|
||||
<a href="https://blog.getbootstrap.com/">Blog</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Quick start](#quick-start)
|
||||
- [Status](#status)
|
||||
- [What's included](#whats-included)
|
||||
- [Bugs and feature requests](#bugs-and-feature-requests)
|
||||
- [Documentation](#documentation)
|
||||
- [Contributing](#contributing)
|
||||
- [Community](#community)
|
||||
- [Versioning](#versioning)
|
||||
- [Creators](#creators)
|
||||
- [Copyright and license](#copyright-and-license)
|
||||
|
||||
## Quick start
|
||||
|
||||
Several quick start options are available:
|
||||
|
||||
- [Download the latest release.](https://github.com/twbs/bootstrap/archive/v4.1.3.zip)
|
||||
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`
|
||||
- Install with [npm](https://www.npmjs.com/): `npm install bootstrap`
|
||||
- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@4.1.3`
|
||||
- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:4.1.3`
|
||||
- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass`
|
||||
|
||||
Read the [Getting started page](https://getbootstrap.com/docs/4.1/getting-started/introduction/) for information on the framework contents, templates and examples, and more.
|
||||
|
||||
## Status
|
||||
|
||||
[![Slack](https://bootstrap-slack.herokuapp.com/badge.svg)](https://bootstrap-slack.herokuapp.com/)
|
||||
[![Build Status](https://img.shields.io/travis/twbs/bootstrap/v4-dev.svg)](https://travis-ci.org/twbs/bootstrap)
|
||||
[![npm version](https://img.shields.io/npm/v/bootstrap.svg)](https://www.npmjs.com/package/bootstrap)
|
||||
[![Gem version](https://img.shields.io/gem/v/bootstrap.svg)](https://rubygems.org/gems/bootstrap)
|
||||
[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue.svg)](https://atmospherejs.com/twbs/bootstrap)
|
||||
[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap.svg)](https://packagist.org/packages/twbs/bootstrap)
|
||||
[![NuGet](https://img.shields.io/nuget/vpre/bootstrap.svg)](https://www.nuget.org/packages/bootstrap/absoluteLatest)
|
||||
[![peerDependencies Status](https://img.shields.io/david/peer/twbs/bootstrap.svg)](https://david-dm.org/twbs/bootstrap?type=peer)
|
||||
[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap.svg)](https://david-dm.org/twbs/bootstrap?type=dev)
|
||||
[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/v4-dev.svg)](https://coveralls.io/github/twbs/bootstrap?branch=v4-dev)
|
||||
[![CSS gzip size](http://img.badgesize.io/twbs/bootstrap/v4-dev/dist/css/bootstrap.min.css?compression=gzip&label=CSS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/css/bootstrap.min.css)
|
||||
[![JS gzip size](http://img.badgesize.io/twbs/bootstrap/v4-dev/dist/js/bootstrap.min.js?compression=gzip&label=JS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/js/bootstrap.min.js)
|
||||
|
||||
[![Sauce Labs Test Status](https://saucelabs.com/browser-matrix/bootstrap.svg)](https://saucelabs.com/u/bootstrap)
|
||||
|
||||
## What's included
|
||||
|
||||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this:
|
||||
|
||||
```
|
||||
bootstrap/
|
||||
└── dist/
|
||||
├── css/
|
||||
│ ├── bootstrap-grid.css
|
||||
│ ├── bootstrap-grid.css.map
|
||||
│ ├── bootstrap-grid.min.css
|
||||
│ ├── bootstrap-grid.min.css.map
|
||||
│ ├── bootstrap-reboot.css
|
||||
│ ├── bootstrap-reboot.css.map
|
||||
│ ├── bootstrap-reboot.min.css
|
||||
│ ├── bootstrap-reboot.min.css.map
|
||||
│ ├── bootstrap.css
|
||||
│ ├── bootstrap.css.map
|
||||
│ ├── bootstrap.min.css
|
||||
│ └── bootstrap.min.css.map
|
||||
└── js/
|
||||
├── bootstrap.bundle.js
|
||||
├── bootstrap.bundle.js.map
|
||||
├── bootstrap.bundle.min.js
|
||||
├── bootstrap.bundle.min.js.map
|
||||
├── bootstrap.js
|
||||
├── bootstrap.js.map
|
||||
├── bootstrap.min.js
|
||||
└── bootstrap.min.js.map
|
||||
```
|
||||
|
||||
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [source maps](https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/), but not [jQuery](https://jquery.com/).
|
||||
|
||||
|
||||
## Bugs and feature requests
|
||||
|
||||
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new).
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](https://jekyllrb.com/) and publicly hosted on GitHub Pages at <https://getbootstrap.com/>. The docs may also be run locally.
|
||||
|
||||
Documentation search is powered by [Algolia's DocSearch](https://community.algolia.com/docsearch/). Working on our search? Be sure to set `debug: true` in `site/docs/4.1/assets/js/src/search.js` file.
|
||||
|
||||
### Running documentation locally
|
||||
|
||||
1. Run through the [tooling setup](https://getbootstrap.com/docs/4.1/getting-started/build-tools/#tooling-setup) to install Jekyll (the site builder) and other Ruby dependencies with `bundle install`.
|
||||
2. Run `npm install` to install Node.js dependencies.
|
||||
3. Run `npm start` to compile CSS and JavaScript files, generate our docs, and watch for changes.
|
||||
4. Open `http://localhost:9001` in your browser, and voilà.
|
||||
|
||||
Learn more about using Jekyll by reading its [documentation](https://jekyllrb.com/docs/home/).
|
||||
|
||||
### Documentation for previous releases
|
||||
|
||||
- For v2.3.2: <https://getbootstrap.com/2.3.2/>
|
||||
- For v3.3.x: <https://getbootstrap.com/docs/3.3/>
|
||||
- For v4.0.x: <https://getbootstrap.com/docs/4.0/>
|
||||
|
||||
[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development.
|
||||
|
||||
Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo).
|
||||
|
||||
Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <https://editorconfig.org/>.
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
Get updates on Bootstrap's development and chat with the project maintainers and community members.
|
||||
|
||||
- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap).
|
||||
- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/).
|
||||
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
|
||||
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
|
||||
- Implementation help may be found at Stack Overflow (tagged [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4)).
|
||||
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
|
||||
|
||||
|
||||
## Versioning
|
||||
|
||||
For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](https://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible.
|
||||
|
||||
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](https://blog.getbootstrap.com/) contain summaries of the most noteworthy changes made in each release.
|
||||
|
||||
|
||||
## Creators
|
||||
|
||||
**Mark Otto**
|
||||
|
||||
- <https://twitter.com/mdo>
|
||||
- <https://github.com/mdo>
|
||||
|
||||
**Jacob Thornton**
|
||||
|
||||
- <https://twitter.com/fat>
|
||||
- <https://github.com/fat>
|
||||
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Code and documentation copyright 2011-2018 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE).
|
@ -0,0 +1,63 @@
|
||||
# Dependencies
|
||||
markdown: kramdown
|
||||
highlighter: rouge
|
||||
|
||||
kramdown:
|
||||
auto_ids: true
|
||||
|
||||
# Permalinks
|
||||
permalink: pretty
|
||||
|
||||
# Server
|
||||
source: "site"
|
||||
destination: ./_gh_pages
|
||||
host: 0.0.0.0
|
||||
port: 9001
|
||||
baseurl: ""
|
||||
url: "https://getbootstrap.com"
|
||||
encoding: UTF-8
|
||||
exclude:
|
||||
- docs/4.1/assets/scss/
|
||||
|
||||
plugins:
|
||||
- jekyll-redirect-from
|
||||
- jekyll-sitemap
|
||||
- jekyll-toc
|
||||
|
||||
# Social
|
||||
title: Bootstrap
|
||||
description: "The most popular HTML, CSS, and JS library in the world."
|
||||
twitter: getbootstrap
|
||||
authors: "Mark Otto, Jacob Thornton, and Bootstrap contributors"
|
||||
social_image_path: /docs/4.1/assets/brand/bootstrap-social.png
|
||||
social_logo_path: /docs/4.1/assets/brand/bootstrap-social-logo.png
|
||||
|
||||
# Custom variables
|
||||
current_version: 4.1.3
|
||||
current_ruby_version: 4.1.3
|
||||
docs_version: 4.1
|
||||
repo: "https://github.com/twbs/bootstrap"
|
||||
slack: "https://bootstrap-slack.herokuapp.com"
|
||||
blog: "https://blog.getbootstrap.com"
|
||||
expo: "https://expo.getbootstrap.com"
|
||||
jobs: "https://jobs.getbootstrap.com"
|
||||
themes: "https://themes.getbootstrap.com"
|
||||
|
||||
download:
|
||||
source: "https://github.com/twbs/bootstrap/archive/v4.1.3.zip"
|
||||
dist: "https://github.com/twbs/bootstrap/releases/download/v4.1.3/bootstrap-4.1.3-dist.zip"
|
||||
|
||||
cdn:
|
||||
# See https://www.srihash.org for info on how to generate the hashes
|
||||
css: "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
|
||||
css_hash: "sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
|
||||
js: "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
|
||||
js_hash: "sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
|
||||
jquery: "https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
jquery_hash: "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
||||
popper: "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
|
||||
popper_hash: "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
|
||||
|
||||
toc:
|
||||
min_level: 2
|
||||
max_level: 4
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "twbs/bootstrap",
|
||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
||||
"keywords": [
|
||||
"css",
|
||||
"js",
|
||||
"sass",
|
||||
"mobile-first",
|
||||
"responsive",
|
||||
"front-end",
|
||||
"framework",
|
||||
"web"
|
||||
],
|
||||
"homepage": "https://getbootstrap.com/",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Otto",
|
||||
"email": "markdotto@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Jacob Thornton",
|
||||
"email": "jacobthornton@gmail.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twbs/bootstrap/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3.x-dev"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"twitter/bootstrap": "self.version"
|
||||
}
|
||||
}
|
183
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/alert.js
vendored
Normal file
183
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/alert.js
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): alert.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Alert = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'alert'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.alert'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Selector = {
|
||||
DISMISS : '[data-dismiss="alert"]'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
CLOSE : `close${EVENT_KEY}`,
|
||||
CLOSED : `closed${EVENT_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
ALERT : 'alert',
|
||||
FADE : 'fade',
|
||||
SHOW : 'show'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Alert {
|
||||
constructor(element) {
|
||||
this._element = element
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
close(element) {
|
||||
let rootElement = this._element
|
||||
if (element) {
|
||||
rootElement = this._getRootElement(element)
|
||||
}
|
||||
|
||||
const customEvent = this._triggerCloseEvent(rootElement)
|
||||
|
||||
if (customEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
this._removeElement(rootElement)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
this._element = null
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getRootElement(element) {
|
||||
const selector = Util.getSelectorFromElement(element)
|
||||
let parent = false
|
||||
|
||||
if (selector) {
|
||||
parent = document.querySelector(selector)
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
parent = $(element).closest(`.${ClassName.ALERT}`)[0]
|
||||
}
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
_triggerCloseEvent(element) {
|
||||
const closeEvent = $.Event(Event.CLOSE)
|
||||
|
||||
$(element).trigger(closeEvent)
|
||||
return closeEvent
|
||||
}
|
||||
|
||||
_removeElement(element) {
|
||||
$(element).removeClass(ClassName.SHOW)
|
||||
|
||||
if (!$(element).hasClass(ClassName.FADE)) {
|
||||
this._destroyElement(element)
|
||||
return
|
||||
}
|
||||
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(element)
|
||||
|
||||
$(element)
|
||||
.one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
}
|
||||
|
||||
_destroyElement(element) {
|
||||
$(element)
|
||||
.detach()
|
||||
.trigger(Event.CLOSED)
|
||||
.remove()
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
const $element = $(this)
|
||||
let data = $element.data(DATA_KEY)
|
||||
|
||||
if (!data) {
|
||||
data = new Alert(this)
|
||||
$element.data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (config === 'close') {
|
||||
data[config](this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static _handleDismiss(alertInstance) {
|
||||
return function (event) {
|
||||
if (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
alertInstance.close(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document).on(
|
||||
Event.CLICK_DATA_API,
|
||||
Selector.DISMISS,
|
||||
Alert._handleDismiss(new Alert())
|
||||
)
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Alert._jQueryInterface
|
||||
$.fn[NAME].Constructor = Alert
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Alert._jQueryInterface
|
||||
}
|
||||
|
||||
return Alert
|
||||
})($)
|
||||
|
||||
export default Alert
|
175
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/button.js
vendored
Normal file
175
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/button.js
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
import $ from 'jquery'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): button.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Button = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'button'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.button'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const ClassName = {
|
||||
ACTIVE : 'active',
|
||||
BUTTON : 'btn',
|
||||
FOCUS : 'focus'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
|
||||
DATA_TOGGLE : '[data-toggle="buttons"]',
|
||||
INPUT : 'input',
|
||||
ACTIVE : '.active',
|
||||
BUTTON : '.btn'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
|
||||
FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +
|
||||
`blur${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Button {
|
||||
constructor(element) {
|
||||
this._element = element
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toggle() {
|
||||
let triggerChangeEvent = true
|
||||
let addAriaPressed = true
|
||||
const rootElement = $(this._element).closest(
|
||||
Selector.DATA_TOGGLE
|
||||
)[0]
|
||||
|
||||
if (rootElement) {
|
||||
const input = this._element.querySelector(Selector.INPUT)
|
||||
|
||||
if (input) {
|
||||
if (input.type === 'radio') {
|
||||
if (input.checked &&
|
||||
this._element.classList.contains(ClassName.ACTIVE)) {
|
||||
triggerChangeEvent = false
|
||||
} else {
|
||||
const activeElement = rootElement.querySelector(Selector.ACTIVE)
|
||||
|
||||
if (activeElement) {
|
||||
$(activeElement).removeClass(ClassName.ACTIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerChangeEvent) {
|
||||
if (input.hasAttribute('disabled') ||
|
||||
rootElement.hasAttribute('disabled') ||
|
||||
input.classList.contains('disabled') ||
|
||||
rootElement.classList.contains('disabled')) {
|
||||
return
|
||||
}
|
||||
input.checked = !this._element.classList.contains(ClassName.ACTIVE)
|
||||
$(input).trigger('change')
|
||||
}
|
||||
|
||||
input.focus()
|
||||
addAriaPressed = false
|
||||
}
|
||||
}
|
||||
|
||||
if (addAriaPressed) {
|
||||
this._element.setAttribute('aria-pressed',
|
||||
!this._element.classList.contains(ClassName.ACTIVE))
|
||||
}
|
||||
|
||||
if (triggerChangeEvent) {
|
||||
$(this._element).toggleClass(ClassName.ACTIVE)
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
this._element = null
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
|
||||
if (!data) {
|
||||
data = new Button(this)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (config === 'toggle') {
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document)
|
||||
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
let button = event.target
|
||||
|
||||
if (!$(button).hasClass(ClassName.BUTTON)) {
|
||||
button = $(button).closest(Selector.BUTTON)
|
||||
}
|
||||
|
||||
Button._jQueryInterface.call($(button), 'toggle')
|
||||
})
|
||||
.on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
|
||||
const button = $(event.target).closest(Selector.BUTTON)[0]
|
||||
$(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Button._jQueryInterface
|
||||
$.fn[NAME].Constructor = Button
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Button._jQueryInterface
|
||||
}
|
||||
|
||||
return Button
|
||||
})($)
|
||||
|
||||
export default Button
|
520
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/carousel.js
vendored
Normal file
520
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/carousel.js
vendored
Normal file
@ -0,0 +1,520 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): carousel.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Carousel = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'carousel'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.carousel'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
|
||||
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
|
||||
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
|
||||
|
||||
const Default = {
|
||||
interval : 5000,
|
||||
keyboard : true,
|
||||
slide : false,
|
||||
pause : 'hover',
|
||||
wrap : true
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
interval : '(number|boolean)',
|
||||
keyboard : 'boolean',
|
||||
slide : '(boolean|string)',
|
||||
pause : '(string|boolean)',
|
||||
wrap : 'boolean'
|
||||
}
|
||||
|
||||
const Direction = {
|
||||
NEXT : 'next',
|
||||
PREV : 'prev',
|
||||
LEFT : 'left',
|
||||
RIGHT : 'right'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
SLIDE : `slide${EVENT_KEY}`,
|
||||
SLID : `slid${EVENT_KEY}`,
|
||||
KEYDOWN : `keydown${EVENT_KEY}`,
|
||||
MOUSEENTER : `mouseenter${EVENT_KEY}`,
|
||||
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
|
||||
TOUCHEND : `touchend${EVENT_KEY}`,
|
||||
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
CAROUSEL : 'carousel',
|
||||
ACTIVE : 'active',
|
||||
SLIDE : 'slide',
|
||||
RIGHT : 'carousel-item-right',
|
||||
LEFT : 'carousel-item-left',
|
||||
NEXT : 'carousel-item-next',
|
||||
PREV : 'carousel-item-prev',
|
||||
ITEM : 'carousel-item'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
ACTIVE : '.active',
|
||||
ACTIVE_ITEM : '.active.carousel-item',
|
||||
ITEM : '.carousel-item',
|
||||
NEXT_PREV : '.carousel-item-next, .carousel-item-prev',
|
||||
INDICATORS : '.carousel-indicators',
|
||||
DATA_SLIDE : '[data-slide], [data-slide-to]',
|
||||
DATA_RIDE : '[data-ride="carousel"]'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Carousel {
|
||||
constructor(element, config) {
|
||||
this._items = null
|
||||
this._interval = null
|
||||
this._activeElement = null
|
||||
|
||||
this._isPaused = false
|
||||
this._isSliding = false
|
||||
|
||||
this.touchTimeout = null
|
||||
|
||||
this._config = this._getConfig(config)
|
||||
this._element = $(element)[0]
|
||||
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
|
||||
|
||||
this._addEventListeners()
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
next() {
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.NEXT)
|
||||
}
|
||||
}
|
||||
|
||||
nextWhenVisible() {
|
||||
// Don't call next when the page isn't visible
|
||||
// or the carousel or its parent isn't visible
|
||||
if (!document.hidden &&
|
||||
($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
|
||||
this.next()
|
||||
}
|
||||
}
|
||||
|
||||
prev() {
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.PREV)
|
||||
}
|
||||
}
|
||||
|
||||
pause(event) {
|
||||
if (!event) {
|
||||
this._isPaused = true
|
||||
}
|
||||
|
||||
if (this._element.querySelector(Selector.NEXT_PREV)) {
|
||||
Util.triggerTransitionEnd(this._element)
|
||||
this.cycle(true)
|
||||
}
|
||||
|
||||
clearInterval(this._interval)
|
||||
this._interval = null
|
||||
}
|
||||
|
||||
cycle(event) {
|
||||
if (!event) {
|
||||
this._isPaused = false
|
||||
}
|
||||
|
||||
if (this._interval) {
|
||||
clearInterval(this._interval)
|
||||
this._interval = null
|
||||
}
|
||||
|
||||
if (this._config.interval && !this._isPaused) {
|
||||
this._interval = setInterval(
|
||||
(document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
|
||||
this._config.interval
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
to(index) {
|
||||
this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
|
||||
|
||||
const activeIndex = this._getItemIndex(this._activeElement)
|
||||
|
||||
if (index > this._items.length - 1 || index < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._isSliding) {
|
||||
$(this._element).one(Event.SLID, () => this.to(index))
|
||||
return
|
||||
}
|
||||
|
||||
if (activeIndex === index) {
|
||||
this.pause()
|
||||
this.cycle()
|
||||
return
|
||||
}
|
||||
|
||||
const direction = index > activeIndex
|
||||
? Direction.NEXT
|
||||
: Direction.PREV
|
||||
|
||||
this._slide(direction, this._items[index])
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$(this._element).off(EVENT_KEY)
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
|
||||
this._items = null
|
||||
this._config = null
|
||||
this._element = null
|
||||
this._interval = null
|
||||
this._isPaused = null
|
||||
this._isSliding = null
|
||||
this._activeElement = null
|
||||
this._indicatorsElement = null
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...config
|
||||
}
|
||||
Util.typeCheckConfig(NAME, config, DefaultType)
|
||||
return config
|
||||
}
|
||||
|
||||
_addEventListeners() {
|
||||
if (this._config.keyboard) {
|
||||
$(this._element)
|
||||
.on(Event.KEYDOWN, (event) => this._keydown(event))
|
||||
}
|
||||
|
||||
if (this._config.pause === 'hover') {
|
||||
$(this._element)
|
||||
.on(Event.MOUSEENTER, (event) => this.pause(event))
|
||||
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
// If it's a touch-enabled device, mouseenter/leave are fired as
|
||||
// part of the mouse compatibility events on first tap - the carousel
|
||||
// would stop cycling until user tapped out of it;
|
||||
// here, we listen for touchend, explicitly pause the carousel
|
||||
// (as if it's the second time we tap on it, mouseenter compat event
|
||||
// is NOT fired) and after a timeout (to allow for mouse compatibility
|
||||
// events to fire) we explicitly restart cycling
|
||||
$(this._element).on(Event.TOUCHEND, () => {
|
||||
this.pause()
|
||||
if (this.touchTimeout) {
|
||||
clearTimeout(this.touchTimeout)
|
||||
}
|
||||
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_keydown(event) {
|
||||
if (/input|textarea/i.test(event.target.tagName)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
case ARROW_LEFT_KEYCODE:
|
||||
event.preventDefault()
|
||||
this.prev()
|
||||
break
|
||||
case ARROW_RIGHT_KEYCODE:
|
||||
event.preventDefault()
|
||||
this.next()
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
_getItemIndex(element) {
|
||||
this._items = element && element.parentNode
|
||||
? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))
|
||||
: []
|
||||
return this._items.indexOf(element)
|
||||
}
|
||||
|
||||
_getItemByDirection(direction, activeElement) {
|
||||
const isNextDirection = direction === Direction.NEXT
|
||||
const isPrevDirection = direction === Direction.PREV
|
||||
const activeIndex = this._getItemIndex(activeElement)
|
||||
const lastItemIndex = this._items.length - 1
|
||||
const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
|
||||
isNextDirection && activeIndex === lastItemIndex
|
||||
|
||||
if (isGoingToWrap && !this._config.wrap) {
|
||||
return activeElement
|
||||
}
|
||||
|
||||
const delta = direction === Direction.PREV ? -1 : 1
|
||||
const itemIndex = (activeIndex + delta) % this._items.length
|
||||
|
||||
return itemIndex === -1
|
||||
? this._items[this._items.length - 1] : this._items[itemIndex]
|
||||
}
|
||||
|
||||
_triggerSlideEvent(relatedTarget, eventDirectionName) {
|
||||
const targetIndex = this._getItemIndex(relatedTarget)
|
||||
const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))
|
||||
const slideEvent = $.Event(Event.SLIDE, {
|
||||
relatedTarget,
|
||||
direction: eventDirectionName,
|
||||
from: fromIndex,
|
||||
to: targetIndex
|
||||
})
|
||||
|
||||
$(this._element).trigger(slideEvent)
|
||||
|
||||
return slideEvent
|
||||
}
|
||||
|
||||
_setActiveIndicatorElement(element) {
|
||||
if (this._indicatorsElement) {
|
||||
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))
|
||||
$(indicators)
|
||||
.removeClass(ClassName.ACTIVE)
|
||||
|
||||
const nextIndicator = this._indicatorsElement.children[
|
||||
this._getItemIndex(element)
|
||||
]
|
||||
|
||||
if (nextIndicator) {
|
||||
$(nextIndicator).addClass(ClassName.ACTIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_slide(direction, element) {
|
||||
const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
|
||||
const activeElementIndex = this._getItemIndex(activeElement)
|
||||
const nextElement = element || activeElement &&
|
||||
this._getItemByDirection(direction, activeElement)
|
||||
const nextElementIndex = this._getItemIndex(nextElement)
|
||||
const isCycling = Boolean(this._interval)
|
||||
|
||||
let directionalClassName
|
||||
let orderClassName
|
||||
let eventDirectionName
|
||||
|
||||
if (direction === Direction.NEXT) {
|
||||
directionalClassName = ClassName.LEFT
|
||||
orderClassName = ClassName.NEXT
|
||||
eventDirectionName = Direction.LEFT
|
||||
} else {
|
||||
directionalClassName = ClassName.RIGHT
|
||||
orderClassName = ClassName.PREV
|
||||
eventDirectionName = Direction.RIGHT
|
||||
}
|
||||
|
||||
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
|
||||
this._isSliding = false
|
||||
return
|
||||
}
|
||||
|
||||
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
|
||||
if (slideEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!activeElement || !nextElement) {
|
||||
// Some weirdness is happening, so we bail
|
||||
return
|
||||
}
|
||||
|
||||
this._isSliding = true
|
||||
|
||||
if (isCycling) {
|
||||
this.pause()
|
||||
}
|
||||
|
||||
this._setActiveIndicatorElement(nextElement)
|
||||
|
||||
const slidEvent = $.Event(Event.SLID, {
|
||||
relatedTarget: nextElement,
|
||||
direction: eventDirectionName,
|
||||
from: activeElementIndex,
|
||||
to: nextElementIndex
|
||||
})
|
||||
|
||||
if ($(this._element).hasClass(ClassName.SLIDE)) {
|
||||
$(nextElement).addClass(orderClassName)
|
||||
|
||||
Util.reflow(nextElement)
|
||||
|
||||
$(activeElement).addClass(directionalClassName)
|
||||
$(nextElement).addClass(directionalClassName)
|
||||
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
|
||||
|
||||
$(activeElement)
|
||||
.one(Util.TRANSITION_END, () => {
|
||||
$(nextElement)
|
||||
.removeClass(`${directionalClassName} ${orderClassName}`)
|
||||
.addClass(ClassName.ACTIVE)
|
||||
|
||||
$(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
|
||||
|
||||
this._isSliding = false
|
||||
|
||||
setTimeout(() => $(this._element).trigger(slidEvent), 0)
|
||||
})
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
$(activeElement).removeClass(ClassName.ACTIVE)
|
||||
$(nextElement).addClass(ClassName.ACTIVE)
|
||||
|
||||
this._isSliding = false
|
||||
$(this._element).trigger(slidEvent)
|
||||
}
|
||||
|
||||
if (isCycling) {
|
||||
this.cycle()
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
let _config = {
|
||||
...Default,
|
||||
...$(this).data()
|
||||
}
|
||||
|
||||
if (typeof config === 'object') {
|
||||
_config = {
|
||||
..._config,
|
||||
...config
|
||||
}
|
||||
}
|
||||
|
||||
const action = typeof config === 'string' ? config : _config.slide
|
||||
|
||||
if (!data) {
|
||||
data = new Carousel(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'number') {
|
||||
data.to(config)
|
||||
} else if (typeof action === 'string') {
|
||||
if (typeof data[action] === 'undefined') {
|
||||
throw new TypeError(`No method named "${action}"`)
|
||||
}
|
||||
data[action]()
|
||||
} else if (_config.interval) {
|
||||
data.pause()
|
||||
data.cycle()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static _dataApiClickHandler(event) {
|
||||
const selector = Util.getSelectorFromElement(this)
|
||||
|
||||
if (!selector) {
|
||||
return
|
||||
}
|
||||
|
||||
const target = $(selector)[0]
|
||||
|
||||
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
|
||||
return
|
||||
}
|
||||
|
||||
const config = {
|
||||
...$(target).data(),
|
||||
...$(this).data()
|
||||
}
|
||||
const slideIndex = this.getAttribute('data-slide-to')
|
||||
|
||||
if (slideIndex) {
|
||||
config.interval = false
|
||||
}
|
||||
|
||||
Carousel._jQueryInterface.call($(target), config)
|
||||
|
||||
if (slideIndex) {
|
||||
$(target).data(DATA_KEY).to(slideIndex)
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document)
|
||||
.on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
|
||||
|
||||
$(window).on(Event.LOAD_DATA_API, () => {
|
||||
const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))
|
||||
for (let i = 0, len = carousels.length; i < len; i++) {
|
||||
const $carousel = $(carousels[i])
|
||||
Carousel._jQueryInterface.call($carousel, $carousel.data())
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Carousel._jQueryInterface
|
||||
$.fn[NAME].Constructor = Carousel
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Carousel._jQueryInterface
|
||||
}
|
||||
|
||||
return Carousel
|
||||
})($)
|
||||
|
||||
export default Carousel
|
398
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/collapse.js
vendored
Normal file
398
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/collapse.js
vendored
Normal file
@ -0,0 +1,398 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): collapse.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Collapse = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'collapse'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.collapse'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Default = {
|
||||
toggle : true,
|
||||
parent : ''
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
toggle : 'boolean',
|
||||
parent : '(string|element)'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
SHOW : 'show',
|
||||
COLLAPSE : 'collapse',
|
||||
COLLAPSING : 'collapsing',
|
||||
COLLAPSED : 'collapsed'
|
||||
}
|
||||
|
||||
const Dimension = {
|
||||
WIDTH : 'width',
|
||||
HEIGHT : 'height'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
ACTIVES : '.show, .collapsing',
|
||||
DATA_TOGGLE : '[data-toggle="collapse"]'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Collapse {
|
||||
constructor(element, config) {
|
||||
this._isTransitioning = false
|
||||
this._element = element
|
||||
this._config = this._getConfig(config)
|
||||
this._triggerArray = $.makeArray(document.querySelectorAll(
|
||||
`[data-toggle="collapse"][href="#${element.id}"],` +
|
||||
`[data-toggle="collapse"][data-target="#${element.id}"]`
|
||||
))
|
||||
const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
|
||||
for (let i = 0, len = toggleList.length; i < len; i++) {
|
||||
const elem = toggleList[i]
|
||||
const selector = Util.getSelectorFromElement(elem)
|
||||
const filterElement = [].slice.call(document.querySelectorAll(selector))
|
||||
.filter((foundElem) => foundElem === element)
|
||||
|
||||
if (selector !== null && filterElement.length > 0) {
|
||||
this._selector = selector
|
||||
this._triggerArray.push(elem)
|
||||
}
|
||||
}
|
||||
|
||||
this._parent = this._config.parent ? this._getParent() : null
|
||||
|
||||
if (!this._config.parent) {
|
||||
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
|
||||
}
|
||||
|
||||
if (this._config.toggle) {
|
||||
this.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toggle() {
|
||||
if ($(this._element).hasClass(ClassName.SHOW)) {
|
||||
this.hide()
|
||||
} else {
|
||||
this.show()
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this._isTransitioning ||
|
||||
$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
let actives
|
||||
let activesData
|
||||
|
||||
if (this._parent) {
|
||||
actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))
|
||||
.filter((elem) => elem.getAttribute('data-parent') === this._config.parent)
|
||||
|
||||
if (actives.length === 0) {
|
||||
actives = null
|
||||
}
|
||||
}
|
||||
|
||||
if (actives) {
|
||||
activesData = $(actives).not(this._selector).data(DATA_KEY)
|
||||
if (activesData && activesData._isTransitioning) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const startEvent = $.Event(Event.SHOW)
|
||||
$(this._element).trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (actives) {
|
||||
Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')
|
||||
if (!activesData) {
|
||||
$(actives).data(DATA_KEY, null)
|
||||
}
|
||||
}
|
||||
|
||||
const dimension = this._getDimension()
|
||||
|
||||
$(this._element)
|
||||
.removeClass(ClassName.COLLAPSE)
|
||||
.addClass(ClassName.COLLAPSING)
|
||||
|
||||
this._element.style[dimension] = 0
|
||||
|
||||
if (this._triggerArray.length) {
|
||||
$(this._triggerArray)
|
||||
.removeClass(ClassName.COLLAPSED)
|
||||
.attr('aria-expanded', true)
|
||||
}
|
||||
|
||||
this.setTransitioning(true)
|
||||
|
||||
const complete = () => {
|
||||
$(this._element)
|
||||
.removeClass(ClassName.COLLAPSING)
|
||||
.addClass(ClassName.COLLAPSE)
|
||||
.addClass(ClassName.SHOW)
|
||||
|
||||
this._element.style[dimension] = ''
|
||||
|
||||
this.setTransitioning(false)
|
||||
|
||||
$(this._element).trigger(Event.SHOWN)
|
||||
}
|
||||
|
||||
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
|
||||
const scrollSize = `scroll${capitalizedDimension}`
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
||||
|
||||
$(this._element)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
|
||||
this._element.style[dimension] = `${this._element[scrollSize]}px`
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this._isTransitioning ||
|
||||
!$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
const startEvent = $.Event(Event.HIDE)
|
||||
$(this._element).trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
const dimension = this._getDimension()
|
||||
|
||||
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
|
||||
|
||||
Util.reflow(this._element)
|
||||
|
||||
$(this._element)
|
||||
.addClass(ClassName.COLLAPSING)
|
||||
.removeClass(ClassName.COLLAPSE)
|
||||
.removeClass(ClassName.SHOW)
|
||||
|
||||
const triggerArrayLength = this._triggerArray.length
|
||||
if (triggerArrayLength > 0) {
|
||||
for (let i = 0; i < triggerArrayLength; i++) {
|
||||
const trigger = this._triggerArray[i]
|
||||
const selector = Util.getSelectorFromElement(trigger)
|
||||
if (selector !== null) {
|
||||
const $elem = $([].slice.call(document.querySelectorAll(selector)))
|
||||
if (!$elem.hasClass(ClassName.SHOW)) {
|
||||
$(trigger).addClass(ClassName.COLLAPSED)
|
||||
.attr('aria-expanded', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setTransitioning(true)
|
||||
|
||||
const complete = () => {
|
||||
this.setTransitioning(false)
|
||||
$(this._element)
|
||||
.removeClass(ClassName.COLLAPSING)
|
||||
.addClass(ClassName.COLLAPSE)
|
||||
.trigger(Event.HIDDEN)
|
||||
}
|
||||
|
||||
this._element.style[dimension] = ''
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
||||
|
||||
$(this._element)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
}
|
||||
|
||||
setTransitioning(isTransitioning) {
|
||||
this._isTransitioning = isTransitioning
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
|
||||
this._config = null
|
||||
this._parent = null
|
||||
this._element = null
|
||||
this._triggerArray = null
|
||||
this._isTransitioning = null
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...config
|
||||
}
|
||||
config.toggle = Boolean(config.toggle) // Coerce string values
|
||||
Util.typeCheckConfig(NAME, config, DefaultType)
|
||||
return config
|
||||
}
|
||||
|
||||
_getDimension() {
|
||||
const hasWidth = $(this._element).hasClass(Dimension.WIDTH)
|
||||
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
|
||||
}
|
||||
|
||||
_getParent() {
|
||||
let parent = null
|
||||
if (Util.isElement(this._config.parent)) {
|
||||
parent = this._config.parent
|
||||
|
||||
// It's a jQuery object
|
||||
if (typeof this._config.parent.jquery !== 'undefined') {
|
||||
parent = this._config.parent[0]
|
||||
}
|
||||
} else {
|
||||
parent = document.querySelector(this._config.parent)
|
||||
}
|
||||
|
||||
const selector =
|
||||
`[data-toggle="collapse"][data-parent="${this._config.parent}"]`
|
||||
|
||||
const children = [].slice.call(parent.querySelectorAll(selector))
|
||||
$(children).each((i, element) => {
|
||||
this._addAriaAndCollapsedClass(
|
||||
Collapse._getTargetFromElement(element),
|
||||
[element]
|
||||
)
|
||||
})
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
_addAriaAndCollapsedClass(element, triggerArray) {
|
||||
if (element) {
|
||||
const isOpen = $(element).hasClass(ClassName.SHOW)
|
||||
|
||||
if (triggerArray.length) {
|
||||
$(triggerArray)
|
||||
.toggleClass(ClassName.COLLAPSED, !isOpen)
|
||||
.attr('aria-expanded', isOpen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _getTargetFromElement(element) {
|
||||
const selector = Util.getSelectorFromElement(element)
|
||||
return selector ? document.querySelector(selector) : null
|
||||
}
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
const $this = $(this)
|
||||
let data = $this.data(DATA_KEY)
|
||||
const _config = {
|
||||
...Default,
|
||||
...$this.data(),
|
||||
...typeof config === 'object' && config ? config : {}
|
||||
}
|
||||
|
||||
if (!data && _config.toggle && /show|hide/.test(config)) {
|
||||
_config.toggle = false
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = new Collapse(this, _config)
|
||||
$this.data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
|
||||
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
|
||||
if (event.currentTarget.tagName === 'A') {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
const $trigger = $(this)
|
||||
const selector = Util.getSelectorFromElement(this)
|
||||
const selectors = [].slice.call(document.querySelectorAll(selector))
|
||||
$(selectors).each(function () {
|
||||
const $target = $(this)
|
||||
const data = $target.data(DATA_KEY)
|
||||
const config = data ? 'toggle' : $trigger.data()
|
||||
Collapse._jQueryInterface.call($target, config)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Collapse._jQueryInterface
|
||||
$.fn[NAME].Constructor = Collapse
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Collapse._jQueryInterface
|
||||
}
|
||||
|
||||
return Collapse
|
||||
})($)
|
||||
|
||||
export default Collapse
|
494
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/dropdown.js
vendored
Normal file
494
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/dropdown.js
vendored
Normal file
@ -0,0 +1,494 @@
|
||||
import $ from 'jquery'
|
||||
import Popper from 'popper.js'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): dropdown.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Dropdown = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'dropdown'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.dropdown'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
|
||||
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
|
||||
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
|
||||
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
|
||||
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
|
||||
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
|
||||
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
|
||||
|
||||
const Event = {
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
CLICK : `click${EVENT_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
|
||||
KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,
|
||||
KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
DISABLED : 'disabled',
|
||||
SHOW : 'show',
|
||||
DROPUP : 'dropup',
|
||||
DROPRIGHT : 'dropright',
|
||||
DROPLEFT : 'dropleft',
|
||||
MENURIGHT : 'dropdown-menu-right',
|
||||
MENULEFT : 'dropdown-menu-left',
|
||||
POSITION_STATIC : 'position-static'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DATA_TOGGLE : '[data-toggle="dropdown"]',
|
||||
FORM_CHILD : '.dropdown form',
|
||||
MENU : '.dropdown-menu',
|
||||
NAVBAR_NAV : '.navbar-nav',
|
||||
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
|
||||
}
|
||||
|
||||
const AttachmentMap = {
|
||||
TOP : 'top-start',
|
||||
TOPEND : 'top-end',
|
||||
BOTTOM : 'bottom-start',
|
||||
BOTTOMEND : 'bottom-end',
|
||||
RIGHT : 'right-start',
|
||||
RIGHTEND : 'right-end',
|
||||
LEFT : 'left-start',
|
||||
LEFTEND : 'left-end'
|
||||
}
|
||||
|
||||
const Default = {
|
||||
offset : 0,
|
||||
flip : true,
|
||||
boundary : 'scrollParent',
|
||||
reference : 'toggle',
|
||||
display : 'dynamic'
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
offset : '(number|string|function)',
|
||||
flip : 'boolean',
|
||||
boundary : '(string|element)',
|
||||
reference : '(string|element)',
|
||||
display : 'string'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Dropdown {
|
||||
constructor(element, config) {
|
||||
this._element = element
|
||||
this._popper = null
|
||||
this._config = this._getConfig(config)
|
||||
this._menu = this._getMenuElement()
|
||||
this._inNavbar = this._detectNavbar()
|
||||
|
||||
this._addEventListeners()
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
static get DefaultType() {
|
||||
return DefaultType
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toggle() {
|
||||
if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {
|
||||
return
|
||||
}
|
||||
|
||||
const parent = Dropdown._getParentFromElement(this._element)
|
||||
const isActive = $(this._menu).hasClass(ClassName.SHOW)
|
||||
|
||||
Dropdown._clearMenus()
|
||||
|
||||
if (isActive) {
|
||||
return
|
||||
}
|
||||
|
||||
const relatedTarget = {
|
||||
relatedTarget: this._element
|
||||
}
|
||||
const showEvent = $.Event(Event.SHOW, relatedTarget)
|
||||
|
||||
$(parent).trigger(showEvent)
|
||||
|
||||
if (showEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Disable totally Popper.js for Dropdown in Navbar
|
||||
if (!this._inNavbar) {
|
||||
/**
|
||||
* Check for Popper dependency
|
||||
* Popper - https://popper.js.org
|
||||
*/
|
||||
if (typeof Popper === 'undefined') {
|
||||
throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')
|
||||
}
|
||||
|
||||
let referenceElement = this._element
|
||||
|
||||
if (this._config.reference === 'parent') {
|
||||
referenceElement = parent
|
||||
} else if (Util.isElement(this._config.reference)) {
|
||||
referenceElement = this._config.reference
|
||||
|
||||
// Check if it's jQuery element
|
||||
if (typeof this._config.reference.jquery !== 'undefined') {
|
||||
referenceElement = this._config.reference[0]
|
||||
}
|
||||
}
|
||||
|
||||
// If boundary is not `scrollParent`, then set position to `static`
|
||||
// to allow the menu to "escape" the scroll parent's boundaries
|
||||
// https://github.com/twbs/bootstrap/issues/24251
|
||||
if (this._config.boundary !== 'scrollParent') {
|
||||
$(parent).addClass(ClassName.POSITION_STATIC)
|
||||
}
|
||||
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
|
||||
}
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement &&
|
||||
$(parent).closest(Selector.NAVBAR_NAV).length === 0) {
|
||||
$(document.body).children().on('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
this._element.focus()
|
||||
this._element.setAttribute('aria-expanded', true)
|
||||
|
||||
$(this._menu).toggleClass(ClassName.SHOW)
|
||||
$(parent)
|
||||
.toggleClass(ClassName.SHOW)
|
||||
.trigger($.Event(Event.SHOWN, relatedTarget))
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
$(this._element).off(EVENT_KEY)
|
||||
this._element = null
|
||||
this._menu = null
|
||||
if (this._popper !== null) {
|
||||
this._popper.destroy()
|
||||
this._popper = null
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
this._inNavbar = this._detectNavbar()
|
||||
if (this._popper !== null) {
|
||||
this._popper.scheduleUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_addEventListeners() {
|
||||
$(this._element).on(Event.CLICK, (event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...this.constructor.Default,
|
||||
...$(this._element).data(),
|
||||
...config
|
||||
}
|
||||
|
||||
Util.typeCheckConfig(
|
||||
NAME,
|
||||
config,
|
||||
this.constructor.DefaultType
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
_getMenuElement() {
|
||||
if (!this._menu) {
|
||||
const parent = Dropdown._getParentFromElement(this._element)
|
||||
if (parent) {
|
||||
this._menu = parent.querySelector(Selector.MENU)
|
||||
}
|
||||
}
|
||||
return this._menu
|
||||
}
|
||||
|
||||
_getPlacement() {
|
||||
const $parentDropdown = $(this._element.parentNode)
|
||||
let placement = AttachmentMap.BOTTOM
|
||||
|
||||
// Handle dropup
|
||||
if ($parentDropdown.hasClass(ClassName.DROPUP)) {
|
||||
placement = AttachmentMap.TOP
|
||||
if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
|
||||
placement = AttachmentMap.TOPEND
|
||||
}
|
||||
} else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
|
||||
placement = AttachmentMap.RIGHT
|
||||
} else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
|
||||
placement = AttachmentMap.LEFT
|
||||
} else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
|
||||
placement = AttachmentMap.BOTTOMEND
|
||||
}
|
||||
return placement
|
||||
}
|
||||
|
||||
_detectNavbar() {
|
||||
return $(this._element).closest('.navbar').length > 0
|
||||
}
|
||||
|
||||
_getPopperConfig() {
|
||||
const offsetConf = {}
|
||||
if (typeof this._config.offset === 'function') {
|
||||
offsetConf.fn = (data) => {
|
||||
data.offsets = {
|
||||
...data.offsets,
|
||||
...this._config.offset(data.offsets) || {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
} else {
|
||||
offsetConf.offset = this._config.offset
|
||||
}
|
||||
|
||||
const popperConfig = {
|
||||
placement: this._getPlacement(),
|
||||
modifiers: {
|
||||
offset: offsetConf,
|
||||
flip: {
|
||||
enabled: this._config.flip
|
||||
},
|
||||
preventOverflow: {
|
||||
boundariesElement: this._config.boundary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disable Popper.js if we have a static display
|
||||
if (this._config.display === 'static') {
|
||||
popperConfig.modifiers.applyStyle = {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
return popperConfig
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
const _config = typeof config === 'object' ? config : null
|
||||
|
||||
if (!data) {
|
||||
data = new Dropdown(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static _clearMenus(event) {
|
||||
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
|
||||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
|
||||
return
|
||||
}
|
||||
|
||||
const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
|
||||
for (let i = 0, len = toggles.length; i < len; i++) {
|
||||
const parent = Dropdown._getParentFromElement(toggles[i])
|
||||
const context = $(toggles[i]).data(DATA_KEY)
|
||||
const relatedTarget = {
|
||||
relatedTarget: toggles[i]
|
||||
}
|
||||
|
||||
if (event && event.type === 'click') {
|
||||
relatedTarget.clickEvent = event
|
||||
}
|
||||
|
||||
if (!context) {
|
||||
continue
|
||||
}
|
||||
|
||||
const dropdownMenu = context._menu
|
||||
if (!$(parent).hasClass(ClassName.SHOW)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (event && (event.type === 'click' &&
|
||||
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
|
||||
$.contains(parent, event.target)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const hideEvent = $.Event(Event.HIDE, relatedTarget)
|
||||
$(parent).trigger(hideEvent)
|
||||
if (hideEvent.isDefaultPrevented()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
$(document.body).children().off('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
toggles[i].setAttribute('aria-expanded', 'false')
|
||||
|
||||
$(dropdownMenu).removeClass(ClassName.SHOW)
|
||||
$(parent)
|
||||
.removeClass(ClassName.SHOW)
|
||||
.trigger($.Event(Event.HIDDEN, relatedTarget))
|
||||
}
|
||||
}
|
||||
|
||||
static _getParentFromElement(element) {
|
||||
let parent
|
||||
const selector = Util.getSelectorFromElement(element)
|
||||
|
||||
if (selector) {
|
||||
parent = document.querySelector(selector)
|
||||
}
|
||||
|
||||
return parent || element.parentNode
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
static _dataApiKeydownHandler(event) {
|
||||
// If not input/textarea:
|
||||
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
|
||||
// If input/textarea:
|
||||
// - If space key => not a dropdown command
|
||||
// - If key is other than escape
|
||||
// - If key is not up or down => not a dropdown command
|
||||
// - If trigger inside the menu => not a dropdown command
|
||||
if (/input|textarea/i.test(event.target.tagName)
|
||||
? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
|
||||
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
|
||||
$(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
|
||||
return
|
||||
}
|
||||
|
||||
const parent = Dropdown._getParentFromElement(this)
|
||||
const isActive = $(parent).hasClass(ClassName.SHOW)
|
||||
|
||||
if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) ||
|
||||
isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
|
||||
if (event.which === ESCAPE_KEYCODE) {
|
||||
const toggle = parent.querySelector(Selector.DATA_TOGGLE)
|
||||
$(toggle).trigger('focus')
|
||||
}
|
||||
|
||||
$(this).trigger('click')
|
||||
return
|
||||
}
|
||||
|
||||
const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))
|
||||
|
||||
if (items.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let index = items.indexOf(event.target)
|
||||
|
||||
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
|
||||
index--
|
||||
}
|
||||
|
||||
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
|
||||
index++
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
index = 0
|
||||
}
|
||||
|
||||
items[index].focus()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document)
|
||||
.on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
|
||||
.on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)
|
||||
.on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)
|
||||
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
Dropdown._jQueryInterface.call($(this), 'toggle')
|
||||
})
|
||||
.on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Dropdown._jQueryInterface
|
||||
$.fn[NAME].Constructor = Dropdown
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Dropdown._jQueryInterface
|
||||
}
|
||||
|
||||
return Dropdown
|
||||
})($, Popper)
|
||||
|
||||
export default Dropdown
|
50
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/index.js
vendored
Normal file
50
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/index.js
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
import $ from 'jquery'
|
||||
import Alert from './alert'
|
||||
import Button from './button'
|
||||
import Carousel from './carousel'
|
||||
import Collapse from './collapse'
|
||||
import Dropdown from './dropdown'
|
||||
import Modal from './modal'
|
||||
import Popover from './popover'
|
||||
import Scrollspy from './scrollspy'
|
||||
import Tab from './tab'
|
||||
import Tooltip from './tooltip'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): index.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
(($) => {
|
||||
if (typeof $ === 'undefined') {
|
||||
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.')
|
||||
}
|
||||
|
||||
const version = $.fn.jquery.split(' ')[0].split('.')
|
||||
const minMajor = 1
|
||||
const ltMajor = 2
|
||||
const minMinor = 9
|
||||
const minPatch = 1
|
||||
const maxMajor = 4
|
||||
|
||||
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
|
||||
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0')
|
||||
}
|
||||
})($)
|
||||
|
||||
export {
|
||||
Util,
|
||||
Alert,
|
||||
Button,
|
||||
Carousel,
|
||||
Collapse,
|
||||
Dropdown,
|
||||
Modal,
|
||||
Popover,
|
||||
Scrollspy,
|
||||
Tab,
|
||||
Tooltip
|
||||
}
|
579
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/modal.js
vendored
Normal file
579
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/modal.js
vendored
Normal file
@ -0,0 +1,579 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): modal.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Modal = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'modal'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.modal'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
|
||||
|
||||
const Default = {
|
||||
backdrop : true,
|
||||
keyboard : true,
|
||||
focus : true,
|
||||
show : true
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
backdrop : '(boolean|string)',
|
||||
keyboard : 'boolean',
|
||||
focus : 'boolean',
|
||||
show : 'boolean'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
FOCUSIN : `focusin${EVENT_KEY}`,
|
||||
RESIZE : `resize${EVENT_KEY}`,
|
||||
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,
|
||||
KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,
|
||||
MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,
|
||||
MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
SCROLLBAR_MEASURER : 'modal-scrollbar-measure',
|
||||
BACKDROP : 'modal-backdrop',
|
||||
OPEN : 'modal-open',
|
||||
FADE : 'fade',
|
||||
SHOW : 'show'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DIALOG : '.modal-dialog',
|
||||
DATA_TOGGLE : '[data-toggle="modal"]',
|
||||
DATA_DISMISS : '[data-dismiss="modal"]',
|
||||
FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
|
||||
STICKY_CONTENT : '.sticky-top'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Modal {
|
||||
constructor(element, config) {
|
||||
this._config = this._getConfig(config)
|
||||
this._element = element
|
||||
this._dialog = element.querySelector(Selector.DIALOG)
|
||||
this._backdrop = null
|
||||
this._isShown = false
|
||||
this._isBodyOverflowing = false
|
||||
this._ignoreBackdropClick = false
|
||||
this._scrollbarWidth = 0
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toggle(relatedTarget) {
|
||||
return this._isShown ? this.hide() : this.show(relatedTarget)
|
||||
}
|
||||
|
||||
show(relatedTarget) {
|
||||
if (this._isTransitioning || this._isShown) {
|
||||
return
|
||||
}
|
||||
|
||||
if ($(this._element).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
const showEvent = $.Event(Event.SHOW, {
|
||||
relatedTarget
|
||||
})
|
||||
|
||||
$(this._element).trigger(showEvent)
|
||||
|
||||
if (this._isShown || showEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
this._isShown = true
|
||||
|
||||
this._checkScrollbar()
|
||||
this._setScrollbar()
|
||||
|
||||
this._adjustDialog()
|
||||
|
||||
$(document.body).addClass(ClassName.OPEN)
|
||||
|
||||
this._setEscapeEvent()
|
||||
this._setResizeEvent()
|
||||
|
||||
$(this._element).on(
|
||||
Event.CLICK_DISMISS,
|
||||
Selector.DATA_DISMISS,
|
||||
(event) => this.hide(event)
|
||||
)
|
||||
|
||||
$(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {
|
||||
$(this._element).one(Event.MOUSEUP_DISMISS, (event) => {
|
||||
if ($(event.target).is(this._element)) {
|
||||
this._ignoreBackdropClick = true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this._showBackdrop(() => this._showElement(relatedTarget))
|
||||
}
|
||||
|
||||
hide(event) {
|
||||
if (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (this._isTransitioning || !this._isShown) {
|
||||
return
|
||||
}
|
||||
|
||||
const hideEvent = $.Event(Event.HIDE)
|
||||
|
||||
$(this._element).trigger(hideEvent)
|
||||
|
||||
if (!this._isShown || hideEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
this._isShown = false
|
||||
const transition = $(this._element).hasClass(ClassName.FADE)
|
||||
|
||||
if (transition) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
this._setEscapeEvent()
|
||||
this._setResizeEvent()
|
||||
|
||||
$(document).off(Event.FOCUSIN)
|
||||
|
||||
$(this._element).removeClass(ClassName.SHOW)
|
||||
|
||||
$(this._element).off(Event.CLICK_DISMISS)
|
||||
$(this._dialog).off(Event.MOUSEDOWN_DISMISS)
|
||||
|
||||
|
||||
if (transition) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
||||
|
||||
$(this._element)
|
||||
.one(Util.TRANSITION_END, (event) => this._hideModal(event))
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
this._hideModal()
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
|
||||
$(window, document, this._element, this._backdrop).off(EVENT_KEY)
|
||||
|
||||
this._config = null
|
||||
this._element = null
|
||||
this._dialog = null
|
||||
this._backdrop = null
|
||||
this._isShown = null
|
||||
this._isBodyOverflowing = null
|
||||
this._ignoreBackdropClick = null
|
||||
this._scrollbarWidth = null
|
||||
}
|
||||
|
||||
handleUpdate() {
|
||||
this._adjustDialog()
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...config
|
||||
}
|
||||
Util.typeCheckConfig(NAME, config, DefaultType)
|
||||
return config
|
||||
}
|
||||
|
||||
_showElement(relatedTarget) {
|
||||
const transition = $(this._element).hasClass(ClassName.FADE)
|
||||
|
||||
if (!this._element.parentNode ||
|
||||
this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
|
||||
// Don't move modal's DOM position
|
||||
document.body.appendChild(this._element)
|
||||
}
|
||||
|
||||
this._element.style.display = 'block'
|
||||
this._element.removeAttribute('aria-hidden')
|
||||
this._element.scrollTop = 0
|
||||
|
||||
if (transition) {
|
||||
Util.reflow(this._element)
|
||||
}
|
||||
|
||||
$(this._element).addClass(ClassName.SHOW)
|
||||
|
||||
if (this._config.focus) {
|
||||
this._enforceFocus()
|
||||
}
|
||||
|
||||
const shownEvent = $.Event(Event.SHOWN, {
|
||||
relatedTarget
|
||||
})
|
||||
|
||||
const transitionComplete = () => {
|
||||
if (this._config.focus) {
|
||||
this._element.focus()
|
||||
}
|
||||
this._isTransitioning = false
|
||||
$(this._element).trigger(shownEvent)
|
||||
}
|
||||
|
||||
if (transition) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
||||
|
||||
$(this._dialog)
|
||||
.one(Util.TRANSITION_END, transitionComplete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
transitionComplete()
|
||||
}
|
||||
}
|
||||
|
||||
_enforceFocus() {
|
||||
$(document)
|
||||
.off(Event.FOCUSIN) // Guard against infinite focus loop
|
||||
.on(Event.FOCUSIN, (event) => {
|
||||
if (document !== event.target &&
|
||||
this._element !== event.target &&
|
||||
$(this._element).has(event.target).length === 0) {
|
||||
this._element.focus()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_setEscapeEvent() {
|
||||
if (this._isShown && this._config.keyboard) {
|
||||
$(this._element).on(Event.KEYDOWN_DISMISS, (event) => {
|
||||
if (event.which === ESCAPE_KEYCODE) {
|
||||
event.preventDefault()
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
} else if (!this._isShown) {
|
||||
$(this._element).off(Event.KEYDOWN_DISMISS)
|
||||
}
|
||||
}
|
||||
|
||||
_setResizeEvent() {
|
||||
if (this._isShown) {
|
||||
$(window).on(Event.RESIZE, (event) => this.handleUpdate(event))
|
||||
} else {
|
||||
$(window).off(Event.RESIZE)
|
||||
}
|
||||
}
|
||||
|
||||
_hideModal() {
|
||||
this._element.style.display = 'none'
|
||||
this._element.setAttribute('aria-hidden', true)
|
||||
this._isTransitioning = false
|
||||
this._showBackdrop(() => {
|
||||
$(document.body).removeClass(ClassName.OPEN)
|
||||
this._resetAdjustments()
|
||||
this._resetScrollbar()
|
||||
$(this._element).trigger(Event.HIDDEN)
|
||||
})
|
||||
}
|
||||
|
||||
_removeBackdrop() {
|
||||
if (this._backdrop) {
|
||||
$(this._backdrop).remove()
|
||||
this._backdrop = null
|
||||
}
|
||||
}
|
||||
|
||||
_showBackdrop(callback) {
|
||||
const animate = $(this._element).hasClass(ClassName.FADE)
|
||||
? ClassName.FADE : ''
|
||||
|
||||
if (this._isShown && this._config.backdrop) {
|
||||
this._backdrop = document.createElement('div')
|
||||
this._backdrop.className = ClassName.BACKDROP
|
||||
|
||||
if (animate) {
|
||||
this._backdrop.classList.add(animate)
|
||||
}
|
||||
|
||||
$(this._backdrop).appendTo(document.body)
|
||||
|
||||
$(this._element).on(Event.CLICK_DISMISS, (event) => {
|
||||
if (this._ignoreBackdropClick) {
|
||||
this._ignoreBackdropClick = false
|
||||
return
|
||||
}
|
||||
if (event.target !== event.currentTarget) {
|
||||
return
|
||||
}
|
||||
if (this._config.backdrop === 'static') {
|
||||
this._element.focus()
|
||||
} else {
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
|
||||
if (animate) {
|
||||
Util.reflow(this._backdrop)
|
||||
}
|
||||
|
||||
$(this._backdrop).addClass(ClassName.SHOW)
|
||||
|
||||
if (!callback) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!animate) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
|
||||
|
||||
$(this._backdrop)
|
||||
.one(Util.TRANSITION_END, callback)
|
||||
.emulateTransitionEnd(backdropTransitionDuration)
|
||||
} else if (!this._isShown && this._backdrop) {
|
||||
$(this._backdrop).removeClass(ClassName.SHOW)
|
||||
|
||||
const callbackRemove = () => {
|
||||
this._removeBackdrop()
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
if ($(this._element).hasClass(ClassName.FADE)) {
|
||||
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
|
||||
|
||||
$(this._backdrop)
|
||||
.one(Util.TRANSITION_END, callbackRemove)
|
||||
.emulateTransitionEnd(backdropTransitionDuration)
|
||||
} else {
|
||||
callbackRemove()
|
||||
}
|
||||
} else if (callback) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// the following methods are used to handle overflowing modals
|
||||
// todo (fat): these should probably be refactored out of modal.js
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
_adjustDialog() {
|
||||
const isModalOverflowing =
|
||||
this._element.scrollHeight > document.documentElement.clientHeight
|
||||
|
||||
if (!this._isBodyOverflowing && isModalOverflowing) {
|
||||
this._element.style.paddingLeft = `${this._scrollbarWidth}px`
|
||||
}
|
||||
|
||||
if (this._isBodyOverflowing && !isModalOverflowing) {
|
||||
this._element.style.paddingRight = `${this._scrollbarWidth}px`
|
||||
}
|
||||
}
|
||||
|
||||
_resetAdjustments() {
|
||||
this._element.style.paddingLeft = ''
|
||||
this._element.style.paddingRight = ''
|
||||
}
|
||||
|
||||
_checkScrollbar() {
|
||||
const rect = document.body.getBoundingClientRect()
|
||||
this._isBodyOverflowing = rect.left + rect.right < window.innerWidth
|
||||
this._scrollbarWidth = this._getScrollbarWidth()
|
||||
}
|
||||
|
||||
_setScrollbar() {
|
||||
if (this._isBodyOverflowing) {
|
||||
// Note: DOMNode.style.paddingRight returns the actual value or '' if not set
|
||||
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
|
||||
const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))
|
||||
const stickyContent = [].slice.call(document.querySelectorAll(Selector.STICKY_CONTENT))
|
||||
|
||||
// Adjust fixed content padding
|
||||
$(fixedContent).each((index, element) => {
|
||||
const actualPadding = element.style.paddingRight
|
||||
const calculatedPadding = $(element).css('padding-right')
|
||||
$(element)
|
||||
.data('padding-right', actualPadding)
|
||||
.css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
|
||||
})
|
||||
|
||||
// Adjust sticky content margin
|
||||
$(stickyContent).each((index, element) => {
|
||||
const actualMargin = element.style.marginRight
|
||||
const calculatedMargin = $(element).css('margin-right')
|
||||
$(element)
|
||||
.data('margin-right', actualMargin)
|
||||
.css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)
|
||||
})
|
||||
|
||||
// Adjust body padding
|
||||
const actualPadding = document.body.style.paddingRight
|
||||
const calculatedPadding = $(document.body).css('padding-right')
|
||||
$(document.body)
|
||||
.data('padding-right', actualPadding)
|
||||
.css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
|
||||
}
|
||||
}
|
||||
|
||||
_resetScrollbar() {
|
||||
// Restore fixed content padding
|
||||
const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))
|
||||
$(fixedContent).each((index, element) => {
|
||||
const padding = $(element).data('padding-right')
|
||||
$(element).removeData('padding-right')
|
||||
element.style.paddingRight = padding ? padding : ''
|
||||
})
|
||||
|
||||
// Restore sticky content
|
||||
const elements = [].slice.call(document.querySelectorAll(`${Selector.STICKY_CONTENT}`))
|
||||
$(elements).each((index, element) => {
|
||||
const margin = $(element).data('margin-right')
|
||||
if (typeof margin !== 'undefined') {
|
||||
$(element).css('margin-right', margin).removeData('margin-right')
|
||||
}
|
||||
})
|
||||
|
||||
// Restore body padding
|
||||
const padding = $(document.body).data('padding-right')
|
||||
$(document.body).removeData('padding-right')
|
||||
document.body.style.paddingRight = padding ? padding : ''
|
||||
}
|
||||
|
||||
_getScrollbarWidth() { // thx d.walsh
|
||||
const scrollDiv = document.createElement('div')
|
||||
scrollDiv.className = ClassName.SCROLLBAR_MEASURER
|
||||
document.body.appendChild(scrollDiv)
|
||||
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
|
||||
document.body.removeChild(scrollDiv)
|
||||
return scrollbarWidth
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config, relatedTarget) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
const _config = {
|
||||
...Default,
|
||||
...$(this).data(),
|
||||
...typeof config === 'object' && config ? config : {}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = new Modal(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config](relatedTarget)
|
||||
} else if (_config.show) {
|
||||
data.show(relatedTarget)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
|
||||
let target
|
||||
const selector = Util.getSelectorFromElement(this)
|
||||
|
||||
if (selector) {
|
||||
target = document.querySelector(selector)
|
||||
}
|
||||
|
||||
const config = $(target).data(DATA_KEY)
|
||||
? 'toggle' : {
|
||||
...$(target).data(),
|
||||
...$(this).data()
|
||||
}
|
||||
|
||||
if (this.tagName === 'A' || this.tagName === 'AREA') {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
const $target = $(target).one(Event.SHOW, (showEvent) => {
|
||||
if (showEvent.isDefaultPrevented()) {
|
||||
// Only register focus restorer if modal will actually get shown
|
||||
return
|
||||
}
|
||||
|
||||
$target.one(Event.HIDDEN, () => {
|
||||
if ($(this).is(':visible')) {
|
||||
this.focus()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Modal._jQueryInterface.call($(target), config, this)
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Modal._jQueryInterface
|
||||
$.fn[NAME].Constructor = Modal
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Modal._jQueryInterface
|
||||
}
|
||||
|
||||
return Modal
|
||||
})($)
|
||||
|
||||
export default Modal
|
188
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/popover.js
vendored
Normal file
188
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/popover.js
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
import $ from 'jquery'
|
||||
import Tooltip from './tooltip'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): popover.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Popover = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'popover'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.popover'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const CLASS_PREFIX = 'bs-popover'
|
||||
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
||||
|
||||
const Default = {
|
||||
...Tooltip.Default,
|
||||
placement : 'right',
|
||||
trigger : 'click',
|
||||
content : '',
|
||||
template : '<div class="popover" role="tooltip">' +
|
||||
'<div class="arrow"></div>' +
|
||||
'<h3 class="popover-header"></h3>' +
|
||||
'<div class="popover-body"></div></div>'
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
...Tooltip.DefaultType,
|
||||
content : '(string|element|function)'
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
FADE : 'fade',
|
||||
SHOW : 'show'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
TITLE : '.popover-header',
|
||||
CONTENT : '.popover-body'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
INSERTED : `inserted${EVENT_KEY}`,
|
||||
CLICK : `click${EVENT_KEY}`,
|
||||
FOCUSIN : `focusin${EVENT_KEY}`,
|
||||
FOCUSOUT : `focusout${EVENT_KEY}`,
|
||||
MOUSEENTER : `mouseenter${EVENT_KEY}`,
|
||||
MOUSELEAVE : `mouseleave${EVENT_KEY}`
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Popover extends Tooltip {
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
static get NAME() {
|
||||
return NAME
|
||||
}
|
||||
|
||||
static get DATA_KEY() {
|
||||
return DATA_KEY
|
||||
}
|
||||
|
||||
static get Event() {
|
||||
return Event
|
||||
}
|
||||
|
||||
static get EVENT_KEY() {
|
||||
return EVENT_KEY
|
||||
}
|
||||
|
||||
static get DefaultType() {
|
||||
return DefaultType
|
||||
}
|
||||
|
||||
// Overrides
|
||||
|
||||
isWithContent() {
|
||||
return this.getTitle() || this._getContent()
|
||||
}
|
||||
|
||||
addAttachmentClass(attachment) {
|
||||
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
|
||||
}
|
||||
|
||||
getTipElement() {
|
||||
this.tip = this.tip || $(this.config.template)[0]
|
||||
return this.tip
|
||||
}
|
||||
|
||||
setContent() {
|
||||
const $tip = $(this.getTipElement())
|
||||
|
||||
// We use append for html objects to maintain js events
|
||||
this.setElementContent($tip.find(Selector.TITLE), this.getTitle())
|
||||
let content = this._getContent()
|
||||
if (typeof content === 'function') {
|
||||
content = content.call(this.element)
|
||||
}
|
||||
this.setElementContent($tip.find(Selector.CONTENT), content)
|
||||
|
||||
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getContent() {
|
||||
return this.element.getAttribute('data-content') ||
|
||||
this.config.content
|
||||
}
|
||||
|
||||
_cleanTipClass() {
|
||||
const $tip = $(this.getTipElement())
|
||||
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
|
||||
if (tabClass !== null && tabClass.length > 0) {
|
||||
$tip.removeClass(tabClass.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
const _config = typeof config === 'object' ? config : null
|
||||
|
||||
if (!data && /destroy|hide/.test(config)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = new Popover(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Popover._jQueryInterface
|
||||
$.fn[NAME].Constructor = Popover
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Popover._jQueryInterface
|
||||
}
|
||||
|
||||
return Popover
|
||||
})($)
|
||||
|
||||
export default Popover
|
332
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/scrollspy.js
vendored
Normal file
332
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/scrollspy.js
vendored
Normal file
@ -0,0 +1,332 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): scrollspy.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const ScrollSpy = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'scrollspy'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.scrollspy'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Default = {
|
||||
offset : 10,
|
||||
method : 'auto',
|
||||
target : ''
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
offset : 'number',
|
||||
method : 'string',
|
||||
target : '(string|element)'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
ACTIVATE : `activate${EVENT_KEY}`,
|
||||
SCROLL : `scroll${EVENT_KEY}`,
|
||||
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
DROPDOWN_ITEM : 'dropdown-item',
|
||||
DROPDOWN_MENU : 'dropdown-menu',
|
||||
ACTIVE : 'active'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DATA_SPY : '[data-spy="scroll"]',
|
||||
ACTIVE : '.active',
|
||||
NAV_LIST_GROUP : '.nav, .list-group',
|
||||
NAV_LINKS : '.nav-link',
|
||||
NAV_ITEMS : '.nav-item',
|
||||
LIST_ITEMS : '.list-group-item',
|
||||
DROPDOWN : '.dropdown',
|
||||
DROPDOWN_ITEMS : '.dropdown-item',
|
||||
DROPDOWN_TOGGLE : '.dropdown-toggle'
|
||||
}
|
||||
|
||||
const OffsetMethod = {
|
||||
OFFSET : 'offset',
|
||||
POSITION : 'position'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ScrollSpy {
|
||||
constructor(element, config) {
|
||||
this._element = element
|
||||
this._scrollElement = element.tagName === 'BODY' ? window : element
|
||||
this._config = this._getConfig(config)
|
||||
this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +
|
||||
`${this._config.target} ${Selector.LIST_ITEMS},` +
|
||||
`${this._config.target} ${Selector.DROPDOWN_ITEMS}`
|
||||
this._offsets = []
|
||||
this._targets = []
|
||||
this._activeTarget = null
|
||||
this._scrollHeight = 0
|
||||
|
||||
$(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))
|
||||
|
||||
this.refresh()
|
||||
this._process()
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
refresh() {
|
||||
const autoMethod = this._scrollElement === this._scrollElement.window
|
||||
? OffsetMethod.OFFSET : OffsetMethod.POSITION
|
||||
|
||||
const offsetMethod = this._config.method === 'auto'
|
||||
? autoMethod : this._config.method
|
||||
|
||||
const offsetBase = offsetMethod === OffsetMethod.POSITION
|
||||
? this._getScrollTop() : 0
|
||||
|
||||
this._offsets = []
|
||||
this._targets = []
|
||||
|
||||
this._scrollHeight = this._getScrollHeight()
|
||||
|
||||
const targets = [].slice.call(document.querySelectorAll(this._selector))
|
||||
|
||||
targets
|
||||
.map((element) => {
|
||||
let target
|
||||
const targetSelector = Util.getSelectorFromElement(element)
|
||||
|
||||
if (targetSelector) {
|
||||
target = document.querySelector(targetSelector)
|
||||
}
|
||||
|
||||
if (target) {
|
||||
const targetBCR = target.getBoundingClientRect()
|
||||
if (targetBCR.width || targetBCR.height) {
|
||||
// TODO (fat): remove sketch reliance on jQuery position/offset
|
||||
return [
|
||||
$(target)[offsetMethod]().top + offsetBase,
|
||||
targetSelector
|
||||
]
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
.filter((item) => item)
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.forEach((item) => {
|
||||
this._offsets.push(item[0])
|
||||
this._targets.push(item[1])
|
||||
})
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
$(this._scrollElement).off(EVENT_KEY)
|
||||
|
||||
this._element = null
|
||||
this._scrollElement = null
|
||||
this._config = null
|
||||
this._selector = null
|
||||
this._offsets = null
|
||||
this._targets = null
|
||||
this._activeTarget = null
|
||||
this._scrollHeight = null
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...typeof config === 'object' && config ? config : {}
|
||||
}
|
||||
|
||||
if (typeof config.target !== 'string') {
|
||||
let id = $(config.target).attr('id')
|
||||
if (!id) {
|
||||
id = Util.getUID(NAME)
|
||||
$(config.target).attr('id', id)
|
||||
}
|
||||
config.target = `#${id}`
|
||||
}
|
||||
|
||||
Util.typeCheckConfig(NAME, config, DefaultType)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
_getScrollTop() {
|
||||
return this._scrollElement === window
|
||||
? this._scrollElement.pageYOffset : this._scrollElement.scrollTop
|
||||
}
|
||||
|
||||
_getScrollHeight() {
|
||||
return this._scrollElement.scrollHeight || Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight
|
||||
)
|
||||
}
|
||||
|
||||
_getOffsetHeight() {
|
||||
return this._scrollElement === window
|
||||
? window.innerHeight : this._scrollElement.getBoundingClientRect().height
|
||||
}
|
||||
|
||||
_process() {
|
||||
const scrollTop = this._getScrollTop() + this._config.offset
|
||||
const scrollHeight = this._getScrollHeight()
|
||||
const maxScroll = this._config.offset +
|
||||
scrollHeight -
|
||||
this._getOffsetHeight()
|
||||
|
||||
if (this._scrollHeight !== scrollHeight) {
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
if (scrollTop >= maxScroll) {
|
||||
const target = this._targets[this._targets.length - 1]
|
||||
|
||||
if (this._activeTarget !== target) {
|
||||
this._activate(target)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
|
||||
this._activeTarget = null
|
||||
this._clear()
|
||||
return
|
||||
}
|
||||
|
||||
const offsetLength = this._offsets.length
|
||||
for (let i = offsetLength; i--;) {
|
||||
const isActiveTarget = this._activeTarget !== this._targets[i] &&
|
||||
scrollTop >= this._offsets[i] &&
|
||||
(typeof this._offsets[i + 1] === 'undefined' ||
|
||||
scrollTop < this._offsets[i + 1])
|
||||
|
||||
if (isActiveTarget) {
|
||||
this._activate(this._targets[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_activate(target) {
|
||||
this._activeTarget = target
|
||||
|
||||
this._clear()
|
||||
|
||||
let queries = this._selector.split(',')
|
||||
// eslint-disable-next-line arrow-body-style
|
||||
queries = queries.map((selector) => {
|
||||
return `${selector}[data-target="${target}"],` +
|
||||
`${selector}[href="${target}"]`
|
||||
})
|
||||
|
||||
const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))
|
||||
|
||||
if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
|
||||
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
|
||||
$link.addClass(ClassName.ACTIVE)
|
||||
} else {
|
||||
// Set triggered link as active
|
||||
$link.addClass(ClassName.ACTIVE)
|
||||
// Set triggered links parents as active
|
||||
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
|
||||
$link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
|
||||
// Handle special case when .nav-link is inside .nav-item
|
||||
$link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE)
|
||||
}
|
||||
|
||||
$(this._scrollElement).trigger(Event.ACTIVATE, {
|
||||
relatedTarget: target
|
||||
})
|
||||
}
|
||||
|
||||
_clear() {
|
||||
const nodes = [].slice.call(document.querySelectorAll(this._selector))
|
||||
$(nodes).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
const _config = typeof config === 'object' && config
|
||||
|
||||
if (!data) {
|
||||
data = new ScrollSpy(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(window).on(Event.LOAD_DATA_API, () => {
|
||||
const scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY))
|
||||
|
||||
const scrollSpysLength = scrollSpys.length
|
||||
for (let i = scrollSpysLength; i--;) {
|
||||
const $spy = $(scrollSpys[i])
|
||||
ScrollSpy._jQueryInterface.call($spy, $spy.data())
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = ScrollSpy._jQueryInterface
|
||||
$.fn[NAME].Constructor = ScrollSpy
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return ScrollSpy._jQueryInterface
|
||||
}
|
||||
|
||||
return ScrollSpy
|
||||
})($)
|
||||
|
||||
export default ScrollSpy
|
264
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/tab.js
vendored
Normal file
264
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/tab.js
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
import $ from 'jquery'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): tab.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Tab = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'tab'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.tab'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Event = {
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
DROPDOWN_MENU : 'dropdown-menu',
|
||||
ACTIVE : 'active',
|
||||
DISABLED : 'disabled',
|
||||
FADE : 'fade',
|
||||
SHOW : 'show'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DROPDOWN : '.dropdown',
|
||||
NAV_LIST_GROUP : '.nav, .list-group',
|
||||
ACTIVE : '.active',
|
||||
ACTIVE_UL : '> li > .active',
|
||||
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
|
||||
DROPDOWN_TOGGLE : '.dropdown-toggle',
|
||||
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Tab {
|
||||
constructor(element) {
|
||||
this._element = element
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
show() {
|
||||
if (this._element.parentNode &&
|
||||
this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
|
||||
$(this._element).hasClass(ClassName.ACTIVE) ||
|
||||
$(this._element).hasClass(ClassName.DISABLED)) {
|
||||
return
|
||||
}
|
||||
|
||||
let target
|
||||
let previous
|
||||
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
|
||||
const selector = Util.getSelectorFromElement(this._element)
|
||||
|
||||
if (listElement) {
|
||||
const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
|
||||
previous = $.makeArray($(listElement).find(itemSelector))
|
||||
previous = previous[previous.length - 1]
|
||||
}
|
||||
|
||||
const hideEvent = $.Event(Event.HIDE, {
|
||||
relatedTarget: this._element
|
||||
})
|
||||
|
||||
const showEvent = $.Event(Event.SHOW, {
|
||||
relatedTarget: previous
|
||||
})
|
||||
|
||||
if (previous) {
|
||||
$(previous).trigger(hideEvent)
|
||||
}
|
||||
|
||||
$(this._element).trigger(showEvent)
|
||||
|
||||
if (showEvent.isDefaultPrevented() ||
|
||||
hideEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selector) {
|
||||
target = document.querySelector(selector)
|
||||
}
|
||||
|
||||
this._activate(
|
||||
this._element,
|
||||
listElement
|
||||
)
|
||||
|
||||
const complete = () => {
|
||||
const hiddenEvent = $.Event(Event.HIDDEN, {
|
||||
relatedTarget: this._element
|
||||
})
|
||||
|
||||
const shownEvent = $.Event(Event.SHOWN, {
|
||||
relatedTarget: previous
|
||||
})
|
||||
|
||||
$(previous).trigger(hiddenEvent)
|
||||
$(this._element).trigger(shownEvent)
|
||||
}
|
||||
|
||||
if (target) {
|
||||
this._activate(target, target.parentNode, complete)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this._element, DATA_KEY)
|
||||
this._element = null
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_activate(element, container, callback) {
|
||||
let activeElements
|
||||
if (container.nodeName === 'UL') {
|
||||
activeElements = $(container).find(Selector.ACTIVE_UL)
|
||||
} else {
|
||||
activeElements = $(container).children(Selector.ACTIVE)
|
||||
}
|
||||
|
||||
const active = activeElements[0]
|
||||
const isTransitioning = callback &&
|
||||
(active && $(active).hasClass(ClassName.FADE))
|
||||
|
||||
const complete = () => this._transitionComplete(
|
||||
element,
|
||||
active,
|
||||
callback
|
||||
)
|
||||
|
||||
if (active && isTransitioning) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(active)
|
||||
|
||||
$(active)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
}
|
||||
|
||||
_transitionComplete(element, active, callback) {
|
||||
if (active) {
|
||||
$(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
|
||||
|
||||
const dropdownChild = $(active.parentNode).find(
|
||||
Selector.DROPDOWN_ACTIVE_CHILD
|
||||
)[0]
|
||||
|
||||
if (dropdownChild) {
|
||||
$(dropdownChild).removeClass(ClassName.ACTIVE)
|
||||
}
|
||||
|
||||
if (active.getAttribute('role') === 'tab') {
|
||||
active.setAttribute('aria-selected', false)
|
||||
}
|
||||
}
|
||||
|
||||
$(element).addClass(ClassName.ACTIVE)
|
||||
if (element.getAttribute('role') === 'tab') {
|
||||
element.setAttribute('aria-selected', true)
|
||||
}
|
||||
|
||||
Util.reflow(element)
|
||||
$(element).addClass(ClassName.SHOW)
|
||||
|
||||
if (element.parentNode &&
|
||||
$(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
|
||||
const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
|
||||
if (dropdownElement) {
|
||||
const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector.DROPDOWN_TOGGLE))
|
||||
$(dropdownToggleList).addClass(ClassName.ACTIVE)
|
||||
}
|
||||
|
||||
element.setAttribute('aria-expanded', true)
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
const $this = $(this)
|
||||
let data = $this.data(DATA_KEY)
|
||||
|
||||
if (!data) {
|
||||
data = new Tab(this)
|
||||
$this.data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$(document)
|
||||
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
|
||||
event.preventDefault()
|
||||
Tab._jQueryInterface.call($(this), 'show')
|
||||
})
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Tab._jQueryInterface
|
||||
$.fn[NAME].Constructor = Tab
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Tab._jQueryInterface
|
||||
}
|
||||
|
||||
return Tab
|
||||
})($)
|
||||
|
||||
export default Tab
|
725
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/tooltip.js
vendored
Normal file
725
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/tooltip.js
vendored
Normal file
@ -0,0 +1,725 @@
|
||||
import $ from 'jquery'
|
||||
import Popper from 'popper.js'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): tooltip.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Tooltip = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const NAME = 'tooltip'
|
||||
const VERSION = '4.1.3'
|
||||
const DATA_KEY = 'bs.tooltip'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const CLASS_PREFIX = 'bs-tooltip'
|
||||
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
||||
|
||||
const DefaultType = {
|
||||
animation : 'boolean',
|
||||
template : 'string',
|
||||
title : '(string|element|function)',
|
||||
trigger : 'string',
|
||||
delay : '(number|object)',
|
||||
html : 'boolean',
|
||||
selector : '(string|boolean)',
|
||||
placement : '(string|function)',
|
||||
offset : '(number|string)',
|
||||
container : '(string|element|boolean)',
|
||||
fallbackPlacement : '(string|array)',
|
||||
boundary : '(string|element)'
|
||||
}
|
||||
|
||||
const AttachmentMap = {
|
||||
AUTO : 'auto',
|
||||
TOP : 'top',
|
||||
RIGHT : 'right',
|
||||
BOTTOM : 'bottom',
|
||||
LEFT : 'left'
|
||||
}
|
||||
|
||||
const Default = {
|
||||
animation : true,
|
||||
template : '<div class="tooltip" role="tooltip">' +
|
||||
'<div class="arrow"></div>' +
|
||||
'<div class="tooltip-inner"></div></div>',
|
||||
trigger : 'hover focus',
|
||||
title : '',
|
||||
delay : 0,
|
||||
html : false,
|
||||
selector : false,
|
||||
placement : 'top',
|
||||
offset : 0,
|
||||
container : false,
|
||||
fallbackPlacement : 'flip',
|
||||
boundary : 'scrollParent'
|
||||
}
|
||||
|
||||
const HoverState = {
|
||||
SHOW : 'show',
|
||||
OUT : 'out'
|
||||
}
|
||||
|
||||
const Event = {
|
||||
HIDE : `hide${EVENT_KEY}`,
|
||||
HIDDEN : `hidden${EVENT_KEY}`,
|
||||
SHOW : `show${EVENT_KEY}`,
|
||||
SHOWN : `shown${EVENT_KEY}`,
|
||||
INSERTED : `inserted${EVENT_KEY}`,
|
||||
CLICK : `click${EVENT_KEY}`,
|
||||
FOCUSIN : `focusin${EVENT_KEY}`,
|
||||
FOCUSOUT : `focusout${EVENT_KEY}`,
|
||||
MOUSEENTER : `mouseenter${EVENT_KEY}`,
|
||||
MOUSELEAVE : `mouseleave${EVENT_KEY}`
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
FADE : 'fade',
|
||||
SHOW : 'show'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
TOOLTIP : '.tooltip',
|
||||
TOOLTIP_INNER : '.tooltip-inner',
|
||||
ARROW : '.arrow'
|
||||
}
|
||||
|
||||
const Trigger = {
|
||||
HOVER : 'hover',
|
||||
FOCUS : 'focus',
|
||||
CLICK : 'click',
|
||||
MANUAL : 'manual'
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class Tooltip {
|
||||
constructor(element, config) {
|
||||
/**
|
||||
* Check for Popper dependency
|
||||
* Popper - https://popper.js.org
|
||||
*/
|
||||
if (typeof Popper === 'undefined') {
|
||||
throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)')
|
||||
}
|
||||
|
||||
// private
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._activeTrigger = {}
|
||||
this._popper = null
|
||||
|
||||
// Protected
|
||||
this.element = element
|
||||
this.config = this._getConfig(config)
|
||||
this.tip = null
|
||||
|
||||
this._setListeners()
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
static get VERSION() {
|
||||
return VERSION
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
static get NAME() {
|
||||
return NAME
|
||||
}
|
||||
|
||||
static get DATA_KEY() {
|
||||
return DATA_KEY
|
||||
}
|
||||
|
||||
static get Event() {
|
||||
return Event
|
||||
}
|
||||
|
||||
static get EVENT_KEY() {
|
||||
return EVENT_KEY
|
||||
}
|
||||
|
||||
static get DefaultType() {
|
||||
return DefaultType
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
enable() {
|
||||
this._isEnabled = true
|
||||
}
|
||||
|
||||
disable() {
|
||||
this._isEnabled = false
|
||||
}
|
||||
|
||||
toggleEnabled() {
|
||||
this._isEnabled = !this._isEnabled
|
||||
}
|
||||
|
||||
toggle(event) {
|
||||
if (!this._isEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
let context = $(event.currentTarget).data(dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
}
|
||||
|
||||
context._activeTrigger.click = !context._activeTrigger.click
|
||||
|
||||
if (context._isWithActiveTrigger()) {
|
||||
context._enter(null, context)
|
||||
} else {
|
||||
context._leave(null, context)
|
||||
}
|
||||
} else {
|
||||
if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {
|
||||
this._leave(null, this)
|
||||
return
|
||||
}
|
||||
|
||||
this._enter(null, this)
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
clearTimeout(this._timeout)
|
||||
|
||||
$.removeData(this.element, this.constructor.DATA_KEY)
|
||||
|
||||
$(this.element).off(this.constructor.EVENT_KEY)
|
||||
$(this.element).closest('.modal').off('hide.bs.modal')
|
||||
|
||||
if (this.tip) {
|
||||
$(this.tip).remove()
|
||||
}
|
||||
|
||||
this._isEnabled = null
|
||||
this._timeout = null
|
||||
this._hoverState = null
|
||||
this._activeTrigger = null
|
||||
if (this._popper !== null) {
|
||||
this._popper.destroy()
|
||||
}
|
||||
|
||||
this._popper = null
|
||||
this.element = null
|
||||
this.config = null
|
||||
this.tip = null
|
||||
}
|
||||
|
||||
show() {
|
||||
if ($(this.element).css('display') === 'none') {
|
||||
throw new Error('Please use show on visible elements')
|
||||
}
|
||||
|
||||
const showEvent = $.Event(this.constructor.Event.SHOW)
|
||||
if (this.isWithContent() && this._isEnabled) {
|
||||
$(this.element).trigger(showEvent)
|
||||
|
||||
const isInTheDom = $.contains(
|
||||
this.element.ownerDocument.documentElement,
|
||||
this.element
|
||||
)
|
||||
|
||||
if (showEvent.isDefaultPrevented() || !isInTheDom) {
|
||||
return
|
||||
}
|
||||
|
||||
const tip = this.getTipElement()
|
||||
const tipId = Util.getUID(this.constructor.NAME)
|
||||
|
||||
tip.setAttribute('id', tipId)
|
||||
this.element.setAttribute('aria-describedby', tipId)
|
||||
|
||||
this.setContent()
|
||||
|
||||
if (this.config.animation) {
|
||||
$(tip).addClass(ClassName.FADE)
|
||||
}
|
||||
|
||||
const placement = typeof this.config.placement === 'function'
|
||||
? this.config.placement.call(this, tip, this.element)
|
||||
: this.config.placement
|
||||
|
||||
const attachment = this._getAttachment(placement)
|
||||
this.addAttachmentClass(attachment)
|
||||
|
||||
const container = this.config.container === false ? document.body : $(document).find(this.config.container)
|
||||
|
||||
$(tip).data(this.constructor.DATA_KEY, this)
|
||||
|
||||
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
|
||||
$(tip).appendTo(container)
|
||||
}
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.INSERTED)
|
||||
|
||||
this._popper = new Popper(this.element, tip, {
|
||||
placement: attachment,
|
||||
modifiers: {
|
||||
offset: {
|
||||
offset: this.config.offset
|
||||
},
|
||||
flip: {
|
||||
behavior: this.config.fallbackPlacement
|
||||
},
|
||||
arrow: {
|
||||
element: Selector.ARROW
|
||||
},
|
||||
preventOverflow: {
|
||||
boundariesElement: this.config.boundary
|
||||
}
|
||||
},
|
||||
onCreate: (data) => {
|
||||
if (data.originalPlacement !== data.placement) {
|
||||
this._handlePopperPlacementChange(data)
|
||||
}
|
||||
},
|
||||
onUpdate: (data) => {
|
||||
this._handlePopperPlacementChange(data)
|
||||
}
|
||||
})
|
||||
|
||||
$(tip).addClass(ClassName.SHOW)
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
$(document.body).children().on('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
const complete = () => {
|
||||
if (this.config.animation) {
|
||||
this._fixTransition()
|
||||
}
|
||||
const prevHoverState = this._hoverState
|
||||
this._hoverState = null
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.SHOWN)
|
||||
|
||||
if (prevHoverState === HoverState.OUT) {
|
||||
this._leave(null, this)
|
||||
}
|
||||
}
|
||||
|
||||
if ($(this.tip).hasClass(ClassName.FADE)) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
|
||||
|
||||
$(this.tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hide(callback) {
|
||||
const tip = this.getTipElement()
|
||||
const hideEvent = $.Event(this.constructor.Event.HIDE)
|
||||
const complete = () => {
|
||||
if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
|
||||
tip.parentNode.removeChild(tip)
|
||||
}
|
||||
|
||||
this._cleanTipClass()
|
||||
this.element.removeAttribute('aria-describedby')
|
||||
$(this.element).trigger(this.constructor.Event.HIDDEN)
|
||||
if (this._popper !== null) {
|
||||
this._popper.destroy()
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
$(this.element).trigger(hideEvent)
|
||||
|
||||
if (hideEvent.isDefaultPrevented()) {
|
||||
return
|
||||
}
|
||||
|
||||
$(tip).removeClass(ClassName.SHOW)
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
$(document.body).children().off('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
this._activeTrigger[Trigger.CLICK] = false
|
||||
this._activeTrigger[Trigger.FOCUS] = false
|
||||
this._activeTrigger[Trigger.HOVER] = false
|
||||
|
||||
if ($(this.tip).hasClass(ClassName.FADE)) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(tip)
|
||||
|
||||
$(tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
|
||||
this._hoverState = ''
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this._popper !== null) {
|
||||
this._popper.scheduleUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
isWithContent() {
|
||||
return Boolean(this.getTitle())
|
||||
}
|
||||
|
||||
addAttachmentClass(attachment) {
|
||||
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
|
||||
}
|
||||
|
||||
getTipElement() {
|
||||
this.tip = this.tip || $(this.config.template)[0]
|
||||
return this.tip
|
||||
}
|
||||
|
||||
setContent() {
|
||||
const tip = this.getTipElement()
|
||||
this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())
|
||||
$(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
|
||||
}
|
||||
|
||||
setElementContent($element, content) {
|
||||
const html = this.config.html
|
||||
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
|
||||
// Content is a DOM node or a jQuery
|
||||
if (html) {
|
||||
if (!$(content).parent().is($element)) {
|
||||
$element.empty().append(content)
|
||||
}
|
||||
} else {
|
||||
$element.text($(content).text())
|
||||
}
|
||||
} else {
|
||||
$element[html ? 'html' : 'text'](content)
|
||||
}
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
let title = this.element.getAttribute('data-original-title')
|
||||
|
||||
if (!title) {
|
||||
title = typeof this.config.title === 'function'
|
||||
? this.config.title.call(this.element)
|
||||
: this.config.title
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getAttachment(placement) {
|
||||
return AttachmentMap[placement.toUpperCase()]
|
||||
}
|
||||
|
||||
_setListeners() {
|
||||
const triggers = this.config.trigger.split(' ')
|
||||
|
||||
triggers.forEach((trigger) => {
|
||||
if (trigger === 'click') {
|
||||
$(this.element).on(
|
||||
this.constructor.Event.CLICK,
|
||||
this.config.selector,
|
||||
(event) => this.toggle(event)
|
||||
)
|
||||
} else if (trigger !== Trigger.MANUAL) {
|
||||
const eventIn = trigger === Trigger.HOVER
|
||||
? this.constructor.Event.MOUSEENTER
|
||||
: this.constructor.Event.FOCUSIN
|
||||
const eventOut = trigger === Trigger.HOVER
|
||||
? this.constructor.Event.MOUSELEAVE
|
||||
: this.constructor.Event.FOCUSOUT
|
||||
|
||||
$(this.element)
|
||||
.on(
|
||||
eventIn,
|
||||
this.config.selector,
|
||||
(event) => this._enter(event)
|
||||
)
|
||||
.on(
|
||||
eventOut,
|
||||
this.config.selector,
|
||||
(event) => this._leave(event)
|
||||
)
|
||||
}
|
||||
|
||||
$(this.element).closest('.modal').on(
|
||||
'hide.bs.modal',
|
||||
() => this.hide()
|
||||
)
|
||||
})
|
||||
|
||||
if (this.config.selector) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
trigger: 'manual',
|
||||
selector: ''
|
||||
}
|
||||
} else {
|
||||
this._fixTitle()
|
||||
}
|
||||
}
|
||||
|
||||
_fixTitle() {
|
||||
const titleType = typeof this.element.getAttribute('data-original-title')
|
||||
if (this.element.getAttribute('title') ||
|
||||
titleType !== 'string') {
|
||||
this.element.setAttribute(
|
||||
'data-original-title',
|
||||
this.element.getAttribute('title') || ''
|
||||
)
|
||||
this.element.setAttribute('title', '')
|
||||
}
|
||||
}
|
||||
|
||||
_enter(event, context) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
|
||||
context = context || $(event.currentTarget).data(dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
}
|
||||
|
||||
if (event) {
|
||||
context._activeTrigger[
|
||||
event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER
|
||||
] = true
|
||||
}
|
||||
|
||||
if ($(context.getTipElement()).hasClass(ClassName.SHOW) ||
|
||||
context._hoverState === HoverState.SHOW) {
|
||||
context._hoverState = HoverState.SHOW
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(context._timeout)
|
||||
|
||||
context._hoverState = HoverState.SHOW
|
||||
|
||||
if (!context.config.delay || !context.config.delay.show) {
|
||||
context.show()
|
||||
return
|
||||
}
|
||||
|
||||
context._timeout = setTimeout(() => {
|
||||
if (context._hoverState === HoverState.SHOW) {
|
||||
context.show()
|
||||
}
|
||||
}, context.config.delay.show)
|
||||
}
|
||||
|
||||
_leave(event, context) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
|
||||
context = context || $(event.currentTarget).data(dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
}
|
||||
|
||||
if (event) {
|
||||
context._activeTrigger[
|
||||
event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER
|
||||
] = false
|
||||
}
|
||||
|
||||
if (context._isWithActiveTrigger()) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(context._timeout)
|
||||
|
||||
context._hoverState = HoverState.OUT
|
||||
|
||||
if (!context.config.delay || !context.config.delay.hide) {
|
||||
context.hide()
|
||||
return
|
||||
}
|
||||
|
||||
context._timeout = setTimeout(() => {
|
||||
if (context._hoverState === HoverState.OUT) {
|
||||
context.hide()
|
||||
}
|
||||
}, context.config.delay.hide)
|
||||
}
|
||||
|
||||
_isWithActiveTrigger() {
|
||||
for (const trigger in this._activeTrigger) {
|
||||
if (this._activeTrigger[trigger]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...this.constructor.Default,
|
||||
...$(this.element).data(),
|
||||
...typeof config === 'object' && config ? config : {}
|
||||
}
|
||||
|
||||
if (typeof config.delay === 'number') {
|
||||
config.delay = {
|
||||
show: config.delay,
|
||||
hide: config.delay
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.title === 'number') {
|
||||
config.title = config.title.toString()
|
||||
}
|
||||
|
||||
if (typeof config.content === 'number') {
|
||||
config.content = config.content.toString()
|
||||
}
|
||||
|
||||
Util.typeCheckConfig(
|
||||
NAME,
|
||||
config,
|
||||
this.constructor.DefaultType
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
_getDelegateConfig() {
|
||||
const config = {}
|
||||
|
||||
if (this.config) {
|
||||
for (const key in this.config) {
|
||||
if (this.constructor.Default[key] !== this.config[key]) {
|
||||
config[key] = this.config[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
_cleanTipClass() {
|
||||
const $tip = $(this.getTipElement())
|
||||
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
|
||||
if (tabClass !== null && tabClass.length) {
|
||||
$tip.removeClass(tabClass.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
_handlePopperPlacementChange(popperData) {
|
||||
const popperInstance = popperData.instance
|
||||
this.tip = popperInstance.popper
|
||||
this._cleanTipClass()
|
||||
this.addAttachmentClass(this._getAttachment(popperData.placement))
|
||||
}
|
||||
|
||||
_fixTransition() {
|
||||
const tip = this.getTipElement()
|
||||
const initConfigAnimation = this.config.animation
|
||||
if (tip.getAttribute('x-placement') !== null) {
|
||||
return
|
||||
}
|
||||
$(tip).removeClass(ClassName.FADE)
|
||||
this.config.animation = false
|
||||
this.hide()
|
||||
this.show()
|
||||
this.config.animation = initConfigAnimation
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
const _config = typeof config === 'object' && config
|
||||
|
||||
if (!data && /dispose|hide/.test(config)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = new Tooltip(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
data[config]()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Tooltip._jQueryInterface
|
||||
$.fn[NAME].Constructor = Tooltip
|
||||
$.fn[NAME].noConflict = function () {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Tooltip._jQueryInterface
|
||||
}
|
||||
|
||||
return Tooltip
|
||||
})($, Popper)
|
||||
|
||||
export default Tooltip
|
152
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/util.js
vendored
Normal file
152
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/src/util.js
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
import $ from 'jquery'
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v4.1.3): util.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Util = (($) => {
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Private TransitionEnd Helpers
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const TRANSITION_END = 'transitionend'
|
||||
const MAX_UID = 1000000
|
||||
const MILLISECONDS_MULTIPLIER = 1000
|
||||
|
||||
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
|
||||
function toType(obj) {
|
||||
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
|
||||
}
|
||||
|
||||
function getSpecialTransitionEndEvent() {
|
||||
return {
|
||||
bindType: TRANSITION_END,
|
||||
delegateType: TRANSITION_END,
|
||||
handle(event) {
|
||||
if ($(event.target).is(this)) {
|
||||
return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params
|
||||
}
|
||||
return undefined // eslint-disable-line no-undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transitionEndEmulator(duration) {
|
||||
let called = false
|
||||
|
||||
$(this).one(Util.TRANSITION_END, () => {
|
||||
called = true
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
if (!called) {
|
||||
Util.triggerTransitionEnd(this)
|
||||
}
|
||||
}, duration)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
function setTransitionEndSupport() {
|
||||
$.fn.emulateTransitionEnd = transitionEndEmulator
|
||||
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Public Util Api
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const Util = {
|
||||
|
||||
TRANSITION_END: 'bsTransitionEnd',
|
||||
|
||||
getUID(prefix) {
|
||||
do {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
|
||||
} while (document.getElementById(prefix))
|
||||
return prefix
|
||||
},
|
||||
|
||||
getSelectorFromElement(element) {
|
||||
let selector = element.getAttribute('data-target')
|
||||
if (!selector || selector === '#') {
|
||||
selector = element.getAttribute('href') || ''
|
||||
}
|
||||
|
||||
try {
|
||||
return document.querySelector(selector) ? selector : null
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
getTransitionDurationFromElement(element) {
|
||||
if (!element) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Get transition-duration of the element
|
||||
let transitionDuration = $(element).css('transition-duration')
|
||||
const floatTransitionDuration = parseFloat(transitionDuration)
|
||||
|
||||
// Return 0 if element or transition duration is not found
|
||||
if (!floatTransitionDuration) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If multiple durations are defined, take the first
|
||||
transitionDuration = transitionDuration.split(',')[0]
|
||||
|
||||
return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER
|
||||
},
|
||||
|
||||
reflow(element) {
|
||||
return element.offsetHeight
|
||||
},
|
||||
|
||||
triggerTransitionEnd(element) {
|
||||
$(element).trigger(TRANSITION_END)
|
||||
},
|
||||
|
||||
// TODO: Remove in v5
|
||||
supportsTransitionEnd() {
|
||||
return Boolean(TRANSITION_END)
|
||||
},
|
||||
|
||||
isElement(obj) {
|
||||
return (obj[0] || obj).nodeType
|
||||
},
|
||||
|
||||
typeCheckConfig(componentName, config, configTypes) {
|
||||
for (const property in configTypes) {
|
||||
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
|
||||
const expectedTypes = configTypes[property]
|
||||
const value = config[property]
|
||||
const valueType = value && Util.isElement(value)
|
||||
? 'element' : toType(value)
|
||||
|
||||
if (!new RegExp(expectedTypes).test(valueType)) {
|
||||
throw new Error(
|
||||
`${componentName.toUpperCase()}: ` +
|
||||
`Option "${property}" provided type "${valueType}" ` +
|
||||
`but expected type "${expectedTypes}".`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTransitionEndSupport()
|
||||
|
||||
return Util
|
||||
})($)
|
||||
|
||||
export default Util
|
@ -0,0 +1,69 @@
|
||||
## How does Bootstrap's test suite work?
|
||||
|
||||
Bootstrap uses [QUnit](https://qunitjs.com/) and [Sinon](http://sinonjs.org/). Each plugin has a file dedicated to its tests in `unit/<plugin-name>.js`.
|
||||
|
||||
* `unit/` contains the unit test files for each Bootstrap plugin.
|
||||
* `vendor/` contains third-party testing-related code (QUnit, jQuery and Sinon).
|
||||
* `visual/` contains "visual" tests which are run interactively in real browsers and require manual verification by humans.
|
||||
|
||||
To run the unit test suite via [Karma](https://karma-runner.github.io/), run `npm run js-test`.
|
||||
|
||||
To run the unit test suite via a real web browser, open `index.html` in the browser.
|
||||
|
||||
|
||||
## How do I add a new unit test?
|
||||
|
||||
1. Locate and open the file dedicated to the plugin which you need to add tests to (`unit/<plugin-name>.js`).
|
||||
2. Review the [QUnit API Documentation](https://api.qunitjs.com/) and use the existing tests as references for how to structure your new tests.
|
||||
3. Write the necessary unit test(s) for the new or revised functionality.
|
||||
4. Run `npm run js-test` to see the results of your newly-added test(s).
|
||||
|
||||
**Note:** Your new unit tests should fail before your changes are applied to the plugin, and should pass after your changes are applied to the plugin.
|
||||
|
||||
## What should a unit test look like?
|
||||
|
||||
* Each test should have a unique name clearly stating what unit is being tested.
|
||||
* Each test should test only one unit per test, although one test can include several assertions. Create multiple tests for multiple units of functionality.
|
||||
* Each test should begin with [`assert.expect`](https://api.qunitjs.com/assert/expect/) to ensure that the expected assertions are run.
|
||||
* Each test should follow the project's [JavaScript Code Guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#js)
|
||||
|
||||
## Code coverage
|
||||
|
||||
Currently we're aiming for at least 80% test coverage for our code. To ensure your changes meet or exceed this limit, run `npm run js-compile && npm run js-test` and open the file in `js/coverage/lcov-report/index.html` to see the code coverage for each plugin. See more details when you select a plugin and ensure your change is fully covered by unit tests.
|
||||
|
||||
### Example tests
|
||||
|
||||
```js
|
||||
// Synchronous test
|
||||
QUnit.test('should describe the unit being tested', function (assert) {
|
||||
assert.expect(1)
|
||||
var templateHTML = '<div class="alert alert-danger fade show">' +
|
||||
'<a class="close" href="#" data-dismiss="alert">×</a>' +
|
||||
'<p><strong>Template necessary for the test.</p>' +
|
||||
'</div>'
|
||||
var $alert = $(templateHTML).appendTo('#qunit-fixture').bootstrapAlert()
|
||||
|
||||
$alert.find('.close').trigger('click')
|
||||
|
||||
// Make assertion
|
||||
assert.strictEqual($alert.hasClass('show'), false, 'remove .show class on .close click')
|
||||
})
|
||||
|
||||
// Asynchronous test
|
||||
QUnit.test('should describe the unit being tested', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<div title="tooltip title"></div>').bootstrapTooltip()
|
||||
var tooltipInstance = $tooltip.data('bs.tooltip')
|
||||
var spyShow = sinon.spy(tooltipInstance, 'show')
|
||||
|
||||
$tooltip.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
assert.ok(true, '"shown" event was fired after calling "show"')
|
||||
assert.ok(spyShow.called, 'show called')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
```
|
@ -0,0 +1,140 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Bootstrap Plugin Test Suite</title>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script>
|
||||
(function () {
|
||||
var path = '../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js'
|
||||
// get jquery param from the query string.
|
||||
var jQueryVersion = location.search.match(/[?&]jquery=(.*?)(?=&|$)/)
|
||||
|
||||
// If a version was specified, use that version from jQuery CDN
|
||||
if (jQueryVersion) {
|
||||
path = 'https://code.jquery.com/jquery-' + jQueryVersion[1] + '.min.js'
|
||||
}
|
||||
document.write('<script src="' + path + '"><\/script>')
|
||||
}())
|
||||
</script>
|
||||
<script src="../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
|
||||
<!-- QUnit -->
|
||||
<link rel="stylesheet" href="../../node_modules/qunit/qunit/qunit.css" media="screen">
|
||||
<script src="../../node_modules/qunit/qunit/qunit.js"></script>
|
||||
|
||||
<!-- Sinon -->
|
||||
<script src="../../node_modules/sinon/pkg/sinon-no-sourcemaps.js"></script>
|
||||
|
||||
<script>
|
||||
// Disable jQuery event aliases to ensure we don't accidentally use any of them
|
||||
[
|
||||
'blur',
|
||||
'focus',
|
||||
'focusin',
|
||||
'focusout',
|
||||
'resize',
|
||||
'scroll',
|
||||
'click',
|
||||
'dblclick',
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mousemove',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
'change',
|
||||
'select',
|
||||
'submit',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'contextmenu'
|
||||
].forEach(function(eventAlias) {
|
||||
$.fn[eventAlias] = function() {
|
||||
throw new Error('Using the ".' + eventAlias + '()" method is not allowed, so that Bootstrap can be compatible with custom jQuery builds which exclude the "event aliases" module that defines said method. See https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#js')
|
||||
}
|
||||
})
|
||||
|
||||
// Require assert.expect in each test
|
||||
QUnit.config.requireExpects = true
|
||||
|
||||
// See https://github.com/axemclion/grunt-saucelabs#test-result-details-with-qunit
|
||||
var log = []
|
||||
var testName
|
||||
|
||||
QUnit.done(function(testResults) {
|
||||
var tests = []
|
||||
for (var i = 0; i < log.length; i++) {
|
||||
var details = log[i]
|
||||
tests.push({
|
||||
name: details.name,
|
||||
result: details.result,
|
||||
expected: details.expected,
|
||||
actual: details.actual,
|
||||
source: details.source
|
||||
})
|
||||
}
|
||||
testResults.tests = tests
|
||||
|
||||
window.global_test_results = testResults
|
||||
})
|
||||
|
||||
QUnit.testStart(function(testDetails) {
|
||||
QUnit.log(function(details) {
|
||||
if (!details.result) {
|
||||
details.name = testDetails.name
|
||||
log.push(details)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Display fixture on-screen on iOS to avoid false positives
|
||||
// See https://github.com/twbs/bootstrap/pull/15955
|
||||
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
|
||||
QUnit.begin(function() {
|
||||
$('#qunit-fixture').css({ top: 0, left: 0 })
|
||||
})
|
||||
|
||||
QUnit.done(function() {
|
||||
$('#qunit-fixture').css({ top: '', left: '' })
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Transpiled Plugins -->
|
||||
<script src="../dist/util.js"></script>
|
||||
<script src="../dist/alert.js"></script>
|
||||
<script src="../dist/button.js"></script>
|
||||
<script src="../dist/carousel.js"></script>
|
||||
<script src="../dist/collapse.js"></script>
|
||||
<script src="../dist/dropdown.js"></script>
|
||||
<script src="../dist/modal.js"></script>
|
||||
<script src="../dist/scrollspy.js"></script>
|
||||
<script src="../dist/tab.js"></script>
|
||||
<script src="../dist/tooltip.js"></script>
|
||||
<script src="../dist/popover.js"></script>
|
||||
|
||||
<!-- Unit Tests -->
|
||||
<script src="unit/alert.js"></script>
|
||||
<script src="unit/button.js"></script>
|
||||
<script src="unit/carousel.js"></script>
|
||||
<script src="unit/collapse.js"></script>
|
||||
<script src="unit/dropdown.js"></script>
|
||||
<script src="unit/modal.js"></script>
|
||||
<script src="unit/scrollspy.js"></script>
|
||||
<script src="unit/tab.js"></script>
|
||||
<script src="unit/tooltip.js"></script>
|
||||
<script src="unit/popover.js"></script>
|
||||
<script src="unit/util.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit-container">
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
53
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/karma-bundle.conf.js
vendored
Normal file
53
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/karma-bundle.conf.js
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/* eslint-env node */
|
||||
/* eslint no-process-env: 0 */
|
||||
|
||||
module.exports = (config) => {
|
||||
const jqueryFile = process.env.USE_OLD_JQUERY ? 'https://code.jquery.com/jquery-1.9.1.min.js' : 'site/docs/4.1/assets/js/vendor/jquery-slim.min.js'
|
||||
|
||||
config.set({
|
||||
basePath: '../..',
|
||||
frameworks: ['qunit', 'sinon', 'detectBrowsers'],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-qunit',
|
||||
'karma-sinon',
|
||||
'karma-detect-browsers'
|
||||
],
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
jqueryFile,
|
||||
'site/docs/4.1/assets/js/vendor/popper.min.js',
|
||||
'dist/js/bootstrap.js',
|
||||
'js/tests/unit/*.js'
|
||||
],
|
||||
reporters: ['dots'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_ERROR || config.LOG_WARN,
|
||||
autoWatch: false,
|
||||
customLaunchers: {
|
||||
FirefoxHeadless: {
|
||||
base: 'Firefox',
|
||||
flags: ['-headless']
|
||||
}
|
||||
},
|
||||
singleRun: true,
|
||||
concurrency: Infinity,
|
||||
detectBrowsers: {
|
||||
usePhantomJS: false,
|
||||
postDetection(availableBrowser) {
|
||||
if (typeof process.env.TRAVIS_JOB_ID !== 'undefined' || availableBrowser.includes('Chrome')) {
|
||||
return ['ChromeHeadless']
|
||||
}
|
||||
|
||||
if (availableBrowser.includes('Firefox')) {
|
||||
return ['FirefoxHeadless']
|
||||
}
|
||||
|
||||
throw new Error('Please install Firefox or Chrome')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
76
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/karma.conf.js
vendored
Normal file
76
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/karma.conf.js
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/* eslint-env node */
|
||||
/* eslint no-process-env: 0 */
|
||||
const path = require('path')
|
||||
const jsCoveragePath = path.resolve(__dirname, '../coverage')
|
||||
|
||||
module.exports = (config) => {
|
||||
const jqueryFile = process.env.USE_OLD_JQUERY ? 'https://code.jquery.com/jquery-1.9.1.min.js' : 'site/docs/4.1/assets/js/vendor/jquery-slim.min.js'
|
||||
|
||||
config.set({
|
||||
basePath: '../..',
|
||||
frameworks: ['qunit', 'sinon', 'detectBrowsers'],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-qunit',
|
||||
'karma-sinon',
|
||||
'karma-detect-browsers',
|
||||
'karma-coverage-istanbul-reporter'
|
||||
],
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
jqueryFile,
|
||||
'site/docs/4.1/assets/js/vendor/popper.min.js',
|
||||
'js/coverage/dist/util.js',
|
||||
'js/coverage/dist/tooltip.js',
|
||||
'js/coverage/dist/!(util|index|tooltip).js', // include all of our js/dist files except util.js, index.js and tooltip.js
|
||||
'js/tests/unit/*.js'
|
||||
],
|
||||
reporters: ['dots', 'coverage-istanbul'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_ERROR || config.LOG_WARN,
|
||||
autoWatch: false,
|
||||
customLaunchers: {
|
||||
FirefoxHeadless: {
|
||||
base: 'Firefox',
|
||||
flags: ['-headless']
|
||||
}
|
||||
},
|
||||
singleRun: true,
|
||||
concurrency: Infinity,
|
||||
detectBrowsers: {
|
||||
usePhantomJS: false,
|
||||
postDetection(availableBrowser) {
|
||||
if (typeof process.env.TRAVIS_JOB_ID !== 'undefined' || availableBrowser.includes('Chrome')) {
|
||||
return ['ChromeHeadless']
|
||||
}
|
||||
|
||||
if (availableBrowser.includes('Firefox')) {
|
||||
return ['FirefoxHeadless']
|
||||
}
|
||||
|
||||
throw new Error('Please install Firefox or Chrome')
|
||||
}
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: jsCoveragePath,
|
||||
reports: ['lcov', 'text-summary'],
|
||||
thresholds: {
|
||||
emitWarning: false,
|
||||
global: {
|
||||
statements: 90,
|
||||
branches: 84,
|
||||
functions: 87,
|
||||
lines: 90
|
||||
}
|
||||
}
|
||||
},
|
||||
client: {
|
||||
qunit: {
|
||||
showUI: true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": false,
|
||||
"jquery": true,
|
||||
"qunit": true
|
||||
},
|
||||
"globals": {
|
||||
"bootstrap": false,
|
||||
"sinon": false,
|
||||
"Util": false,
|
||||
"Alert": false,
|
||||
"Button": false
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
"sourceType": "script"
|
||||
},
|
||||
"extends": "../../../.eslintrc.json",
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
// Best Practices
|
||||
"consistent-return": "off",
|
||||
"no-magic-numbers": "off",
|
||||
"vars-on-top": "off",
|
||||
|
||||
// Stylistic Issues
|
||||
"func-style": "off",
|
||||
"spaced-comment": "off",
|
||||
|
||||
// ECMAScript 6
|
||||
"no-var": "off",
|
||||
"object-shorthand": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"prefer-template": "off",
|
||||
"prefer-rest-params": "off"
|
||||
}
|
||||
}
|
123
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/alert.js
vendored
Normal file
123
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/alert.js
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('alert plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).alert, 'alert method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('alert', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapAlert = $.fn.alert.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.alert = $.fn.bootstrapAlert
|
||||
delete $.fn.bootstrapAlert
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.alert, 'undefined', 'alert was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $alert = $el.bootstrapAlert()
|
||||
assert.ok($alert instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($alert[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should fade element out on clicking .close', function (assert) {
|
||||
assert.expect(1)
|
||||
var alertHTML = '<div class="alert alert-danger fade show">' +
|
||||
'<a class="close" href="#" data-dismiss="alert">×</a>' +
|
||||
'<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>' +
|
||||
'</div>'
|
||||
|
||||
var $alert = $(alertHTML).bootstrapAlert().appendTo($('#qunit-fixture'))
|
||||
|
||||
$alert.find('.close').trigger('click')
|
||||
|
||||
assert.strictEqual($alert.hasClass('show'), false, 'remove .show class on .close click')
|
||||
})
|
||||
|
||||
QUnit.test('should remove element when clicking .close', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var alertHTML = '<div class="alert alert-danger fade show">' +
|
||||
'<a class="close" href="#" data-dismiss="alert">×</a>' +
|
||||
'<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>' +
|
||||
'</div>'
|
||||
var $alert = $(alertHTML).appendTo('#qunit-fixture').bootstrapAlert()
|
||||
|
||||
assert.notEqual($('#qunit-fixture').find('.alert').length, 0, 'element added to dom')
|
||||
|
||||
$alert
|
||||
.one('closed.bs.alert', function () {
|
||||
assert.strictEqual($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom')
|
||||
done()
|
||||
})
|
||||
.find('.close')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire closed when close is prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
$('<div class="alert"/>')
|
||||
.on('close.bs.alert', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'close event fired')
|
||||
done()
|
||||
})
|
||||
.on('closed.bs.alert', function () {
|
||||
assert.ok(false, 'closed event fired')
|
||||
})
|
||||
.bootstrapAlert('close')
|
||||
})
|
||||
|
||||
QUnit.test('close should use internal _element if no element provided', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
var done = assert.async()
|
||||
var $el = $('<div/>')
|
||||
var $alert = $el.bootstrapAlert()
|
||||
var alertInstance = $alert.data('bs.alert')
|
||||
|
||||
$alert.one('closed.bs.alert', function () {
|
||||
assert.ok('alert closed')
|
||||
done()
|
||||
})
|
||||
|
||||
alertInstance.close()
|
||||
})
|
||||
|
||||
QUnit.test('dispose should remove data and the element', function (assert) {
|
||||
assert.expect(2)
|
||||
|
||||
var $el = $('<div/>')
|
||||
var $alert = $el.bootstrapAlert()
|
||||
|
||||
assert.ok(typeof $alert.data('bs.alert') !== 'undefined')
|
||||
|
||||
$alert.data('bs.alert').dispose()
|
||||
|
||||
assert.ok(typeof $alert.data('bs.button') === 'undefined')
|
||||
})
|
||||
|
||||
QUnit.test('should return alert version', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
if (typeof Alert !== 'undefined') {
|
||||
assert.ok(typeof Alert.VERSION === 'string')
|
||||
} else {
|
||||
assert.notOk()
|
||||
}
|
||||
})
|
||||
})
|
199
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/button.js
vendored
Normal file
199
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/button.js
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('button plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).button, 'button method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('button', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapButton = $.fn.button.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.button = $.fn.bootstrapButton
|
||||
delete $.fn.bootstrapButton
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.button, 'undefined', 'button was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $button = $el.bootstrapButton()
|
||||
assert.ok($button instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($button[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle active', function (assert) {
|
||||
assert.expect(2)
|
||||
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
|
||||
assert.ok(!$btn.hasClass('active'), 'btn does not have active class')
|
||||
$btn.bootstrapButton('toggle')
|
||||
assert.ok($btn.hasClass('active'), 'btn has class active')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle active when btn children are clicked', function (assert) {
|
||||
assert.expect(2)
|
||||
var $btn = $('<button class="btn" data-toggle="button">mdo</button>')
|
||||
var $inner = $('<i/>')
|
||||
$btn
|
||||
.append($inner)
|
||||
.appendTo('#qunit-fixture')
|
||||
assert.ok(!$btn.hasClass('active'), 'btn does not have active class')
|
||||
$inner.trigger('click')
|
||||
assert.ok($btn.hasClass('active'), 'btn has class active')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle aria-pressed', function (assert) {
|
||||
assert.expect(2)
|
||||
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
|
||||
assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
|
||||
$btn.bootstrapButton('toggle')
|
||||
assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle aria-pressed on buttons with container', function (assert) {
|
||||
assert.expect(1)
|
||||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' +
|
||||
'<button id="btn1" class="btn btn-secondary" type="button">One</button>' +
|
||||
'<button class="btn btn-secondary" type="button">Two</button>' +
|
||||
'</div>'
|
||||
$('#qunit-fixture').append(groupHTML)
|
||||
$('#btn1').bootstrapButton('toggle')
|
||||
assert.strictEqual($('#btn1').attr('aria-pressed'), 'true')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle aria-pressed when btn children are clicked', function (assert) {
|
||||
assert.expect(2)
|
||||
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>')
|
||||
var $inner = $('<i/>')
|
||||
$btn
|
||||
.append($inner)
|
||||
.appendTo('#qunit-fixture')
|
||||
assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false')
|
||||
$inner.trigger('click')
|
||||
assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true')
|
||||
})
|
||||
|
||||
QUnit.test('should trigger input change event when toggled button has input field', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' +
|
||||
'<label class="btn btn-primary">' +
|
||||
'<input type="radio" id="radio" autocomplete="off">Radio' +
|
||||
'</label>' +
|
||||
'</div>'
|
||||
var $group = $(groupHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$group.find('input').on('change', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'change event fired')
|
||||
done()
|
||||
})
|
||||
|
||||
$group.find('label').trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should check for closest matching toggle', function (assert) {
|
||||
assert.expect(12)
|
||||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' +
|
||||
'<label class="btn btn-primary active">' +
|
||||
'<input type="radio" name="options" id="option1" checked="true"> Option 1' +
|
||||
'</label>' +
|
||||
'<label class="btn btn-primary">' +
|
||||
'<input type="radio" name="options" id="option2"> Option 2' +
|
||||
'</label>' +
|
||||
'<label class="btn btn-primary">' +
|
||||
'<input type="radio" name="options" id="option3"> Option 3' +
|
||||
'</label>' +
|
||||
'</div>'
|
||||
var $group = $(groupHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var $btn1 = $group.children().eq(0)
|
||||
var $btn2 = $group.children().eq(1)
|
||||
|
||||
assert.ok($btn1.hasClass('active'), 'btn1 has active class')
|
||||
assert.ok($btn1.find('input').prop('checked'), 'btn1 is checked')
|
||||
assert.ok(!$btn2.hasClass('active'), 'btn2 does not have active class')
|
||||
assert.ok(!$btn2.find('input').prop('checked'), 'btn2 is not checked')
|
||||
$btn2.find('input').trigger('click')
|
||||
assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
|
||||
assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked')
|
||||
assert.ok($btn2.hasClass('active'), 'btn2 has active class')
|
||||
assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked')
|
||||
|
||||
$btn2.find('input').trigger('click') // Clicking an already checked radio should not un-check it
|
||||
assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class')
|
||||
assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked')
|
||||
assert.ok($btn2.hasClass('active'), 'btn2 has active class')
|
||||
assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked')
|
||||
})
|
||||
|
||||
QUnit.test('should not add aria-pressed on labels for radio/checkbox inputs in a data-toggle="buttons" group', function (assert) {
|
||||
assert.expect(2)
|
||||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' +
|
||||
'<label class="btn btn-primary"><input type="checkbox" autocomplete="off"> Checkbox</label>' +
|
||||
'<label class="btn btn-primary"><input type="radio" name="options" autocomplete="off"> Radio</label>' +
|
||||
'</div>'
|
||||
var $group = $(groupHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var $btn1 = $group.children().eq(0)
|
||||
var $btn2 = $group.children().eq(1)
|
||||
|
||||
$btn1.find('input').trigger('click')
|
||||
assert.ok($btn1.is(':not([aria-pressed])'), 'label for nested checkbox input has not been given an aria-pressed attribute')
|
||||
|
||||
$btn2.find('input').trigger('click')
|
||||
assert.ok($btn2.is(':not([aria-pressed])'), 'label for nested radio input has not been given an aria-pressed attribute')
|
||||
})
|
||||
|
||||
QUnit.test('should handle disabled attribute on non-button elements', function (assert) {
|
||||
assert.expect(2)
|
||||
var groupHTML = '<div class="btn-group disabled" data-toggle="buttons" aria-disabled="true" disabled>' +
|
||||
'<label class="btn btn-danger disabled" aria-disabled="true" disabled>' +
|
||||
'<input type="checkbox" aria-disabled="true" autocomplete="off" disabled class="disabled"/>' +
|
||||
'</label>' +
|
||||
'</div>'
|
||||
var $group = $(groupHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var $btn = $group.children().eq(0)
|
||||
var $input = $btn.children().eq(0)
|
||||
|
||||
$btn.trigger('click')
|
||||
assert.ok($btn.is(':not(.active)'), 'button did not become active')
|
||||
assert.ok(!$input.is(':checked'), 'checkbox did not get checked')
|
||||
})
|
||||
|
||||
QUnit.test('dispose should remove data and the element', function (assert) {
|
||||
assert.expect(2)
|
||||
|
||||
var $el = $('<div/>')
|
||||
var $button = $el.bootstrapButton()
|
||||
|
||||
assert.ok(typeof $button.data('bs.button') !== 'undefined')
|
||||
|
||||
$button.data('bs.button').dispose()
|
||||
|
||||
assert.ok(typeof $button.data('bs.button') === 'undefined')
|
||||
})
|
||||
|
||||
QUnit.test('should return button version', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
if (typeof Button !== 'undefined') {
|
||||
assert.ok(typeof Button.VERSION === 'string')
|
||||
} else {
|
||||
assert.notOk()
|
||||
}
|
||||
})
|
||||
})
|
944
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/carousel.js
vendored
Normal file
944
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/carousel.js
vendored
Normal file
@ -0,0 +1,944 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('carousel plugin')
|
||||
|
||||
QUnit.test('should be defined on jQuery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).carousel, 'carousel method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('carousel', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapCarousel = $.fn.carousel.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.carousel = $.fn.bootstrapCarousel
|
||||
delete $.fn.bootstrapCarousel
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.carousel, 'undefined', 'carousel was set back to undefined (orig value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>')
|
||||
$el.bootstrapCarousel()
|
||||
try {
|
||||
$el.bootstrapCarousel('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $carousel = $el.bootstrapCarousel()
|
||||
assert.ok($carousel instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($carousel[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should type check config options', function (assert) {
|
||||
assert.expect(2)
|
||||
|
||||
var message
|
||||
var expectedMessage = 'CAROUSEL: Option "interval" provided type "string" but expected type "(number|boolean)".'
|
||||
var config = {
|
||||
interval: 'fat sux'
|
||||
}
|
||||
|
||||
try {
|
||||
$('<div/>').bootstrapCarousel(config)
|
||||
} catch (err) {
|
||||
message = err.message
|
||||
}
|
||||
|
||||
assert.ok(message === expectedMessage, 'correct error message')
|
||||
|
||||
config = {
|
||||
keyboard: document.createElement('div')
|
||||
}
|
||||
expectedMessage = 'CAROUSEL: Option "keyboard" provided type "element" but expected type "boolean".'
|
||||
|
||||
try {
|
||||
$('<div/>').bootstrapCarousel(config)
|
||||
} catch (err) {
|
||||
message = err.message
|
||||
}
|
||||
|
||||
assert.ok(message === expectedMessage, 'correct error message')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire slid when slide is prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
$('<div class="carousel"/>')
|
||||
.on('slide.bs.carousel', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'slide event fired')
|
||||
done()
|
||||
})
|
||||
.on('slid.bs.carousel', function () {
|
||||
assert.ok(false, 'slid event fired')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should reset when slide is prevented', function (assert) {
|
||||
assert.expect(6)
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">' +
|
||||
'<ol class="carousel-indicators">' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
|
||||
'</ol>' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' +
|
||||
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
var done = assert.async()
|
||||
$carousel
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
e.preventDefault()
|
||||
setTimeout(function () {
|
||||
assert.ok($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active')
|
||||
assert.ok($carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active')
|
||||
$carousel.bootstrapCarousel('next')
|
||||
}, 0)
|
||||
})
|
||||
.one('slid.bs.carousel', function () {
|
||||
setTimeout(function () {
|
||||
assert.ok(!$carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active')
|
||||
assert.ok(!$carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active')
|
||||
assert.ok($carousel.find('.carousel-item:nth-child(2)').is('.active'), 'second item active')
|
||||
assert.ok($carousel.find('.carousel-indicators li:nth-child(2)').is('.active'), 'second indicator active')
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should fire slide event with direction', function (assert) {
|
||||
assert.expect(4)
|
||||
var carouselHTML = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
assert.ok(e.direction, 'direction present on next')
|
||||
assert.strictEqual(e.direction, 'left', 'direction is left on next')
|
||||
|
||||
$carousel
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
assert.ok(e.direction, 'direction present on prev')
|
||||
assert.strictEqual(e.direction, 'right', 'direction is right on prev')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should fire slid event with direction', function (assert) {
|
||||
assert.expect(4)
|
||||
var carouselHTML = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function (e) {
|
||||
assert.ok(e.direction, 'direction present on next')
|
||||
assert.strictEqual(e.direction, 'left', 'direction is left on next')
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function (e) {
|
||||
assert.ok(e.direction, 'direction present on prev')
|
||||
assert.strictEqual(e.direction, 'right', 'direction is right on prev')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should fire slide event with relatedTarget', function (assert) {
|
||||
assert.expect(2)
|
||||
var template = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$(template)
|
||||
.on('slide.bs.carousel', function (e) {
|
||||
assert.ok(e.relatedTarget, 'relatedTarget present')
|
||||
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should fire slid event with relatedTarget', function (assert) {
|
||||
assert.expect(2)
|
||||
var template = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$(template)
|
||||
.on('slid.bs.carousel', function (e) {
|
||||
assert.ok(e.relatedTarget, 'relatedTarget present')
|
||||
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should fire slid and slide events with from and to', function (assert) {
|
||||
assert.expect(4)
|
||||
var template = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
|
||||
var done = assert.async()
|
||||
$(template)
|
||||
.on('slid.bs.carousel', function (e) {
|
||||
assert.ok(typeof e.from !== 'undefined', 'from present')
|
||||
assert.ok(typeof e.to !== 'undefined', 'to present')
|
||||
$(this).off()
|
||||
done()
|
||||
})
|
||||
.on('slide.bs.carousel', function (e) {
|
||||
assert.ok(typeof e.from !== 'undefined', 'from present')
|
||||
assert.ok(typeof e.to !== 'undefined', 'to present')
|
||||
$(this).off('slide.bs.carousel')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should set interval from data attribute', function (assert) {
|
||||
assert.expect(4)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel slide">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>First Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Second Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'<div class="carousel-caption">' +
|
||||
'<h4>Third Thumbnail label</h4>' +
|
||||
'<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' +
|
||||
'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' +
|
||||
'ultricies vehicula ut id elit.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
'<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
var $carousel = $(templateHTML)
|
||||
$carousel.attr('data-interval', 1814)
|
||||
|
||||
$carousel.appendTo('body')
|
||||
$('[data-slide]').first().trigger('click')
|
||||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814)
|
||||
$carousel.remove()
|
||||
|
||||
$carousel.appendTo('body').attr('data-modal', 'foobar')
|
||||
$('[data-slide]').first().trigger('click')
|
||||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set')
|
||||
$carousel.remove()
|
||||
|
||||
$carousel.appendTo('body')
|
||||
$('[data-slide]').first().trigger('click')
|
||||
$carousel.attr('data-interval', 1860)
|
||||
$('[data-slide]').first().trigger('click')
|
||||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization')
|
||||
$carousel.remove()
|
||||
|
||||
$carousel.attr('data-interval', false)
|
||||
$carousel.appendTo('body')
|
||||
$carousel.bootstrapCarousel(1)
|
||||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, false, 'data attribute has higher priority than default options')
|
||||
$carousel.remove()
|
||||
})
|
||||
|
||||
QUnit.test('should skip over non-items when using item indices', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<script type="text/x-metamorph" id="thingy"/>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
|
||||
$template.bootstrapCarousel(1)
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
|
||||
})
|
||||
|
||||
QUnit.test('should skip over non-items when using next/prev methods', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<script type="text/x-metamorph" id="thingy"/>' +
|
||||
'<div class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
|
||||
$template.bootstrapCarousel('next')
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
|
||||
})
|
||||
|
||||
QUnit.test('should go to previous item if left arrow key is pressed', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="second" class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="third" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
|
||||
|
||||
$template.trigger($.Event('keydown', {
|
||||
which: 37
|
||||
}))
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
})
|
||||
|
||||
QUnit.test('should go to next item if right arrow key is pressed', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="second" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="third" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
|
||||
$template.trigger($.Event('keydown', {
|
||||
which: 39
|
||||
}))
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
|
||||
})
|
||||
|
||||
QUnit.test('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
var done = assert.async()
|
||||
|
||||
var eventArrowDown = $.Event('keydown', {
|
||||
which: 40
|
||||
})
|
||||
var eventArrowUp = $.Event('keydown', {
|
||||
which: 38
|
||||
})
|
||||
|
||||
$template.one('keydown', function (event) {
|
||||
assert.strictEqual(event.isDefaultPrevented(), false)
|
||||
})
|
||||
|
||||
$template.trigger(eventArrowDown)
|
||||
|
||||
$template.one('keydown', function (event) {
|
||||
assert.strictEqual(event.isDefaultPrevented(), false)
|
||||
done()
|
||||
})
|
||||
|
||||
$template.trigger(eventArrowUp)
|
||||
})
|
||||
|
||||
QUnit.test('should support disabling the keyboard navigation', function (assert) {
|
||||
assert.expect(3)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-keyboard="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="second" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="third" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
|
||||
$template.trigger($.Event('keydown', {
|
||||
which: 39
|
||||
}))
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press')
|
||||
|
||||
$template.trigger($.Event('keydown', {
|
||||
which: 37
|
||||
}))
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press')
|
||||
})
|
||||
|
||||
QUnit.test('should ignore keyboard events within <input>s and <textarea>s', function (assert) {
|
||||
assert.expect(7)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item active">' +
|
||||
'<img alt="">' +
|
||||
'<input type="text" id="in-put">' +
|
||||
'<textarea id="text-area"></textarea>' +
|
||||
'</div>' +
|
||||
'<div id="second" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'<div id="third" class="carousel-item">' +
|
||||
'<img alt="">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
var $input = $template.find('#in-put')
|
||||
var $textarea = $template.find('#text-area')
|
||||
|
||||
assert.strictEqual($input.length, 1, 'found <input>')
|
||||
assert.strictEqual($textarea.length, 1, 'found <textarea>')
|
||||
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
|
||||
|
||||
$input.trigger($.Event('keydown', {
|
||||
which: 39
|
||||
}))
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <input>')
|
||||
|
||||
$input.trigger($.Event('keydown', {
|
||||
which: 37
|
||||
}))
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <input>')
|
||||
|
||||
$textarea.trigger($.Event('keydown', {
|
||||
which: 39
|
||||
}))
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <textarea>')
|
||||
|
||||
$textarea.trigger($.Event('keydown', {
|
||||
which: 37
|
||||
}))
|
||||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <textarea>')
|
||||
})
|
||||
|
||||
QUnit.test('should wrap around from end to start when wrap option is true', function (assert) {
|
||||
assert.expect(3)
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' +
|
||||
'<ol class="carousel-indicators">' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
|
||||
'</ol>' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active" id="one">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="two">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="three">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' +
|
||||
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
var getActiveId = function () {
|
||||
return $carousel.find('.carousel-item.active').attr('id')
|
||||
}
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should wrap around from start to end when wrap option is true', function (assert) {
|
||||
assert.expect(1)
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' +
|
||||
'<ol class="carousel-indicators">' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
|
||||
'</ol>' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active" id="one">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="two">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="three">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' +
|
||||
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.on('slid.bs.carousel', function () {
|
||||
assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
|
||||
QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) {
|
||||
assert.expect(3)
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' +
|
||||
'<ol class="carousel-indicators">' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
|
||||
'</ol>' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active" id="one">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="two">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="three">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' +
|
||||
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
var getActiveId = function () {
|
||||
return $carousel.find('.carousel-item.active').attr('id')
|
||||
}
|
||||
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
assert.ok(false, 'carousel slid when it should not have slid')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) {
|
||||
assert.expect(1)
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' +
|
||||
'<ol class="carousel-indicators">' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
|
||||
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
|
||||
'</ol>' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div class="carousel-item active" id="one">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="two">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'<div class="carousel-item" id="three">' +
|
||||
'<div class="carousel-caption"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' +
|
||||
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
|
||||
'</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
$carousel
|
||||
.on('slid.bs.carousel', function () {
|
||||
assert.ok(false, 'carousel slid when it should not have slid')
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide')
|
||||
})
|
||||
|
||||
QUnit.test('should not prevent keydown for inputs and textareas', function (assert) {
|
||||
assert.expect(2)
|
||||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' +
|
||||
'<div class="carousel-inner">' +
|
||||
'<div id="first" class="carousel-item">' +
|
||||
'<input type="text" id="inputText" />' +
|
||||
'</div>' +
|
||||
'<div id="second" class="carousel-item active">' +
|
||||
'<textarea id="txtArea"></textarea>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $template = $(templateHTML)
|
||||
var done = assert.async()
|
||||
$template.appendTo('#qunit-fixture')
|
||||
var $inputText = $template.find('#inputText')
|
||||
var $textArea = $template.find('#txtArea')
|
||||
$template.bootstrapCarousel()
|
||||
|
||||
var eventKeyDown = $.Event('keydown', {
|
||||
which: 65
|
||||
}) // 65 for "a"
|
||||
$inputText.on('keydown', function (event) {
|
||||
assert.strictEqual(event.isDefaultPrevented(), false)
|
||||
})
|
||||
$inputText.trigger(eventKeyDown)
|
||||
|
||||
$textArea.on('keydown', function (event) {
|
||||
assert.strictEqual(event.isDefaultPrevented(), false)
|
||||
done()
|
||||
})
|
||||
$textArea.trigger(eventKeyDown)
|
||||
})
|
||||
|
||||
QUnit.test('Should not go to the next item when the carousel is not visible', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var html = '<div id="myCarousel" class="carousel slide" data-interval="50" style="display: none;">' +
|
||||
' <div class="carousel-inner">' +
|
||||
' <div id="firstItem" class="carousel-item active">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <div class="carousel-item">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <div class="carousel-item">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
' <a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
'</div>'
|
||||
var $html = $(html)
|
||||
$html
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapCarousel()
|
||||
|
||||
var $firstItem = $('#firstItem')
|
||||
setTimeout(function () {
|
||||
assert.ok($firstItem.hasClass('active'))
|
||||
$html
|
||||
.bootstrapCarousel('dispose')
|
||||
.attr('style', 'visibility: hidden;')
|
||||
.bootstrapCarousel()
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($firstItem.hasClass('active'))
|
||||
done()
|
||||
}, 80)
|
||||
}, 80)
|
||||
})
|
||||
|
||||
QUnit.test('Should not go to the next item when the parent of the carousel is not visible', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var html = '<div id="parent" style="display: none;">' +
|
||||
' <div id="myCarousel" class="carousel slide" data-interval="50" style="display: none;">' +
|
||||
' <div class="carousel-inner">' +
|
||||
' <div id="firstItem" class="carousel-item active">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <div class="carousel-item">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <div class="carousel-item">' +
|
||||
' <img alt="">' +
|
||||
' </div>' +
|
||||
' <a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' +
|
||||
' <a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' +
|
||||
' </div>' +
|
||||
'</div>'
|
||||
var $html = $(html)
|
||||
$html.appendTo('#qunit-fixture')
|
||||
var $parent = $html.find('#parent')
|
||||
var $carousel = $html.find('#myCarousel')
|
||||
$carousel.bootstrapCarousel()
|
||||
var $firstItem = $('#firstItem')
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($firstItem.hasClass('active'))
|
||||
$carousel.bootstrapCarousel('dispose')
|
||||
$parent.attr('style', 'visibility: hidden;')
|
||||
$carousel.bootstrapCarousel()
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($firstItem.hasClass('active'))
|
||||
done()
|
||||
}, 80)
|
||||
}, 80)
|
||||
})
|
||||
})
|
858
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/collapse.js
vendored
Normal file
858
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/collapse.js
vendored
Normal file
@ -0,0 +1,858 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('collapse plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).collapse, 'collapse method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('collapse', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapCollapse = $.fn.collapse.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.collapse = $.fn.bootstrapCollapse
|
||||
delete $.fn.bootstrapCollapse
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.collapse, 'undefined', 'collapse was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>')
|
||||
$el.bootstrapCollapse()
|
||||
try {
|
||||
$el.bootstrapCollapse('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $collapse = $el.bootstrapCollapse()
|
||||
assert.ok($collapse instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($collapse[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should show a collapsed element', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $el = $('<div class="collapse"/>')
|
||||
|
||||
$el.one('shown.bs.collapse', function () {
|
||||
assert.ok($el.hasClass('show'), 'has class "show"')
|
||||
assert.ok(!/height/i.test($el.attr('style')), 'has height reset')
|
||||
done()
|
||||
}).bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should show multiple collapsed elements', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href=".multi"/>').appendTo('#qunit-fixture')
|
||||
var $el = $('<div class="collapse multi"/>').appendTo('#qunit-fixture')
|
||||
var $el2 = $('<div class="collapse multi"/>').appendTo('#qunit-fixture')
|
||||
$el.one('shown.bs.collapse', function () {
|
||||
assert.ok($el.hasClass('show'), 'has class "show"')
|
||||
assert.ok(!/height/i.test($el.attr('style')), 'has height reset')
|
||||
})
|
||||
$el2.one('shown.bs.collapse', function () {
|
||||
assert.ok($el2.hasClass('show'), 'has class "show"')
|
||||
assert.ok(!/height/i.test($el2.attr('style')), 'has height reset')
|
||||
done()
|
||||
})
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should collapse only the first collapse', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var html = [
|
||||
'<div class="panel-group" id="accordion1">',
|
||||
'<div class="panel">',
|
||||
'<div id="collapse1" class="collapse"/>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="panel-group" id="accordion2">',
|
||||
'<div class="panel">',
|
||||
'<div id="collapse2" class="collapse show"/>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
$(html).appendTo('#qunit-fixture')
|
||||
var $el1 = $('#collapse1')
|
||||
var $el2 = $('#collapse2')
|
||||
$el1.one('shown.bs.collapse', function () {
|
||||
assert.ok($el1.hasClass('show'))
|
||||
assert.ok($el2.hasClass('show'))
|
||||
done()
|
||||
}).bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should hide a collapsed element', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div class="collapse"/>').bootstrapCollapse('hide')
|
||||
|
||||
assert.ok(!$el.hasClass('show'), 'does not have class "show"')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown when show is prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse"/>')
|
||||
.on('show.bs.collapse', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok(false, 'shown event fired')
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should reset style to auto after finishing opening collapse', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse" style="height: 0px"/>')
|
||||
.on('show.bs.collapse', function () {
|
||||
assert.strictEqual(this.style.height, '0px', 'height is 0px')
|
||||
})
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.strictEqual(this.style.height, '', 'height is auto')
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should reset style to auto after finishing closing collapse', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse"/>')
|
||||
.on('shown.bs.collapse', function () {
|
||||
$(this).bootstrapCollapse('hide')
|
||||
})
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.strictEqual(this.style.height, '', 'height is auto')
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should remove "collapsed" class from target when collapse is shown', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok(!$target.hasClass('collapsed'), 'target does not have collapsed class')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should add "collapsed" class to target when collapse is hidden', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.ok($target.hasClass('collapsed'), 'target has collapsed class')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should remove "collapsed" class from all triggers targeting the collapse when the collapse is shown', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
var $alt = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok(!$target.hasClass('collapsed'), 'target trigger does not have collapsed class')
|
||||
assert.ok(!$alt.hasClass('collapsed'), 'alt trigger does not have collapsed class')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should add "collapsed" class to all triggers targeting the collapse when the collapse is hidden', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
var $alt = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.ok($target.hasClass('collapsed'), 'target has collapsed class')
|
||||
assert.ok($alt.hasClass('collapsed'), 'alt trigger has collapsed class')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not close a collapse when initialized with "show" option if already shown', function (assert) {
|
||||
assert.expect(0)
|
||||
var done = assert.async()
|
||||
|
||||
var $test = $('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hide.bs.collapse', function () {
|
||||
assert.ok(false)
|
||||
})
|
||||
|
||||
$test.bootstrapCollapse('show')
|
||||
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
QUnit.test('should open a collapse when initialized with "show" option if not already shown', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $test = $('<div id="test1" />')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('show.bs.collapse', function () {
|
||||
assert.ok(true)
|
||||
})
|
||||
|
||||
$test.bootstrapCollapse('show')
|
||||
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
QUnit.test('should not show a collapse when initialized with "hide" option if already hidden', function (assert) {
|
||||
assert.expect(0)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse"></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('show.bs.collapse', function () {
|
||||
assert.ok(false, 'showing a previously-uninitialized hidden collapse when the "hide" method is called')
|
||||
})
|
||||
.bootstrapCollapse('hide')
|
||||
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
QUnit.test('should hide a collapse when initialized with "hide" option if not already hidden', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse show"></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hide.bs.collapse', function () {
|
||||
assert.ok(true, 'hiding a previously-uninitialized shown collapse when the "hide" method is called')
|
||||
})
|
||||
.bootstrapCollapse('hide')
|
||||
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
QUnit.test('should remove "collapsed" class from active accordion target', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'</div>'
|
||||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card')
|
||||
|
||||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1" />').appendTo($groups.eq(0))
|
||||
|
||||
$('<div id="body1" class="show" data-parent="#accordion"/>').appendTo($groups.eq(0))
|
||||
|
||||
var $target2 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body2" />').appendTo($groups.eq(1))
|
||||
|
||||
$('<div id="body2" data-parent="#accordion"/>').appendTo($groups.eq(1))
|
||||
|
||||
var $target3 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body3" />').appendTo($groups.eq(2))
|
||||
|
||||
$('<div id="body3" data-parent="#accordion"/>')
|
||||
.appendTo($groups.eq(2))
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"')
|
||||
assert.ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
|
||||
assert.ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should allow dots in data-parent', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div class="accordion">' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'</div>'
|
||||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card')
|
||||
|
||||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1"/>').appendTo($groups.eq(0))
|
||||
|
||||
$('<div id="body1" class="show" data-parent=".accordion"/>').appendTo($groups.eq(0))
|
||||
|
||||
var $target2 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body2"/>').appendTo($groups.eq(1))
|
||||
|
||||
$('<div id="body2" data-parent=".accordion"/>').appendTo($groups.eq(1))
|
||||
|
||||
var $target3 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body3"/>').appendTo($groups.eq(2))
|
||||
|
||||
$('<div id="body3" data-parent=".accordion"/>')
|
||||
.appendTo($groups.eq(2))
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"')
|
||||
assert.ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
|
||||
assert.ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should set aria-expanded="true" on trigger/control when collapse is shown', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.strictEqual($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should set aria-expanded="false" on trigger/control when collapse is hidden', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.strictEqual($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should set aria-expanded="true" on all triggers targeting the collapse when the collapse is shown', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture')
|
||||
var $alt = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.strictEqual($target.attr('aria-expanded'), 'true', 'aria-expanded on trigger/control is "true"')
|
||||
assert.strictEqual($alt.attr('aria-expanded'), 'true', 'aria-expanded on alternative trigger/control is "true"')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should set aria-expanded="false" on all triggers targeting the collapse when the collapse is hidden', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture')
|
||||
var $alt = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.strictEqual($target.attr('aria-expanded'), 'false', 'aria-expanded on trigger/control is "false"')
|
||||
assert.strictEqual($alt.attr('aria-expanded'), 'false', 'aria-expanded on alternative trigger/control is "false"')
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should change aria-expanded from active accordion trigger/control to "false" and set the trigger/control for the newly active one to "true"', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'</div>'
|
||||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card')
|
||||
|
||||
var $target1 = $('<a role="button" data-toggle="collapse" aria-expanded="true" href="#body1"/>').appendTo($groups.eq(0))
|
||||
|
||||
$('<div id="body1" class="show" data-parent="#accordion"/>').appendTo($groups.eq(0))
|
||||
|
||||
var $target2 = $('<a role="button" data-toggle="collapse" aria-expanded="false" href="#body2" class="collapsed" aria-expanded="false" />').appendTo($groups.eq(1))
|
||||
|
||||
$('<div id="body2" data-parent="#accordion"/>').appendTo($groups.eq(1))
|
||||
|
||||
var $target3 = $('<a class="collapsed" data-toggle="collapse" aria-expanded="false" role="button" href="#body3"/>').appendTo($groups.eq(2))
|
||||
|
||||
$('<div id="body3" data-parent="#accordion"/>')
|
||||
.appendTo($groups.eq(2))
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.strictEqual($target1.attr('aria-expanded'), 'false', 'inactive trigger/control 1 has aria-expanded="false"')
|
||||
assert.strictEqual($target2.attr('aria-expanded'), 'false', 'inactive trigger/control 2 has aria-expanded="false"')
|
||||
assert.strictEqual($target3.attr('aria-expanded'), 'true', 'active trigger/control 3 has aria-expanded="true"')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire show event if show is prevented because other element is still transitioning', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<div class="card"/>' +
|
||||
'<div class="card"/>' +
|
||||
'</div>'
|
||||
var showFired = false
|
||||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card')
|
||||
|
||||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1"/>').appendTo($groups.eq(0))
|
||||
|
||||
$('<div id="body1" class="collapse" data-parent="#accordion"/>')
|
||||
.appendTo($groups.eq(0))
|
||||
.on('show.bs.collapse', function () {
|
||||
showFired = true
|
||||
})
|
||||
|
||||
var $target2 = $('<a role="button" data-toggle="collapse" href="#body2"/>').appendTo($groups.eq(1))
|
||||
var $body2 = $('<div id="body2" class="collapse" data-parent="#accordion"/>').appendTo($groups.eq(1))
|
||||
|
||||
$target2.trigger('click')
|
||||
|
||||
$body2
|
||||
.toggleClass('show collapsing')
|
||||
.data('bs.collapse')._isTransitioning = 1
|
||||
|
||||
$target1.trigger('click')
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!showFired, 'show event did not fire')
|
||||
done()
|
||||
}, 1)
|
||||
})
|
||||
|
||||
QUnit.test('should add "collapsed" class to target when collapse is hidden via manual invocation', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1" class="show"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
assert.ok($target.hasClass('collapsed'))
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('hide')
|
||||
})
|
||||
|
||||
QUnit.test('should remove "collapsed" class from target when collapse is shown via manual invocation', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="test1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok(!$target.hasClass('collapsed'))
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
QUnit.test('should allow accordion to use children other than card', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTrigger" data-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>' +
|
||||
'<div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-parent="#accordion"></div>' +
|
||||
'</div>' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTriggerTwo" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>' +
|
||||
'<div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-parent="#accordion"></div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(accordionHTML).appendTo('#qunit-fixture')
|
||||
var $trigger = $('#linkTrigger')
|
||||
var $triggerTwo = $('#linkTriggerTwo')
|
||||
var $collapseOne = $('#collapseOne')
|
||||
var $collapseTwo = $('#collapseTwo')
|
||||
$collapseOne.on('shown.bs.collapse', function () {
|
||||
assert.ok($collapseOne.hasClass('show'), '#collapseOne is shown')
|
||||
assert.ok(!$collapseTwo.hasClass('show'), '#collapseTwo is not shown')
|
||||
$collapseTwo.on('shown.bs.collapse', function () {
|
||||
assert.ok(!$collapseOne.hasClass('show'), '#collapseOne is not shown')
|
||||
assert.ok($collapseTwo.hasClass('show'), '#collapseTwo is shown')
|
||||
done()
|
||||
})
|
||||
$triggerTwo.trigger($.Event('click'))
|
||||
})
|
||||
$trigger.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should allow accordion to contain nested elements', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<div class="row">' +
|
||||
'<div class="col-lg-6">' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTrigger" data-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>' +
|
||||
'<div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-parent="#accordion"></div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="col-lg-6">' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTriggerTwo" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>' +
|
||||
'<div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-parent="#accordion"></div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(accordionHTML).appendTo('#qunit-fixture')
|
||||
var $trigger = $('#linkTrigger')
|
||||
var $triggerTwo = $('#linkTriggerTwo')
|
||||
var $collapseOne = $('#collapseOne')
|
||||
var $collapseTwo = $('#collapseTwo')
|
||||
$collapseOne.on('shown.bs.collapse', function () {
|
||||
assert.ok($collapseOne.hasClass('show'), '#collapseOne is shown')
|
||||
assert.ok(!$collapseTwo.hasClass('show'), '#collapseTwo is not shown')
|
||||
$collapseTwo.on('shown.bs.collapse', function () {
|
||||
assert.ok(!$collapseOne.hasClass('show'), '#collapseOne is not shown')
|
||||
assert.ok($collapseTwo.hasClass('show'), '#collapseTwo is shown')
|
||||
done()
|
||||
})
|
||||
$triggerTwo.trigger($.Event('click'))
|
||||
})
|
||||
$trigger.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should allow accordion to target multiple elements', function (assert) {
|
||||
assert.expect(8)
|
||||
var done = assert.async()
|
||||
var accordionHTML = '<div id="accordion">' +
|
||||
'<a id="linkTriggerOne" data-toggle="collapse" data-target=".collapseOne" href="#" aria-expanded="false" aria-controls="collapseOne"></a>' +
|
||||
'<a id="linkTriggerTwo" data-toggle="collapse" data-target=".collapseTwo" href="#" aria-expanded="false" aria-controls="collapseTwo"></a>' +
|
||||
'<div id="collapseOneOne" class="collapse collapseOne" role="tabpanel" data-parent="#accordion"></div>' +
|
||||
'<div id="collapseOneTwo" class="collapse collapseOne" role="tabpanel" data-parent="#accordion"></div>' +
|
||||
'<div id="collapseTwoOne" class="collapse collapseTwo" role="tabpanel" data-parent="#accordion"></div>' +
|
||||
'<div id="collapseTwoTwo" class="collapse collapseTwo" role="tabpanel" data-parent="#accordion"></div>' +
|
||||
'</div>'
|
||||
|
||||
$(accordionHTML).appendTo('#qunit-fixture')
|
||||
var $trigger = $('#linkTriggerOne')
|
||||
var $triggerTwo = $('#linkTriggerTwo')
|
||||
var $collapseOneOne = $('#collapseOneOne')
|
||||
var $collapseOneTwo = $('#collapseOneTwo')
|
||||
var $collapseTwoOne = $('#collapseTwoOne')
|
||||
var $collapseTwoTwo = $('#collapseTwoTwo')
|
||||
var collapsedElements = {
|
||||
one : false,
|
||||
two : false
|
||||
}
|
||||
|
||||
function firstTest() {
|
||||
assert.ok($collapseOneOne.hasClass('show'), '#collapseOneOne is shown')
|
||||
assert.ok($collapseOneTwo.hasClass('show'), '#collapseOneTwo is shown')
|
||||
assert.ok(!$collapseTwoOne.hasClass('show'), '#collapseTwoOne is not shown')
|
||||
assert.ok(!$collapseTwoTwo.hasClass('show'), '#collapseTwoTwo is not shown')
|
||||
$triggerTwo.trigger($.Event('click'))
|
||||
}
|
||||
|
||||
function secondTest() {
|
||||
assert.ok(!$collapseOneOne.hasClass('show'), '#collapseOneOne is not shown')
|
||||
assert.ok(!$collapseOneTwo.hasClass('show'), '#collapseOneTwo is not shown')
|
||||
assert.ok($collapseTwoOne.hasClass('show'), '#collapseTwoOne is shown')
|
||||
assert.ok($collapseTwoTwo.hasClass('show'), '#collapseTwoTwo is shown')
|
||||
done()
|
||||
}
|
||||
|
||||
$collapseOneOne.on('shown.bs.collapse', function () {
|
||||
if (collapsedElements.one) {
|
||||
firstTest()
|
||||
} else {
|
||||
collapsedElements.one = true
|
||||
}
|
||||
})
|
||||
|
||||
$collapseOneTwo.on('shown.bs.collapse', function () {
|
||||
if (collapsedElements.one) {
|
||||
firstTest()
|
||||
} else {
|
||||
collapsedElements.one = true
|
||||
}
|
||||
})
|
||||
|
||||
$collapseTwoOne.on('shown.bs.collapse', function () {
|
||||
if (collapsedElements.two) {
|
||||
secondTest()
|
||||
} else {
|
||||
collapsedElements.two = true
|
||||
}
|
||||
})
|
||||
|
||||
$collapseTwoTwo.on('shown.bs.collapse', function () {
|
||||
if (collapsedElements.two) {
|
||||
secondTest()
|
||||
} else {
|
||||
collapsedElements.two = true
|
||||
}
|
||||
})
|
||||
|
||||
$trigger.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should collapse accordion children but not nested accordion children', function (assert) {
|
||||
assert.expect(9)
|
||||
var done = assert.async()
|
||||
$('<div id="accordion">' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTrigger" data-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>' +
|
||||
'<div id="collapseOne" data-parent="#accordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">' +
|
||||
'<div id="nestedAccordion">' +
|
||||
'<div class="item">' +
|
||||
'<a id="nestedLinkTrigger" data-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>' +
|
||||
'<div id="nestedCollapseOne" data-parent="#nestedAccordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="item">' +
|
||||
'<a id="linkTriggerTwo" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>' +
|
||||
'<div id="collapseTwo" data-parent="#accordion" class="collapse show" role="tabpanel" aria-labelledby="headingTwo"></div>' +
|
||||
'</div>' +
|
||||
'</div>').appendTo('#qunit-fixture')
|
||||
var $trigger = $('#linkTrigger')
|
||||
var $triggerTwo = $('#linkTriggerTwo')
|
||||
var $nestedTrigger = $('#nestedLinkTrigger')
|
||||
var $collapseOne = $('#collapseOne')
|
||||
var $collapseTwo = $('#collapseTwo')
|
||||
var $nestedCollapseOne = $('#nestedCollapseOne')
|
||||
|
||||
$collapseOne.one('shown.bs.collapse', function () {
|
||||
assert.ok($collapseOne.hasClass('show'), '#collapseOne is shown')
|
||||
assert.ok(!$collapseTwo.hasClass('show'), '#collapseTwo is not shown')
|
||||
assert.ok(!$('#nestedCollapseOne').hasClass('show'), '#nestedCollapseOne is not shown')
|
||||
$nestedCollapseOne.one('shown.bs.collapse', function () {
|
||||
assert.ok($collapseOne.hasClass('show'), '#collapseOne is shown')
|
||||
assert.ok(!$collapseTwo.hasClass('show'), '#collapseTwo is not shown')
|
||||
assert.ok($nestedCollapseOne.hasClass('show'), '#nestedCollapseOne is shown')
|
||||
$collapseTwo.one('shown.bs.collapse', function () {
|
||||
assert.ok(!$collapseOne.hasClass('show'), '#collapseOne is not shown')
|
||||
assert.ok($collapseTwo.hasClass('show'), '#collapseTwo is shown')
|
||||
assert.ok($nestedCollapseOne.hasClass('show'), '#nestedCollapseOne is shown')
|
||||
done()
|
||||
})
|
||||
$triggerTwo.trigger($.Event('click'))
|
||||
})
|
||||
$nestedTrigger.trigger($.Event('click'))
|
||||
})
|
||||
$trigger.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should not prevent event for input', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
var $target = $('<input type="checkbox" data-toggle="collapse" data-target="#collapsediv1" />').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="collapsediv1"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok($(this).hasClass('show'))
|
||||
assert.ok($target.attr('aria-expanded') === 'true')
|
||||
assert.ok($target.prop('checked'))
|
||||
done()
|
||||
})
|
||||
|
||||
$target.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should add "collapsed" class to triggers only when all the targeted collapse are hidden', function (assert) {
|
||||
assert.expect(9)
|
||||
var done = assert.async()
|
||||
|
||||
var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture')
|
||||
var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture')
|
||||
|
||||
var $target1 = $('<div id="test1" class="multi"/>').appendTo('#qunit-fixture')
|
||||
var $target2 = $('<div id="test2" class="multi"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$target2.one('shown.bs.collapse', function () {
|
||||
assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class')
|
||||
assert.ok(!$trigger2.hasClass('collapsed'), 'trigger2 does not have collapsed class')
|
||||
assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class')
|
||||
$target2.one('hidden.bs.collapse', function () {
|
||||
assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class')
|
||||
assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class')
|
||||
assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class')
|
||||
$target1.one('hidden.bs.collapse', function () {
|
||||
assert.ok($trigger1.hasClass('collapsed'), 'trigger1 has collapsed class')
|
||||
assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class')
|
||||
assert.ok($trigger3.hasClass('collapsed'), 'trigger3 has collapsed class')
|
||||
done()
|
||||
})
|
||||
$trigger1.trigger('click')
|
||||
})
|
||||
$trigger2.trigger('click')
|
||||
})
|
||||
$trigger3.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should set aria-expanded="true" to triggers targeting shown collaspe and aria-expanded="false" only when all the targeted collapses are shown', function (assert) {
|
||||
assert.expect(9)
|
||||
var done = assert.async()
|
||||
|
||||
var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture')
|
||||
var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture')
|
||||
|
||||
var $target1 = $('<div id="test1" class="multi collapse"/>').appendTo('#qunit-fixture')
|
||||
var $target2 = $('<div id="test2" class="multi collapse"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$target2.one('shown.bs.collapse', function () {
|
||||
assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"')
|
||||
assert.strictEqual($trigger2.attr('aria-expanded'), 'true', 'aria-expanded on trigger2 is "true"')
|
||||
assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"')
|
||||
$target2.one('hidden.bs.collapse', function () {
|
||||
assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"')
|
||||
assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"')
|
||||
assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"')
|
||||
$target1.one('hidden.bs.collapse', function () {
|
||||
assert.strictEqual($trigger1.attr('aria-expanded'), 'false', 'aria-expanded on trigger1 is "fasle"')
|
||||
assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"')
|
||||
assert.strictEqual($trigger3.attr('aria-expanded'), 'false', 'aria-expanded on trigger3 is "false"')
|
||||
done()
|
||||
})
|
||||
$trigger1.trigger('click')
|
||||
})
|
||||
$trigger2.trigger('click')
|
||||
})
|
||||
$trigger3.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not prevent interactions inside the collapse element', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<input type="checkbox" data-toggle="collapse" data-target="#collapsediv1" />').appendTo('#qunit-fixture')
|
||||
var htmlCollapse =
|
||||
'<div id="collapsediv1" class="collapse">' +
|
||||
' <input type="checkbox" id="testCheckbox" />' +
|
||||
'</div>'
|
||||
|
||||
$(htmlCollapse)
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
assert.ok($target.prop('checked'), '$trigger is checked')
|
||||
var $testCheckbox = $('#testCheckbox')
|
||||
$testCheckbox.trigger($.Event('click'))
|
||||
setTimeout(function () {
|
||||
assert.ok($testCheckbox.prop('checked'), '$testCheckbox is checked too')
|
||||
done()
|
||||
}, 5)
|
||||
})
|
||||
|
||||
$target.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should allow jquery object in parent config', function (assert) {
|
||||
assert.expect(1)
|
||||
var html =
|
||||
'<div class="my-collapse">' +
|
||||
' <div class="item">' +
|
||||
' <a data-toggle="collapse" href="#">Toggle item</a>' +
|
||||
' <div class="collapse">Lorem ipsum</div>' +
|
||||
' </div>' +
|
||||
'</div>'
|
||||
|
||||
$(html).appendTo('#qunit-fixture')
|
||||
try {
|
||||
$('[data-toggle="collapse"]').bootstrapCollapse({
|
||||
parent: $('.my-collapse')
|
||||
})
|
||||
assert.ok(true, 'collapse correctly created')
|
||||
} catch (err) {
|
||||
assert.ok(false, 'collapse not created')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should allow DOM object in parent config', function (assert) {
|
||||
assert.expect(1)
|
||||
var html =
|
||||
'<div class="my-collapse">' +
|
||||
' <div class="item">' +
|
||||
' <a data-toggle="collapse" href="#">Toggle item</a>' +
|
||||
' <div class="collapse">Lorem ipsum</div>' +
|
||||
' </div>' +
|
||||
'</div>'
|
||||
|
||||
$(html).appendTo('#qunit-fixture')
|
||||
try {
|
||||
$('[data-toggle="collapse"]').bootstrapCollapse({
|
||||
parent: $('.my-collapse')[0]
|
||||
})
|
||||
assert.ok(true, 'collapse correctly created')
|
||||
} catch (err) {
|
||||
assert.ok(false, 'collapse not created')
|
||||
}
|
||||
})
|
||||
})
|
1124
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/dropdown.js
vendored
Normal file
1124
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/dropdown.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
657
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/modal.js
vendored
Normal file
657
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/modal.js
vendored
Normal file
@ -0,0 +1,657 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('modal plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).modal, 'modal method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('modal', {
|
||||
before: function () {
|
||||
// Enable the scrollbar measurer
|
||||
$('<style type="text/css"> .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } </style>').appendTo('head')
|
||||
// Function to calculate the scrollbar width which is then compared to the padding or margin changes
|
||||
$.fn.getScrollbarWidth = function () {
|
||||
var scrollDiv = document.createElement('div')
|
||||
scrollDiv.className = 'modal-scrollbar-measure'
|
||||
document.body.appendChild(scrollDiv)
|
||||
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
|
||||
document.body.removeChild(scrollDiv)
|
||||
return scrollbarWidth
|
||||
}
|
||||
|
||||
// Simulate scrollbars
|
||||
$('html').css('padding-right', '16px')
|
||||
},
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapModal = $.fn.modal.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$('.modal-backdrop, #modal-test').remove()
|
||||
$(document.body).removeClass('modal-open')
|
||||
$.fn.modal = $.fn.bootstrapModal
|
||||
delete $.fn.bootstrapModal
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.modal, 'undefined', 'modal was set back to undefined (orig value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div id="modal-test"/>')
|
||||
$el.bootstrapModal()
|
||||
try {
|
||||
$el.bootstrapModal('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div id="modal-test"/>')
|
||||
var $modal = $el.bootstrapModal()
|
||||
assert.ok($modal instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($modal[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should expose defaults var for settings', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($.fn.bootstrapModal.Constructor.Default, 'default object exposed')
|
||||
})
|
||||
|
||||
QUnit.test('should insert into dom when show method is called', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire show event', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('show.bs.modal', function () {
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown when show was prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('show.bs.modal', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok(false, 'shown event fired')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should hide modal when hide is called', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should toggle when toggle is called', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
$(this).bootstrapModal('toggle')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('toggle')
|
||||
})
|
||||
|
||||
QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
$(this).find('.close').trigger('click')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('toggle')
|
||||
})
|
||||
|
||||
QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test" data-backdrop="false"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><div class="contents"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
$('.contents').trigger('click')
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
$('#modal-test').trigger('click')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not close modal when clicking outside of modal-content if data-backdrop="true"', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test" data-backdrop="false"><div class="contents"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
$('#modal-test').trigger('click')
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal not hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var $div = $('<div id="modal-test"/>')
|
||||
$div
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').length, 'modal inserted into dom')
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
$div.trigger($.Event('keydown', {
|
||||
which: 27
|
||||
}))
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
$div.remove()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var $div = $('<div id="modal-test"/>')
|
||||
$div
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.ok($('#modal-test').length, 'modal inserted into dom')
|
||||
assert.ok($('#modal-test').is(':visible'), 'modal visible')
|
||||
$div.trigger($.Event('keyup', {
|
||||
which: 27
|
||||
}))
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($div.is(':visible'), 'modal still visible')
|
||||
$div.remove()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var triggered
|
||||
|
||||
$('<div id="modal-test"><div class="contents"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
triggered = 0
|
||||
$('#modal-test').trigger('click')
|
||||
})
|
||||
.on('hide.bs.modal', function () {
|
||||
triggered += 1
|
||||
assert.strictEqual(triggered, 1, 'modal hide triggered once')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should remove aria-hidden attribute when shown, add it back when hidden', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test" aria-hidden="true"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.notOk($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute removed')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.ok($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute added')
|
||||
assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'correct aria-hidden="true" added')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
|
||||
.one('shown.bs.modal', function () {
|
||||
$('#close').trigger('click')
|
||||
})
|
||||
.one('hidden.bs.modal', function () {
|
||||
// After one open-close cycle
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
$(this)
|
||||
.one('shown.bs.modal', function () {
|
||||
$('#close').trigger('click')
|
||||
})
|
||||
.one('hidden.bs.modal', function () {
|
||||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
setTimeout(function () {
|
||||
assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
$('#close').trigger('click')
|
||||
})
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$toggleBtn.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
|
||||
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div>')
|
||||
.one('show.bs.modal', function (e) {
|
||||
e.preventDefault()
|
||||
$otherBtn.trigger('focus')
|
||||
setTimeout($.proxy(function () {
|
||||
$(this).bootstrapModal('show')
|
||||
}, this), 0)
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
setTimeout(function () {
|
||||
assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
$('#close').trigger('click')
|
||||
})
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$toggleBtn.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should adjust the inline padding of the modal when opening', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
var expectedPadding = $(this).getScrollbarWidth() + 'px'
|
||||
var currentPadding = $(this).css('padding-right')
|
||||
assert.strictEqual(currentPadding, expectedPadding, 'modal padding should be adjusted while opening')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
var originalPadding = $body.css('padding-right')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
var currentPadding = $body.css('padding-right')
|
||||
assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing')
|
||||
$body.removeAttr('style')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
|
||||
var currentPadding = $body.css('padding-right')
|
||||
assert.strictEqual(currentPadding, expectedPadding, 'body padding should be adjusted while opening')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
var originalPadding = '0px'
|
||||
$body.css('padding-right', originalPadding)
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual(typeof $body.data('padding-right'), 'undefined', 'data-padding-right should be cleared after closing')
|
||||
$body.removeAttr('style')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not adjust the inline body padding when it does not overflow', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
var originalPadding = $body.css('padding-right')
|
||||
|
||||
// Hide scrollbars to prevent the body overflowing
|
||||
$body.css('overflow', 'hidden') // Real scrollbar (for in-browser testing)
|
||||
$('html').css('padding-right', '0px') // Simulated scrollbar (for PhantomJS)
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
var currentPadding = $body.css('padding-right')
|
||||
assert.strictEqual(currentPadding, originalPadding, 'body padding should not be adjusted')
|
||||
$(this).bootstrapModal('hide')
|
||||
|
||||
// Restore scrollbars
|
||||
$body.css('overflow', 'auto')
|
||||
$('html').css('padding-right', '16px')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
|
||||
var originalPadding = $element.css('padding-right')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
var currentPadding = $element.css('padding-right')
|
||||
assert.strictEqual(currentPadding, originalPadding, 'fixed element padding should be reset after closing')
|
||||
$element.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
|
||||
var currentPadding = $element.css('padding-right')
|
||||
assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should store the original padding of fixed elements in data-padding-right before showing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
|
||||
var originalPadding = '0px'
|
||||
$element.css('padding-right', originalPadding)
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual(typeof $element.data('padding-right'), 'undefined', 'data-padding-right should be cleared after closing')
|
||||
$element.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.strictEqual($element.data('padding-right'), originalPadding, 'original fixed element padding should be stored in data-padding-right')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should adjust the inline margin of sticky elements when opening and restore when closing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
|
||||
var originalPadding = $element.css('margin-right')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
var currentPadding = $element.css('margin-right')
|
||||
assert.strictEqual(currentPadding, originalPadding, 'sticky element margin should be reset after closing')
|
||||
$element.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
var expectedPadding = parseFloat(originalPadding) - $(this).getScrollbarWidth() + 'px'
|
||||
var currentPadding = $element.css('margin-right')
|
||||
assert.strictEqual(currentPadding, expectedPadding, 'sticky element margin should be adjusted while opening')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should store the original margin of sticky elements in data-margin-right before showing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
|
||||
var originalPadding = '0px'
|
||||
$element.css('margin-right', originalPadding)
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual(typeof $element.data('margin-right'), 'undefined', 'data-margin-right should be cleared after closing')
|
||||
$element.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
assert.strictEqual($element.data('margin-right'), originalPadding, 'original sticky element margin should be stored in data-margin-right')
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should ignore values set via CSS when trying to restore body padding after closing', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual($body.attr('style').indexOf('padding-right'), -1, 'body does not have inline padding set')
|
||||
$style.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should ignore other inline styles when trying to restore body padding after closing', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
|
||||
|
||||
$body.css('color', 'red')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual($body[0].style.paddingRight, '', 'body does not have inline padding set')
|
||||
assert.strictEqual($body[0].style.color, 'red', 'body still has other inline styles set')
|
||||
$body.removeAttr('style')
|
||||
$style.remove()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should properly restore non-pixel inline body padding after closing', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var $body = $(document.body)
|
||||
|
||||
$body.css('padding-right', '5%')
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('hidden.bs.modal', function () {
|
||||
assert.strictEqual($body[0].style.paddingRight, '5%', 'body does not have inline padding set')
|
||||
$body.removeAttr('style')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
$(this).bootstrapModal('hide')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not follow link in area tag', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<map><area id="test" shape="default" data-toggle="modal" data-target="#modal-test" href="demo.html"/></map>')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$('#test')
|
||||
.on('click.bs.modal.data-api', function (event) {
|
||||
assert.notOk(event.isDefaultPrevented(), 'navigating to href will happen')
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(event.isDefaultPrevented(), 'model shown instead of navigating to href')
|
||||
done()
|
||||
}, 1)
|
||||
})
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should not parse target as html', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="<div id="modal-test"><div class="contents"<div<div id="close" data-dismiss="modal"/></div></div>"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$toggleBtn.trigger('click')
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($('#modal-test').length, 0, 'target has not been parsed and added to the document')
|
||||
done()
|
||||
}, 1)
|
||||
})
|
||||
|
||||
QUnit.test('should not execute js from target', function (assert) {
|
||||
assert.expect(0)
|
||||
var done = assert.async()
|
||||
|
||||
// This toggle button contains XSS payload in its data-target
|
||||
// Note: it uses the onerror handler of an img element to execute the js, because a simple script element does not work here
|
||||
// a script element works in manual tests though, so here it is likely blocked by the qunit framework
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="<div><image src="missing.png" onerror="$('#qunit-fixture button.control').trigger('click')"></div>"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
// The XSS payload above does not have a closure over this function and cannot access the assert object directly
|
||||
// However, it can send a click event to the following control button, which will then fail the assert
|
||||
$('<button>')
|
||||
.addClass('control')
|
||||
.on('click', function () {
|
||||
assert.notOk(true, 'XSS payload is not executed as js')
|
||||
})
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$toggleBtn.trigger('click')
|
||||
setTimeout(done, 500)
|
||||
})
|
||||
|
||||
QUnit.test('should not try to open a modal which is already visible', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var count = 0
|
||||
|
||||
$('<div id="modal-test"/>').on('shown.bs.modal', function () {
|
||||
count++
|
||||
}).on('hidden.bs.modal', function () {
|
||||
assert.strictEqual(count, 1, 'show() runs only once')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
.bootstrapModal('show')
|
||||
.bootstrapModal('hide')
|
||||
})
|
||||
})
|
471
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/popover.js
vendored
Normal file
471
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/popover.js
vendored
Normal file
@ -0,0 +1,471 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('popover plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).popover, 'popover method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('popover', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapPopover = $.fn.popover.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.popover = $.fn.bootstrapPopover
|
||||
delete $.fn.bootstrapPopover
|
||||
$('.popover').remove()
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.popover, 'undefined', 'popover was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>')
|
||||
$el.bootstrapPopover()
|
||||
try {
|
||||
$el.bootstrapPopover('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $popover = $el.bootstrapPopover()
|
||||
assert.ok($popover instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($popover[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should render popover element', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
$('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
$(this).bootstrapPopover('hide')
|
||||
})
|
||||
.on('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should store popover instance in popover data object', function (assert) {
|
||||
assert.expect(1)
|
||||
var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>').bootstrapPopover()
|
||||
|
||||
assert.ok($popover.data('bs.popover'), 'popover instance exists')
|
||||
})
|
||||
|
||||
QUnit.test('should store popover trigger in popover instance data object', function (assert) {
|
||||
assert.expect(1)
|
||||
var $popover = $('<a href="#" title="ResentedHook">@ResentedHook</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover()
|
||||
|
||||
$popover.bootstrapPopover('show')
|
||||
|
||||
assert.ok($('.popover').data('bs.popover'), 'popover trigger stored in instance data')
|
||||
})
|
||||
|
||||
QUnit.test('should get title and content from options', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#">@fat</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: function () {
|
||||
return '@fat'
|
||||
},
|
||||
content: function () {
|
||||
return 'loves writing tests (╯°□°)╯︵ ┻━┻'
|
||||
}
|
||||
})
|
||||
|
||||
$popover
|
||||
.one('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.strictEqual($('.popover .popover-header').text(), '@fat', 'title correctly inserted')
|
||||
assert.strictEqual($('.popover .popover-body').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted')
|
||||
$popover.bootstrapPopover('hide')
|
||||
})
|
||||
.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should allow DOMElement title and content (html: true)', function (assert) {
|
||||
assert.expect(5)
|
||||
var title = document.createTextNode('@glebm <3 writing tests')
|
||||
var content = $('<i>¯\\_(ツ)_/¯</i>').get(0)
|
||||
var $popover = $('<a href="#" rel="tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
html: true,
|
||||
title: title,
|
||||
content: content
|
||||
})
|
||||
|
||||
$popover.bootstrapPopover('show')
|
||||
|
||||
assert.notEqual($('.popover').length, 0, 'popover inserted')
|
||||
assert.strictEqual($('.popover .popover-header').text(), '@glebm <3 writing tests', 'title inserted')
|
||||
assert.ok($.contains($('.popover').get(0), title), 'title node moved, not copied')
|
||||
// toLowerCase because IE8 will return <I>...</I>
|
||||
assert.strictEqual($('.popover .popover-body').html().toLowerCase(), '<i>¯\\_(ツ)_/¯</i>', 'content inserted')
|
||||
assert.ok($.contains($('.popover').get(0), content), 'content node moved, not copied')
|
||||
})
|
||||
|
||||
QUnit.test('should allow DOMElement title and content (html: false)', function (assert) {
|
||||
assert.expect(5)
|
||||
var title = document.createTextNode('@glebm <3 writing tests')
|
||||
var content = $('<i>¯\\_(ツ)_/¯</i>').get(0)
|
||||
var $popover = $('<a href="#" rel="tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: title,
|
||||
content: content
|
||||
})
|
||||
|
||||
$popover.bootstrapPopover('show')
|
||||
|
||||
assert.notEqual($('.popover').length, 0, 'popover inserted')
|
||||
assert.strictEqual($('.popover .popover-header').text(), '@glebm <3 writing tests', 'title inserted')
|
||||
assert.ok(!$.contains($('.popover').get(0), title), 'title node copied, not moved')
|
||||
assert.strictEqual($('.popover .popover-body').html(), '¯\\_(ツ)_/¯', 'content inserted')
|
||||
assert.ok(!$.contains($('.popover').get(0), content), 'content node copied, not moved')
|
||||
})
|
||||
|
||||
QUnit.test('should not duplicate HTML object', function (assert) {
|
||||
assert.expect(6)
|
||||
var done = assert.async()
|
||||
var $div = $('<div/>').html('loves writing tests (╯°□°)╯︵ ┻━┻')
|
||||
|
||||
var $popover = $('<a href="#">@fat</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
html: true,
|
||||
content: function () {
|
||||
return $div
|
||||
}
|
||||
})
|
||||
|
||||
function popoverInserted() {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.equal($('.popover .popover-body').html(), $div[0].outerHTML, 'content correctly inserted')
|
||||
}
|
||||
|
||||
$popover
|
||||
.one('shown.bs.popover', function () {
|
||||
popoverInserted()
|
||||
|
||||
$popover.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
|
||||
$popover.one('shown.bs.popover', function () {
|
||||
popoverInserted()
|
||||
|
||||
$popover.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
}).bootstrapPopover('hide')
|
||||
}).bootstrapPopover('show')
|
||||
}).bootstrapPopover('hide')
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should get title and content from attributes', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover()
|
||||
.one('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.strictEqual($('.popover .popover-header').text(), '@mdo', 'title correctly inserted')
|
||||
assert.strictEqual($('.popover .popover-body').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted')
|
||||
$popover.bootstrapPopover('hide')
|
||||
})
|
||||
.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should get title and content from attributes ignoring options passed via js', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: 'ignored title option',
|
||||
content: 'ignored content option'
|
||||
})
|
||||
.one('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.strictEqual($('.popover .popover-header').text(), '@mdo', 'title correctly inserted')
|
||||
assert.strictEqual($('.popover .popover-body').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted')
|
||||
$popover.bootstrapPopover('hide')
|
||||
})
|
||||
.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should respect custom template', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#">@fat</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: 'Test',
|
||||
content: 'Test',
|
||||
template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>'
|
||||
})
|
||||
.one('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.ok($('.popover').hasClass('foobar'), 'custom class is present')
|
||||
$popover.bootstrapPopover('hide')
|
||||
})
|
||||
.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should destroy popover', function (assert) {
|
||||
assert.expect(7)
|
||||
var $popover = $('<div/>')
|
||||
.bootstrapPopover({
|
||||
trigger: 'hover'
|
||||
})
|
||||
.on('click.foo', $.noop)
|
||||
|
||||
assert.ok($popover.data('bs.popover'), 'popover has data')
|
||||
assert.ok($._data($popover[0], 'events').mouseover && $._data($popover[0], 'events').mouseout, 'popover has hover event')
|
||||
assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover has extra click.foo event')
|
||||
|
||||
$popover.bootstrapPopover('show')
|
||||
$popover.bootstrapPopover('dispose')
|
||||
|
||||
assert.ok(!$popover.hasClass('show'), 'popover is hidden')
|
||||
assert.ok(!$popover.data('popover'), 'popover does not have data')
|
||||
assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover still has click.foo')
|
||||
assert.ok(!$._data($popover[0], 'events').mouseover && !$._data($popover[0], 'events').mouseout, 'popover does not have any events')
|
||||
})
|
||||
|
||||
QUnit.test('should render popover element using delegated selector', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $div = $('<div><a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
selector: 'a',
|
||||
trigger: 'click'
|
||||
})
|
||||
.one('shown.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
$div.find('a').trigger('click')
|
||||
})
|
||||
.one('hidden.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').length, 0, 'popover was removed')
|
||||
done()
|
||||
})
|
||||
|
||||
$div.find('a').trigger('click')
|
||||
})
|
||||
|
||||
QUnit.test('should detach popover content rather than removing it so that event handlers are left intact', function (assert) {
|
||||
assert.expect(1)
|
||||
var $content = $('<div class="content-with-handler"><a class="btn btn-warning">Button with event handler</a></div>').appendTo('#qunit-fixture')
|
||||
|
||||
var handlerCalled = false
|
||||
$('.content-with-handler .btn').on('click', function () {
|
||||
handlerCalled = true
|
||||
})
|
||||
|
||||
var $div = $('<div><a href="#">Show popover</a></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
html: true,
|
||||
trigger: 'manual',
|
||||
container: 'body',
|
||||
animation: false,
|
||||
content: function () {
|
||||
return $content
|
||||
}
|
||||
})
|
||||
|
||||
var done = assert.async()
|
||||
$div
|
||||
.one('shown.bs.popover', function () {
|
||||
$div
|
||||
.one('hidden.bs.popover', function () {
|
||||
$div
|
||||
.one('shown.bs.popover', function () {
|
||||
$('.content-with-handler .btn').trigger('click')
|
||||
assert.ok(handlerCalled, 'content\'s event handler still present')
|
||||
$div.bootstrapPopover('dispose')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
.bootstrapPopover('hide')
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should do nothing when an attempt is made to hide an uninitialized popover', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
var $popover = $('<span data-toggle="popover" data-title="some title" data-content="some content">some text</span>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.popover shown.bs.popover', function () {
|
||||
assert.ok(false, 'should not fire any popover events')
|
||||
})
|
||||
.bootstrapPopover('hide')
|
||||
assert.strictEqual(typeof $popover.data('bs.popover'), 'undefined', 'should not initialize the popover')
|
||||
})
|
||||
|
||||
QUnit.test('should fire inserted event', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<a href="#">@Johann-S</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('inserted.bs.popover', function () {
|
||||
assert.notEqual($('.popover').length, 0, 'popover was inserted')
|
||||
assert.ok(true, 'inserted event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover({
|
||||
title: 'Test',
|
||||
content: 'Test'
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('should throw an error when show is called on hidden elements', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
try {
|
||||
$('<div data-toggle="popover" data-title="some title" data-content="@Johann-S" style="display: none"/>').bootstrapPopover('show')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'Please use show on visible elements')
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should hide popovers when their containing modal is closed', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var templateHTML = '<div id="modal-test" class="modal">' +
|
||||
'<div class="modal-dialog" role="document">' +
|
||||
'<div class="modal-content">' +
|
||||
'<div class="modal-body">' +
|
||||
'<button id="popover-test" type="button" class="btn btn-secondary" data-toggle="popover" data-placement="top" data-content="Popover">' +
|
||||
'Popover on top' +
|
||||
'</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(templateHTML).appendTo('#qunit-fixture')
|
||||
$('#popover-test')
|
||||
.on('shown.bs.popover', function () {
|
||||
$('#modal-test').modal('hide')
|
||||
})
|
||||
.on('hide.bs.popover', function () {
|
||||
assert.ok(true, 'popover hide')
|
||||
done()
|
||||
})
|
||||
|
||||
$('#modal-test')
|
||||
.on('shown.bs.modal', function () {
|
||||
$('#popover-test').bootstrapPopover('show')
|
||||
})
|
||||
.modal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should convert number to string without error for content and title', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#">@mdo</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: 5,
|
||||
content: 7
|
||||
})
|
||||
.on('shown.bs.popover', function () {
|
||||
assert.strictEqual($('.popover .popover-header').text(), '5')
|
||||
assert.strictEqual($('.popover .popover-body').text(), '7')
|
||||
done()
|
||||
})
|
||||
|
||||
$popover.bootstrapPopover('show')
|
||||
})
|
||||
|
||||
QUnit.test('popover should be shown right away after the call of disable/enable', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $popover = $('<a href="#">@mdo</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
title: 'Test popover',
|
||||
content: 'with disable/enable'
|
||||
})
|
||||
.on('shown.bs.popover', function () {
|
||||
assert.strictEqual($('.popover').hasClass('show'), true)
|
||||
done()
|
||||
})
|
||||
|
||||
$popover.bootstrapPopover('disable')
|
||||
$popover.trigger($.Event('click'))
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($('.popover').length === 0, true)
|
||||
$popover.bootstrapPopover('enable')
|
||||
$popover.trigger($.Event('click'))
|
||||
}, 200)
|
||||
})
|
||||
|
||||
QUnit.test('popover should call content function only once', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var nbCall = 0
|
||||
$('<div id="popover" style="display:none">content</div>').appendTo('#qunit-fixture')
|
||||
var $popover = $('<a href="#">@Johann-S</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapPopover({
|
||||
content: function () {
|
||||
nbCall++
|
||||
return $('#popover').clone().show().get(0)
|
||||
}
|
||||
})
|
||||
.on('shown.bs.popover', function () {
|
||||
assert.strictEqual(nbCall, 1)
|
||||
done()
|
||||
})
|
||||
|
||||
$popover.trigger($.Event('click'))
|
||||
})
|
||||
})
|
728
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/scrollspy.js
vendored
Normal file
728
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/scrollspy.js
vendored
Normal file
@ -0,0 +1,728 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('scrollspy plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).scrollspy, 'scrollspy method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('scrollspy', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapScrollspy = $.fn.scrollspy.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.scrollspy = $.fn.bootstrapScrollspy
|
||||
delete $.fn.bootstrapScrollspy
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.scrollspy, 'undefined', 'scrollspy was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>').appendTo('#qunit-fixture')
|
||||
$el.bootstrapScrollspy()
|
||||
try {
|
||||
$el.bootstrapScrollspy('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>').appendTo('#qunit-fixture')
|
||||
var $scrollspy = $el.bootstrapScrollspy()
|
||||
assert.ok($scrollspy instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($scrollspy[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should only switch "active" class on current target', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="root" class="active">' +
|
||||
'<div class="topbar">' +
|
||||
'<div class="topbar-inner">' +
|
||||
'<div class="container" id="ss-target">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a href="#masthead">Overview</a></li>' +
|
||||
'<li class="nav-item"><a href="#detail">Detail</a></li>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div id="scrollspy-example" style="height: 100px; overflow: auto;">' +
|
||||
'<div style="height: 200px;">' +
|
||||
'<h4 id="masthead">Overview</h4>' +
|
||||
'<p style="height: 200px">' +
|
||||
'Ad leggings keytar, brunch id art party dolor labore.' +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div style="height: 200px;">' +
|
||||
'<h4 id="detail">Detail</h4>' +
|
||||
'<p style="height: 200px">' +
|
||||
'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $section = $(sectionHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var $scrollspy = $section
|
||||
.show()
|
||||
.find('#scrollspy-example')
|
||||
.bootstrapScrollspy({
|
||||
target: '#ss-target'
|
||||
})
|
||||
|
||||
$scrollspy.one('scroll', function () {
|
||||
assert.ok($section.hasClass('active'), '"active" class still on root node')
|
||||
done()
|
||||
})
|
||||
|
||||
$scrollspy.scrollTop(350)
|
||||
})
|
||||
|
||||
QUnit.test('should only switch "active" class on current target specified w element', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="root" class="active">' +
|
||||
'<div class="topbar">' +
|
||||
'<div class="topbar-inner">' +
|
||||
'<div class="container" id="ss-target">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a href="#masthead">Overview</a></li>' +
|
||||
'<li class="nav-item"><a href="#detail">Detail</a></li>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div id="scrollspy-example" style="height: 100px; overflow: auto;">' +
|
||||
'<div style="height: 200px;">' +
|
||||
'<h4 id="masthead">Overview</h4>' +
|
||||
'<p style="height: 200px">' +
|
||||
'Ad leggings keytar, brunch id art party dolor labore.' +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div style="height: 200px;">' +
|
||||
'<h4 id="detail">Detail</h4>' +
|
||||
'<p style="height: 200px">' +
|
||||
'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
var $section = $(sectionHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var $scrollspy = $section
|
||||
.show()
|
||||
.find('#scrollspy-example')
|
||||
.bootstrapScrollspy({
|
||||
target: document.getElementById('#ss-target')
|
||||
})
|
||||
|
||||
$scrollspy.one('scroll', function () {
|
||||
assert.ok($section.hasClass('active'), '"active" class still on root node')
|
||||
done()
|
||||
})
|
||||
|
||||
$scrollspy.scrollTop(350)
|
||||
})
|
||||
|
||||
QUnit.test('should correctly select middle navigation option when large offset is used', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' +
|
||||
'<nav id="navigation" class="navbar">' +
|
||||
'<ul class="navbar-nav">' +
|
||||
'<li class="nav-item active"><a class="nav-link" id="one-link" href="#one">One</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>' +
|
||||
'<div id="content" style="height: 200px; overflow-y: auto;">' +
|
||||
'<div id="one" style="height: 500px;"></div>' +
|
||||
'<div id="two" style="height: 300px;"></div>' +
|
||||
'<div id="three" style="height: 10px;"></div>' +
|
||||
'</div>'
|
||||
var $section = $(sectionHTML).appendTo('#qunit-fixture')
|
||||
var $scrollspy = $section
|
||||
.show()
|
||||
.filter('#content')
|
||||
|
||||
$scrollspy.bootstrapScrollspy({
|
||||
target: '#navigation',
|
||||
offset: $scrollspy.position().top
|
||||
})
|
||||
|
||||
$scrollspy.one('scroll', function () {
|
||||
assert.ok(!$section.find('#one-link').hasClass('active'), '"active" class removed from first section')
|
||||
assert.ok($section.find('#two-link').hasClass('active'), '"active" class on middle section')
|
||||
assert.ok(!$section.find('#three-link').hasClass('active'), '"active" class not on last section')
|
||||
done()
|
||||
})
|
||||
|
||||
$scrollspy.scrollTop(550)
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class to the correct element', function (assert) {
|
||||
assert.expect(2)
|
||||
var navbarHtml =
|
||||
'<nav class="navbar">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
var contentHtml =
|
||||
'<div class="content" style="overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '.navbar'
|
||||
})
|
||||
|
||||
var done = assert.async()
|
||||
var testElementIsActiveAfterScroll = function (element, target) {
|
||||
var deferred = $.Deferred()
|
||||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element' + element)
|
||||
deferred.resolve()
|
||||
})
|
||||
$content.scrollTop(scrollHeight)
|
||||
return deferred.promise()
|
||||
}
|
||||
|
||||
$.when(testElementIsActiveAfterScroll('#a-1', '#div-1'))
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#a-2', '#div-2')
|
||||
})
|
||||
.then(function () {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class to the correct element (nav markup)', function (assert) {
|
||||
assert.expect(2)
|
||||
var navbarHtml =
|
||||
'<nav class="navbar">' +
|
||||
'<nav class="nav">' +
|
||||
'<a class="nav-link" id="a-1" href="#div-1">div 1</a>' +
|
||||
'<a class="nav-link" id="a-2" href="#div-2">div 2</a>' +
|
||||
'</nav>' +
|
||||
'</nav>'
|
||||
var contentHtml =
|
||||
'<div class="content" style="overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '.navbar'
|
||||
})
|
||||
|
||||
var done = assert.async()
|
||||
var testElementIsActiveAfterScroll = function (element, target) {
|
||||
var deferred = $.Deferred()
|
||||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element' + element)
|
||||
deferred.resolve()
|
||||
})
|
||||
$content.scrollTop(scrollHeight)
|
||||
return deferred.promise()
|
||||
}
|
||||
|
||||
$.when(testElementIsActiveAfterScroll('#a-1', '#div-1'))
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#a-2', '#div-2')
|
||||
})
|
||||
.then(function () {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class to the correct element (list-group markup)', function (assert) {
|
||||
assert.expect(2)
|
||||
var navbarHtml =
|
||||
'<nav class="navbar">' +
|
||||
'<div class="list-group">' +
|
||||
'<a class="list-group-item" id="a-1" href="#div-1">div 1</a>' +
|
||||
'<a class="list-group-item" id="a-2" href="#div-2">div 2</a>' +
|
||||
'</div>' +
|
||||
'</nav>'
|
||||
var contentHtml =
|
||||
'<div class="content" style="overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '.navbar'
|
||||
})
|
||||
|
||||
var done = assert.async()
|
||||
var testElementIsActiveAfterScroll = function (element, target) {
|
||||
var deferred = $.Deferred()
|
||||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element' + element)
|
||||
deferred.resolve()
|
||||
})
|
||||
$content.scrollTop(scrollHeight)
|
||||
return deferred.promise()
|
||||
}
|
||||
|
||||
$.when(testElementIsActiveAfterScroll('#a-1', '#div-1'))
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#a-2', '#div-2')
|
||||
})
|
||||
.then(function () {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class correctly when there are nested elements at 0 scroll offset', function (assert) {
|
||||
assert.expect(6)
|
||||
var times = 0
|
||||
var done = assert.async()
|
||||
var navbarHtml = '<nav id="navigation" class="navbar">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="a-1" class="nav-link" href="#div-1">div 1</a>' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="a-2" class="nav-link" href="#div-2">div 2</a></li>' +
|
||||
'</ul>' +
|
||||
'</li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
|
||||
var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="padding: 0; margin: 0">' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
function testActiveElements() {
|
||||
if (++times > 3) {
|
||||
return done()
|
||||
}
|
||||
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class')
|
||||
assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class')
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
$content.scrollTop($content.scrollTop() + 10)
|
||||
}
|
||||
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class correctly when there are nested elements (nav markup)', function (assert) {
|
||||
assert.expect(6)
|
||||
var times = 0
|
||||
var done = assert.async()
|
||||
var navbarHtml = '<nav id="navigation" class="navbar">' +
|
||||
'<nav class="nav">' +
|
||||
'<a id="a-1" class="nav-link" href="#div-1">div 1</a>' +
|
||||
'<nav class="nav">' +
|
||||
'<a id="a-2" class="nav-link" href="#div-2">div 2</a>' +
|
||||
'</nav>' +
|
||||
'</nav>' +
|
||||
'</nav>'
|
||||
|
||||
var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="padding: 0; margin: 0">' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
function testActiveElements() {
|
||||
if (++times > 3) {
|
||||
return done()
|
||||
}
|
||||
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class')
|
||||
assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class')
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
$content.scrollTop($content.scrollTop() + 10)
|
||||
}
|
||||
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class correctly when there are nested elements (nav nav-item markup)', function (assert) {
|
||||
assert.expect(6)
|
||||
var times = 0
|
||||
var done = assert.async()
|
||||
var navbarHtml = '<nav id="navigation" class="navbar">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="a-1" class="nav-link" href="#div-1">div 1</a></li>' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="a-2" class="nav-link" href="#div-2">div 2</a></li>' +
|
||||
'</ul>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
|
||||
var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="padding: 0; margin: 0">' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
function testActiveElements() {
|
||||
if (++times > 3) {
|
||||
return done()
|
||||
}
|
||||
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class')
|
||||
assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class')
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
$content.scrollTop($content.scrollTop() + 10)
|
||||
}
|
||||
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
QUnit.test('should add the active class correctly when there are nested elements (list-group markup)', function (assert) {
|
||||
assert.expect(6)
|
||||
var times = 0
|
||||
var done = assert.async()
|
||||
var navbarHtml = '<nav id="navigation" class="navbar">' +
|
||||
'<div class="list-group">' +
|
||||
'<a id="a-1" class="list-group-item" href="#div-1">div 1</a>' +
|
||||
'<div class="list-group">' +
|
||||
'<a id="a-2" class="list-group-item" href="#div-2">div 2</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</nav>'
|
||||
|
||||
var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' +
|
||||
'<div id="div-1" style="padding: 0; margin: 0">' +
|
||||
'<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
function testActiveElements() {
|
||||
if (++times > 3) {
|
||||
return done()
|
||||
}
|
||||
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class')
|
||||
assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class')
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
$content.scrollTop($content.scrollTop() + 10)
|
||||
}
|
||||
|
||||
testActiveElements()
|
||||
})
|
||||
|
||||
QUnit.test('should clear selection if above the first section', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' +
|
||||
'<nav id="navigation" class="navbar">' +
|
||||
'<ul class="navbar-nav">' +
|
||||
'<li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>' +
|
||||
'<li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>' +
|
||||
'<li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
$(sectionHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' +
|
||||
'<div id="spacer" style="height: 100px;"/>' +
|
||||
'<div id="one" style="height: 100px;"/>' +
|
||||
'<div id="two" style="height: 100px;"/>' +
|
||||
'<div id="three" style="height: 100px;"/>' +
|
||||
'<div id="spacer" style="height: 100px;"/>' +
|
||||
'</div>'
|
||||
var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$scrollspy
|
||||
.bootstrapScrollspy({
|
||||
target: '#navigation',
|
||||
offset: $scrollspy.position().top
|
||||
})
|
||||
.one('scroll', function () {
|
||||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present')
|
||||
assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section')
|
||||
$scrollspy
|
||||
.one('scroll', function () {
|
||||
assert.strictEqual($('.active').length, 0, 'selection cleared')
|
||||
done()
|
||||
})
|
||||
.scrollTop(0)
|
||||
})
|
||||
.scrollTop(201)
|
||||
})
|
||||
|
||||
QUnit.test('should NOT clear selection if above the first section and first section is at the top', function (assert) {
|
||||
assert.expect(4)
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' +
|
||||
'<nav id="navigation" class="navbar">' +
|
||||
'<ul class="navbar-nav">' +
|
||||
'<li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>' +
|
||||
'<li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>' +
|
||||
'<li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
$(sectionHTML).appendTo('#qunit-fixture')
|
||||
|
||||
var negativeHeight = -10
|
||||
var startOfSectionTwo = 101
|
||||
|
||||
var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' +
|
||||
'<div id="one" style="height: 100px;"/>' +
|
||||
'<div id="two" style="height: 100px;"/>' +
|
||||
'<div id="three" style="height: 100px;"/>' +
|
||||
'<div id="spacer" style="height: 100px;"/>' +
|
||||
'</div>'
|
||||
var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$scrollspy
|
||||
.bootstrapScrollspy({
|
||||
target: '#navigation',
|
||||
offset: $scrollspy.position().top
|
||||
})
|
||||
.one('scroll', function () {
|
||||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present')
|
||||
assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section')
|
||||
$scrollspy
|
||||
.one('scroll', function () {
|
||||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present')
|
||||
assert.strictEqual($('.active').is('#one-link'), true, '"active" class on first section')
|
||||
done()
|
||||
})
|
||||
.scrollTop(negativeHeight)
|
||||
})
|
||||
.scrollTop(startOfSectionTwo)
|
||||
})
|
||||
|
||||
QUnit.test('should correctly select navigation element on backward scrolling when each target section height is 100%', function (assert) {
|
||||
assert.expect(5)
|
||||
var navbarHtml =
|
||||
'<nav class="navbar">' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>' +
|
||||
'<li class="nav-item"><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>' +
|
||||
'<li class="nav-item"><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>' +
|
||||
'<li class="nav-item"><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>' +
|
||||
'<li class="nav-item"><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
var contentHtml =
|
||||
'<div class="content" style="position: relative; overflow: auto; height: 100px">' +
|
||||
'<div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>' +
|
||||
'<div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>' +
|
||||
'<div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>' +
|
||||
'<div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>' +
|
||||
'</div>'
|
||||
|
||||
$(navbarHtml).appendTo('#qunit-fixture')
|
||||
var $content = $(contentHtml)
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapScrollspy({
|
||||
offset: 0,
|
||||
target: '.navbar'
|
||||
})
|
||||
|
||||
var testElementIsActiveAfterScroll = function (element, target) {
|
||||
var deferred = $.Deferred()
|
||||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
|
||||
$content.one('scroll', function () {
|
||||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element: ' + element)
|
||||
deferred.resolve()
|
||||
})
|
||||
$content.scrollTop(scrollHeight)
|
||||
return deferred.promise()
|
||||
}
|
||||
|
||||
var done = assert.async()
|
||||
$.when(testElementIsActiveAfterScroll('#li-100-5', '#div-100-5'))
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#li-100-4', '#div-100-4')
|
||||
})
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#li-100-3', '#div-100-3')
|
||||
})
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#li-100-2', '#div-100-2')
|
||||
})
|
||||
.then(function () {
|
||||
return testElementIsActiveAfterScroll('#li-100-1', '#div-100-1')
|
||||
})
|
||||
.then(function () {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.test('should allow passed in option offset method: offset', function (assert) {
|
||||
assert.expect(4)
|
||||
|
||||
var testOffsetMethod = function (type) {
|
||||
var $navbar = $(
|
||||
'<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a id="li-' + type + 'm-1" class="nav-link" href="#div-' + type + 'm-1">div 1</a></li>' +
|
||||
'<li class="nav-item"><a id="li-' + type + 'm-2" class="nav-link" href="#div-' + type + 'm-2">div 2</a></li>' +
|
||||
'<li class="nav-item"><a id="li-' + type + 'm-3" class="nav-link" href="#div-' + type + 'm-3">div 3</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
)
|
||||
var $content = $(
|
||||
'<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="offset"' : '') + ' style="position: relative; overflow: auto; height: 100px">' +
|
||||
'<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' +
|
||||
'<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' +
|
||||
'</div>'
|
||||
)
|
||||
|
||||
$navbar.appendTo('#qunit-fixture')
|
||||
$content.appendTo('#qunit-fixture')
|
||||
|
||||
if (type === 'js') {
|
||||
$content.bootstrapScrollspy({
|
||||
target: '.navbar',
|
||||
offset: 0,
|
||||
method: 'offset'
|
||||
})
|
||||
} else if (type === 'data') {
|
||||
$(window).trigger('load')
|
||||
}
|
||||
|
||||
var $target = $('#div-' + type + 'm-2')
|
||||
var scrollspy = $content.data('bs.scrollspy')
|
||||
|
||||
assert.ok(scrollspy._offsets[1] === $target.offset().top, 'offset method with ' + type + ' option')
|
||||
assert.ok(scrollspy._offsets[1] !== $target.position().top, 'position method with ' + type + ' option')
|
||||
$navbar.remove()
|
||||
$content.remove()
|
||||
}
|
||||
|
||||
testOffsetMethod('js')
|
||||
testOffsetMethod('data')
|
||||
})
|
||||
|
||||
QUnit.test('should allow passed in option offset method: position', function (assert) {
|
||||
assert.expect(4)
|
||||
|
||||
var testOffsetMethod = function (type) {
|
||||
var $navbar = $(
|
||||
'<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' +
|
||||
'<ul class="nav">' +
|
||||
'<li class="nav-item"><a class="nav-link" id="li-' + type + 'm-1" href="#div-' + type + 'm-1">div 1</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" id="li-' + type + 'm-2" href="#div-' + type + 'm-2">div 2</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" id="li-' + type + 'm-3" href="#div-' + type + 'm-3">div 3</a></li>' +
|
||||
'</ul>' +
|
||||
'</nav>'
|
||||
)
|
||||
var $content = $(
|
||||
'<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="position"' : '') + ' style="position: relative; overflow: auto; height: 100px">' +
|
||||
'<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' +
|
||||
'<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' +
|
||||
'<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' +
|
||||
'</div>'
|
||||
)
|
||||
|
||||
$navbar.appendTo('#qunit-fixture')
|
||||
$content.appendTo('#qunit-fixture')
|
||||
|
||||
if (type === 'js') {
|
||||
$content.bootstrapScrollspy({
|
||||
target: '.navbar',
|
||||
offset: 0,
|
||||
method: 'position'
|
||||
})
|
||||
} else if (type === 'data') {
|
||||
$(window).trigger('load')
|
||||
}
|
||||
|
||||
var $target = $('#div-' + type + 'm-2')
|
||||
var scrollspy = $content.data('bs.scrollspy')
|
||||
|
||||
assert.ok(scrollspy._offsets[1] !== $target.offset().top, 'offset method with ' + type + ' option')
|
||||
assert.ok(scrollspy._offsets[1] === $target.position().top, 'position method with ' + type + ' option')
|
||||
$navbar.remove()
|
||||
$content.remove()
|
||||
}
|
||||
|
||||
testOffsetMethod('js')
|
||||
testOffsetMethod('data')
|
||||
})
|
||||
})
|
417
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/tab.js
vendored
Normal file
417
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/tab.js
vendored
Normal file
@ -0,0 +1,417 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('tabs plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).tab, 'tabs method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('tabs', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapTab = $.fn.tab.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.tab = $.fn.bootstrapTab
|
||||
delete $.fn.bootstrapTab
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.tab, 'undefined', 'tab was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>')
|
||||
$el.bootstrapTab()
|
||||
try {
|
||||
$el.bootstrapTab('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $tab = $el.bootstrapTab()
|
||||
assert.ok($tab instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($tab[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should activate element by tab id', function (assert) {
|
||||
assert.expect(2)
|
||||
var tabsHTML = '<ul class="nav">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ul>'
|
||||
|
||||
$('<ul><li id="home"/><li id="profile"/></ul>').appendTo('#qunit-fixture')
|
||||
|
||||
$(tabsHTML).find('li:last-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile')
|
||||
|
||||
$(tabsHTML).find('li:first-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
QUnit.test('should activate element by tab id', function (assert) {
|
||||
assert.expect(2)
|
||||
var pillsHTML = '<ul class="nav nav-pills">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ul>'
|
||||
|
||||
$('<ul><li id="home"/><li id="profile"/></ul>').appendTo('#qunit-fixture')
|
||||
|
||||
$(pillsHTML).find('li:last-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile')
|
||||
|
||||
$(pillsHTML).find('li:first-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
QUnit.test('should activate element by tab id in ordered list', function (assert) {
|
||||
assert.expect(2)
|
||||
var pillsHTML = '<ol class="nav nav-pills">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ol>'
|
||||
|
||||
$('<ol><li id="home"/><li id="profile"/></ol>').appendTo('#qunit-fixture')
|
||||
|
||||
$(pillsHTML).find('li:last-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile')
|
||||
|
||||
$(pillsHTML).find('li:first-child a').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
QUnit.test('should activate element by tab id in nav list', function (assert) {
|
||||
assert.expect(2)
|
||||
var tabsHTML = '<nav class="nav">' +
|
||||
'<a href="#home">Home</a>' +
|
||||
'<a href="#profile">Profile</a>' +
|
||||
'</nav>'
|
||||
|
||||
$('<nav><div id="home"></div><div id="profile"></div></nav>').appendTo('#qunit-fixture')
|
||||
|
||||
$(tabsHTML).find('a:last-child').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile')
|
||||
|
||||
$(tabsHTML).find('a:first-child').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
QUnit.test('should activate element by tab id in list group', function (assert) {
|
||||
assert.expect(2)
|
||||
var tabsHTML = '<div class="list-group">' +
|
||||
'<a href="#home">Home</a>' +
|
||||
'<a href="#profile">Profile</a>' +
|
||||
'</div>'
|
||||
|
||||
$('<nav><div id="home"></div><div id="profile"></div></nav>').appendTo('#qunit-fixture')
|
||||
|
||||
$(tabsHTML).find('a:last-child').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile')
|
||||
|
||||
$(tabsHTML).find('a:first-child').bootstrapTab('show')
|
||||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown when show is prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="nav"/>')
|
||||
.on('show.bs.tab', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.tab', function () {
|
||||
assert.ok(false, 'shown event fired')
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown when tab is already active', function (assert) {
|
||||
assert.expect(0)
|
||||
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
|
||||
'<li class="nav-item"><a href="#home" class="nav-link active" role="tab">Home</a></li>' +
|
||||
'<li class="nav-item"><a href="#profile" class="nav-link" role="tab">Profile</a></li>' +
|
||||
'</ul>' +
|
||||
'<div class="tab-content">' +
|
||||
'<div class="tab-pane active" id="home" role="tabpanel"></div>' +
|
||||
'<div class="tab-pane" id="profile" role="tabpanel"></div>' +
|
||||
'</div>'
|
||||
|
||||
$(tabsHTML)
|
||||
.find('a.active')
|
||||
.on('shown.bs.tab', function () {
|
||||
assert.ok(true, 'shown event fired')
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown when tab is disabled', function (assert) {
|
||||
assert.expect(0)
|
||||
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
|
||||
'<li class="nav-item"><a href="#home" class="nav-link active" role="tab">Home</a></li>' +
|
||||
'<li class="nav-item"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>' +
|
||||
'</ul>' +
|
||||
'<div class="tab-content">' +
|
||||
'<div class="tab-pane active" id="home" role="tabpanel"></div>' +
|
||||
'<div class="tab-pane" id="profile" role="tabpanel"></div>' +
|
||||
'</div>'
|
||||
|
||||
$(tabsHTML)
|
||||
.find('a.disabled')
|
||||
.on('shown.bs.tab', function () {
|
||||
assert.ok(true, 'shown event fired')
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('show and shown events should reference correct relatedTarget', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var dropHTML =
|
||||
'<ul class="drop nav">' +
|
||||
' <li class="dropdown"><a data-toggle="dropdown" href="#">1</a>' +
|
||||
' <ul class="dropdown-menu nav">' +
|
||||
' <li><a href="#1-1" data-toggle="tab">1-1</a></li>' +
|
||||
' <li><a href="#1-2" data-toggle="tab">1-2</a></li>' +
|
||||
' </ul>' +
|
||||
' </li>' +
|
||||
'</ul>'
|
||||
|
||||
$(dropHTML)
|
||||
.find('ul > li:first-child a')
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
.find('ul > li:last-child a')
|
||||
.on('show.bs.tab', function (e) {
|
||||
assert.strictEqual(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
|
||||
})
|
||||
.on('shown.bs.tab', function (e) {
|
||||
assert.strictEqual(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire hide and hidden events', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="nav">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ul>'
|
||||
|
||||
$(tabsHTML)
|
||||
.find('li:first-child a')
|
||||
.on('hide.bs.tab', function () {
|
||||
assert.ok(true, 'hide event fired')
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
.find('li:last-child a')
|
||||
.bootstrapTab('show')
|
||||
|
||||
$(tabsHTML)
|
||||
.find('li:first-child a')
|
||||
.on('hidden.bs.tab', function () {
|
||||
assert.ok(true, 'hidden event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
.find('li:last-child a')
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire hidden when hide is prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="nav">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ul>'
|
||||
|
||||
$(tabsHTML)
|
||||
.find('li:first-child a')
|
||||
.on('hide.bs.tab', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'hide event fired')
|
||||
done()
|
||||
})
|
||||
.on('hidden.bs.tab', function () {
|
||||
assert.ok(false, 'hidden event fired')
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
.find('li:last-child a')
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('hide and hidden events contain correct relatedTarget', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="nav">' +
|
||||
'<li><a href="#home">Home</a></li>' +
|
||||
'<li><a href="#profile">Profile</a></li>' +
|
||||
'</ul>'
|
||||
|
||||
$(tabsHTML)
|
||||
.find('li:first-child a')
|
||||
.on('hide.bs.tab', function (e) {
|
||||
assert.strictEqual(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
|
||||
})
|
||||
.on('hidden.bs.tab', function (e) {
|
||||
assert.strictEqual(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
.find('li:last-child a')
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
QUnit.test('selected tab should have aria-selected', function (assert) {
|
||||
assert.expect(8)
|
||||
var tabsHTML = '<ul class="nav nav-tabs">' +
|
||||
'<li><a class="nav-item active" href="#home" toggle="tab" aria-selected="true">Home</a></li>' +
|
||||
'<li><a class="nav-item" href="#profile" toggle="tab" aria-selected="false">Profile</a></li>' +
|
||||
'</ul>'
|
||||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$tabs.find('li:first-child a').bootstrapTab('show')
|
||||
assert.strictEqual($tabs.find('.active').attr('aria-selected'), 'true', 'shown tab has aria-selected = true')
|
||||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-selected'), 'false', 'hidden tab has aria-selected = false')
|
||||
|
||||
$tabs.find('li:last-child a').trigger('click')
|
||||
assert.strictEqual($tabs.find('.active').attr('aria-selected'), 'true', 'after click, shown tab has aria-selected = true')
|
||||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-selected'), 'false', 'after click, hidden tab has aria-selected = false')
|
||||
|
||||
$tabs.find('li:first-child a').bootstrapTab('show')
|
||||
assert.strictEqual($tabs.find('.active').attr('aria-selected'), 'true', 'shown tab has aria-selected = true')
|
||||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-selected'), 'false', 'hidden tab has aria-selected = false')
|
||||
|
||||
$tabs.find('li:first-child a').trigger('click')
|
||||
assert.strictEqual($tabs.find('.active').attr('aria-selected'), 'true', 'after second show event, shown tab still has aria-selected = true')
|
||||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-selected'), 'false', 'after second show event, hidden tab has aria-selected = false')
|
||||
})
|
||||
|
||||
QUnit.test('selected tab should deactivate previous selected tab', function (assert) {
|
||||
assert.expect(2)
|
||||
var tabsHTML = '<ul class="nav nav-tabs">' +
|
||||
'<li class="nav-item"><a class="nav-link active" href="#home" data-toggle="tab">Home</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" href="#profile" data-toggle="tab">Profile</a></li>' +
|
||||
'</ul>'
|
||||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$tabs.find('li:last-child a').trigger('click')
|
||||
assert.notOk($tabs.find('li:first-child a').hasClass('active'))
|
||||
assert.ok($tabs.find('li:last-child a').hasClass('active'))
|
||||
})
|
||||
|
||||
QUnit.test('selected tab should deactivate previous selected link in dropdown', function (assert) {
|
||||
assert.expect(3)
|
||||
var tabsHTML = '<ul class="nav nav-tabs">' +
|
||||
'<li class="nav-item"><a class="nav-link" href="#home" data-toggle="tab">Home</a></li>' +
|
||||
'<li class="nav-item"><a class="nav-link" href="#profile" data-toggle="tab">Profile</a></li>' +
|
||||
'<li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" data-toggle="dropdown" href="#">Dropdown</a>' +
|
||||
'<div class="dropdown-menu">' +
|
||||
'<a class="dropdown-item active" href="#dropdown1" id="dropdown1-tab" data-toggle="tab">@fat</a>' +
|
||||
'<a class="dropdown-item" href="#dropdown2" id="dropdown2-tab" data-toggle="tab">@mdo</a>' +
|
||||
'</div>' +
|
||||
'</li>' +
|
||||
'</ul>'
|
||||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$tabs.find('li:first-child a').trigger('click')
|
||||
assert.ok($tabs.find('li:first-child a').hasClass('active'))
|
||||
assert.notOk($tabs.find('li:last-child a').hasClass('active'))
|
||||
assert.notOk($tabs.find('li:last-child .dropdown-menu a:first-child').hasClass('active'))
|
||||
})
|
||||
|
||||
QUnit.test('Nested tabs', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var tabsHTML =
|
||||
'<nav class="nav nav-tabs" role="tablist">' +
|
||||
' <a id="tab1" href="#x-tab1" class="nav-item nav-link" data-toggle="tab" role="tab" aria-controls="x-tab1">Tab 1</a>' +
|
||||
' <a href="#x-tab2" class="nav-item nav-link active" data-toggle="tab" role="tab" aria-controls="x-tab2" aria-selected="true">Tab 2</a>' +
|
||||
' <a href="#x-tab3" class="nav-item nav-link" data-toggle="tab" role="tab" aria-controls="x-tab3">Tab 3</a>' +
|
||||
'</nav>' +
|
||||
'<div class="tab-content">' +
|
||||
' <div class="tab-pane" id="x-tab1" role="tabpanel">' +
|
||||
' <nav class="nav nav-tabs" role="tablist">' +
|
||||
' <a href="#nested-tab1" class="nav-item nav-link active" data-toggle="tab" role="tab" aria-controls="x-tab1" aria-selected="true">Nested Tab 1</a>' +
|
||||
' <a id="tabNested2" href="#nested-tab2" class="nav-item nav-link" data-toggle="tab" role="tab" aria-controls="x-profile">Nested Tab2</a>' +
|
||||
' </nav>' +
|
||||
' <div class="tab-content">' +
|
||||
' <div class="tab-pane active" id="nested-tab1" role="tabpanel">Nested Tab1 Content</div>' +
|
||||
' <div class="tab-pane" id="nested-tab2" role="tabpanel">Nested Tab2 Content</div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' <div class="tab-pane active" id="x-tab2" role="tabpanel">Tab2 Content</div>' +
|
||||
' <div class="tab-pane" id="x-tab3" role="tabpanel">Tab3 Content</div>' +
|
||||
'</div>'
|
||||
|
||||
$(tabsHTML).appendTo('#qunit-fixture')
|
||||
|
||||
$('#tabNested2').on('shown.bs.tab', function () {
|
||||
assert.ok($('#x-tab1').hasClass('active'))
|
||||
done()
|
||||
})
|
||||
|
||||
$('#tab1').on('shown.bs.tab', function () {
|
||||
assert.ok($('#x-tab1').hasClass('active'))
|
||||
$('#tabNested2').trigger($.Event('click'))
|
||||
})
|
||||
.trigger($.Event('click'))
|
||||
})
|
||||
|
||||
QUnit.test('should not remove fade class if no active pane is present', function (assert) {
|
||||
assert.expect(6)
|
||||
var done = assert.async()
|
||||
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
|
||||
'<li class="nav-item"><a id="tab-home" href="#home" class="nav-link" data-toggle="tab" role="tab">Home</a></li>' +
|
||||
'<li class="nav-item"><a id="tab-profile" href="#profile" class="nav-link" data-toggle="tab" role="tab">Profile</a></li>' +
|
||||
'</ul>' +
|
||||
'<div class="tab-content">' +
|
||||
'<div class="tab-pane fade" id="home" role="tabpanel"></div>' +
|
||||
'<div class="tab-pane fade" id="profile" role="tabpanel"></div>' +
|
||||
'</div>'
|
||||
|
||||
$(tabsHTML).appendTo('#qunit-fixture')
|
||||
$('#tab-profile')
|
||||
.on('shown.bs.tab', function () {
|
||||
assert.ok($('#profile').hasClass('fade'))
|
||||
assert.ok($('#profile').hasClass('show'))
|
||||
|
||||
$('#tab-home')
|
||||
.on('shown.bs.tab', function () {
|
||||
assert.ok($('#profile').hasClass('fade'))
|
||||
assert.notOk($('#profile').hasClass('show'))
|
||||
assert.ok($('#home').hasClass('fade'))
|
||||
assert.ok($('#home').hasClass('show'))
|
||||
|
||||
done()
|
||||
})
|
||||
.trigger($.Event('click'))
|
||||
})
|
||||
.trigger($.Event('click'))
|
||||
})
|
||||
})
|
969
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/tooltip.js
vendored
Normal file
969
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/tooltip.js
vendored
Normal file
@ -0,0 +1,969 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
QUnit.module('tooltip plugin')
|
||||
|
||||
QUnit.test('should be defined on jquery object', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($(document.body).tooltip, 'tooltip method is defined')
|
||||
})
|
||||
|
||||
QUnit.module('tooltip', {
|
||||
beforeEach: function () {
|
||||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
||||
$.fn.bootstrapTooltip = $.fn.tooltip.noConflict()
|
||||
},
|
||||
afterEach: function () {
|
||||
$.fn.tooltip = $.fn.bootstrapTooltip
|
||||
delete $.fn.bootstrapTooltip
|
||||
$('.tooltip').remove()
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should provide no conflict', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.strictEqual(typeof $.fn.tooltip, 'undefined', 'tooltip was set back to undefined (org value)')
|
||||
})
|
||||
|
||||
QUnit.test('should throw explicit error on undefined method', function (assert) {
|
||||
assert.expect(1)
|
||||
var $el = $('<div/>')
|
||||
$el.bootstrapTooltip()
|
||||
try {
|
||||
$el.bootstrapTooltip('noMethod')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'No method named "noMethod"')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should return jquery collection containing the element', function (assert) {
|
||||
assert.expect(2)
|
||||
var $el = $('<div/>')
|
||||
var $tooltip = $el.bootstrapTooltip()
|
||||
assert.ok($tooltip instanceof $, 'returns jquery collection')
|
||||
assert.strictEqual($tooltip[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
QUnit.test('should expose default settings', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok($.fn.bootstrapTooltip.Constructor.Default, 'defaults is defined')
|
||||
})
|
||||
|
||||
QUnit.test('should empty title attribute', function (assert) {
|
||||
assert.expect(1)
|
||||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
|
||||
assert.strictEqual($trigger.attr('title'), '', 'title attribute was emptied')
|
||||
})
|
||||
|
||||
QUnit.test('should add data attribute for referencing original title', function (assert) {
|
||||
assert.expect(1)
|
||||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
|
||||
assert.strictEqual($trigger.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
|
||||
})
|
||||
|
||||
QUnit.test('should add aria-describedby to the trigger on show', function (assert) {
|
||||
assert.expect(3)
|
||||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.bootstrapTooltip()
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip('show')
|
||||
|
||||
var id = $('.tooltip').attr('id')
|
||||
|
||||
assert.strictEqual($('#' + id).length, 1, 'has a unique id')
|
||||
assert.strictEqual($('.tooltip').attr('aria-describedby'), $trigger.attr('id'), 'tooltip id and aria-describedby on trigger match')
|
||||
assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
|
||||
})
|
||||
|
||||
QUnit.test('should remove aria-describedby from trigger on hide', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.bootstrapTooltip()
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$trigger
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
|
||||
$trigger.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.ok(!$trigger[0].hasAttribute('aria-describedby'), 'trigger does not have aria-describedby')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should assign a unique id tooltip element', function (assert) {
|
||||
assert.expect(2)
|
||||
$('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip('show')
|
||||
|
||||
var id = $('.tooltip').attr('id')
|
||||
|
||||
assert.strictEqual($('#' + id).length, 1, 'tooltip has unique id')
|
||||
assert.strictEqual(id.indexOf('tooltip'), 0, 'tooltip id has prefix')
|
||||
})
|
||||
|
||||
QUnit.test('should place tooltips relative to placement option', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
placement: 'bottom'
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.ok($('.tooltip')
|
||||
.is('.fade.bs-tooltip-bottom.show'), 'has correct classes applied')
|
||||
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should allow html entities', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="<b>@fat</b>"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
html: true
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.notEqual($('.tooltip b').length, 0, 'b tag was inserted')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should allow DOMElement title (html: false)', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
var title = document.createTextNode('<3 writing tests')
|
||||
var $tooltip = $('<a href="#" rel="tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
title: title
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.notEqual($('.tooltip').length, 0, 'tooltip inserted')
|
||||
assert.strictEqual($('.tooltip').text(), '<3 writing tests', 'title inserted')
|
||||
assert.ok(!$.contains($('.tooltip').get(0), title), 'title node copied, not moved')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should allow DOMElement title (html: true)', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
var title = document.createTextNode('<3 writing tests')
|
||||
var $tooltip = $('<a href="#" rel="tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
html: true,
|
||||
title: title
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.notEqual($('.tooltip').length, 0, 'tooltip inserted')
|
||||
assert.strictEqual($('.tooltip').text(), '<3 writing tests', 'title inserted')
|
||||
assert.ok($.contains($('.tooltip').get(0), title), 'title node moved, not copied')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should respect custom classes', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>'
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.ok($('.tooltip').hasClass('some-class'), 'custom class is present')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire show event', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.on('show.bs.tooltip', function () {
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should throw an error when show is called on hidden elements', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
try {
|
||||
$('<div title="tooltip title" style="display: none"/>').bootstrapTooltip('show')
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'Please use show on visible elements')
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('should fire inserted event', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('inserted.bs.tooltip', function () {
|
||||
assert.notEqual($('.tooltip').length, 0, 'tooltip was inserted')
|
||||
assert.ok(true, 'inserted event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire shown event', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
assert.ok(true, 'shown was called')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire shown event when show was prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.on('show.bs.tooltip', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'show event fired')
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.tooltip', function () {
|
||||
assert.ok(false, 'shown event fired')
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire hide event', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
$(this).bootstrapTooltip('hide')
|
||||
})
|
||||
.on('hide.bs.tooltip', function () {
|
||||
assert.ok(true, 'hide event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should fire hidden event', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
$(this).bootstrapTooltip('hide')
|
||||
})
|
||||
.on('hidden.bs.tooltip', function () {
|
||||
assert.ok(true, 'hidden event fired')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not fire hidden event when hide was prevented', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
$(this).bootstrapTooltip('hide')
|
||||
})
|
||||
.on('hide.bs.tooltip', function (e) {
|
||||
e.preventDefault()
|
||||
assert.ok(true, 'hide event fired')
|
||||
done()
|
||||
})
|
||||
.on('hidden.bs.tooltip', function () {
|
||||
assert.ok(false, 'hidden event fired')
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should destroy tooltip', function (assert) {
|
||||
assert.expect(7)
|
||||
var $tooltip = $('<div/>')
|
||||
.bootstrapTooltip()
|
||||
.on('click.foo', function () {}) // eslint-disable-line no-empty-function
|
||||
|
||||
assert.ok($tooltip.data('bs.tooltip'), 'tooltip has data')
|
||||
assert.ok($._data($tooltip[0], 'events').mouseover && $._data($tooltip[0], 'events').mouseout, 'tooltip has hover events')
|
||||
assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip has extra click.foo event')
|
||||
|
||||
$tooltip.bootstrapTooltip('show')
|
||||
$tooltip.bootstrapTooltip('dispose')
|
||||
|
||||
assert.ok(!$tooltip.hasClass('show'), 'tooltip is hidden')
|
||||
assert.ok(!$._data($tooltip[0], 'bs.tooltip'), 'tooltip does not have data')
|
||||
assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip still has click.foo')
|
||||
assert.ok(!$._data($tooltip[0], 'events').mouseover && !$._data($tooltip[0], 'events').mouseout, 'tooltip does not have hover events')
|
||||
})
|
||||
|
||||
// QUnit.test('should show tooltip with delegate selector on click', function (assert) {
|
||||
// assert.expect(2)
|
||||
// var $div = $('<div><a href="#" rel="tooltip" title="Another tooltip"/></div>')
|
||||
// .appendTo('#qunit-fixture')
|
||||
// .bootstrapTooltip({
|
||||
// selector: 'a[rel="tooltip"]',
|
||||
// trigger: 'click'
|
||||
// })
|
||||
|
||||
// $div.find('a').trigger('click')
|
||||
// assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
|
||||
|
||||
// $div.find('a').trigger('click')
|
||||
// assert.strictEqual($div.data('bs.tooltip').tip.parentNode, null, 'tooltip removed')
|
||||
// })
|
||||
|
||||
QUnit.test('should show tooltip when toggle is called', function (assert) {
|
||||
assert.expect(1)
|
||||
$('<a href="#" rel="tooltip" title="tooltip on toggle"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
trigger: 'manual'
|
||||
})
|
||||
.bootstrapTooltip('toggle')
|
||||
|
||||
assert.ok($('.tooltip').is('.fade.show'), 'tooltip is faded active')
|
||||
})
|
||||
|
||||
QUnit.test('should hide previously shown tooltip when toggle is called on tooltip', function (assert) {
|
||||
assert.expect(1)
|
||||
$('<a href="#" rel="tooltip" title="tooltip on toggle">@ResentedHook</a>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
trigger: 'manual'
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
|
||||
$('.tooltip').bootstrapTooltip('toggle')
|
||||
assert.ok($('.tooltip').not('.fade.show'), 'tooltip was faded out')
|
||||
})
|
||||
|
||||
QUnit.test('should place tooltips inside body when container is body', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
container: 'body'
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.notEqual($('body > .tooltip').length, 0, 'tooltip is direct descendant of body')
|
||||
assert.strictEqual($('#qunit-fixture > .tooltip').length, 0, 'tooltip is not in parent')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($('body > .tooltip').length, 0, 'tooltip was removed from dom')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should add position class before positioning so that position-specific styles are taken into account', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var styles = '<style>' +
|
||||
'.bs-tooltip-right { white-space: nowrap; }' +
|
||||
'.bs-tooltip-right .tooltip-inner { max-width: none; }' +
|
||||
'</style>'
|
||||
var $styles = $(styles).appendTo('head')
|
||||
|
||||
var $container = $('<div/>').appendTo('#qunit-fixture')
|
||||
$('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"/>')
|
||||
.appendTo($container)
|
||||
.bootstrapTooltip({
|
||||
placement: 'right',
|
||||
trigger: 'manual'
|
||||
})
|
||||
.on('inserted.bs.tooltip', function () {
|
||||
var $tooltip = $($(this).data('bs.tooltip').tip)
|
||||
assert.ok($tooltip.hasClass('bs-tooltip-right'))
|
||||
assert.ok(typeof $tooltip.attr('style') === 'undefined')
|
||||
$styles.remove()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should use title attribute for tooltip text', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip()
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should prefer title attribute over title option', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
title: 'This is a tooltip with some content'
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while preferred over title option')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should use title option', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $tooltip = $('<a href="#" rel="tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
title: 'This is a tooltip with some content'
|
||||
})
|
||||
|
||||
$tooltip
|
||||
.one('shown.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set')
|
||||
$tooltip.bootstrapTooltip('hide')
|
||||
})
|
||||
.one('hidden.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('should not error when trying to show an top-placed tooltip that has been removed from the dom', function (assert) {
|
||||
assert.expect(1)
|
||||
var passed = true
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.one('show.bs.tooltip', function () {
|
||||
$(this).remove()
|
||||
})
|
||||
.bootstrapTooltip({
|
||||
placement: 'top'
|
||||
})
|
||||
|
||||
try {
|
||||
$tooltip.bootstrapTooltip('show')
|
||||
} catch (err) {
|
||||
passed = false
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
assert.ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom')
|
||||
})
|
||||
|
||||
QUnit.test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: 150
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip is not faded active')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($('.tooltip').is('.fade.show'), '200ms: tooltip is faded active')
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: 150
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active')
|
||||
$tooltip.trigger('mouseout')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '200ms: tooltip not faded active')
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: {
|
||||
show: 0,
|
||||
hide: 150
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($('.tooltip').is('.fade.show'), '1ms: tooltip faded active')
|
||||
$tooltip.trigger('mouseout')
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($('.tooltip').is('.fade.show'), '100ms: tooltip still faded active')
|
||||
$tooltip.trigger('mouseenter')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($('.tooltip').is('.fade.show'), '200ms: tooltip still faded active')
|
||||
done()
|
||||
}, 200)
|
||||
}, 0)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: 150
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active')
|
||||
$tooltip.trigger('mouseout')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '200ms: tooltip not faded active')
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: {
|
||||
show: 150,
|
||||
hide: 0
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active')
|
||||
$tooltip.trigger('mouseout')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$('.tooltip').is('.fade.show'), '250ms: tooltip not faded active')
|
||||
done()
|
||||
}, 250)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should wait 200ms before hiding the tooltip', function (assert) {
|
||||
assert.expect(3)
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
delay: {
|
||||
show: 0,
|
||||
hide: 150
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($($tooltip.data('bs.tooltip').tip).is('.fade.show'), '1ms: tooltip faded active')
|
||||
|
||||
$tooltip.trigger('mouseout')
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok($($tooltip.data('bs.tooltip').tip).is('.fade.show'), '100ms: tooltip still faded active')
|
||||
}, 100)
|
||||
|
||||
setTimeout(function () {
|
||||
assert.ok(!$($tooltip.data('bs.tooltip').tip).is('.show'), '200ms: tooltip removed')
|
||||
done()
|
||||
}, 200)
|
||||
}, 0)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
QUnit.test('should not reload the tooltip on subsequent mouseenter events', function (assert) {
|
||||
assert.expect(1)
|
||||
var titleHtml = function () {
|
||||
var uid = Util.getUID('tooltip')
|
||||
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
|
||||
}
|
||||
|
||||
var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$tooltip.bootstrapTooltip({
|
||||
html: true,
|
||||
animation: false,
|
||||
trigger: 'hover',
|
||||
delay: {
|
||||
show: 0,
|
||||
hide: 500
|
||||
},
|
||||
container: $tooltip,
|
||||
title: titleHtml
|
||||
})
|
||||
|
||||
$('#tt-outer').trigger('mouseenter')
|
||||
|
||||
var currentUid = $('#tt-content').text()
|
||||
|
||||
$('#tt-content').trigger('mouseenter')
|
||||
assert.strictEqual(currentUid, $('#tt-content').text())
|
||||
})
|
||||
|
||||
QUnit.test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function (assert) {
|
||||
assert.expect(4)
|
||||
|
||||
var titleHtml = function () {
|
||||
var uid = Util.getUID('tooltip')
|
||||
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
|
||||
}
|
||||
|
||||
var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
$tooltip.bootstrapTooltip({
|
||||
html: true,
|
||||
animation: false,
|
||||
trigger: 'hover',
|
||||
delay: {
|
||||
show: 0,
|
||||
hide: 500
|
||||
},
|
||||
title: titleHtml
|
||||
})
|
||||
|
||||
var obj = $tooltip.data('bs.tooltip')
|
||||
|
||||
$('#tt-outer').trigger('mouseenter')
|
||||
|
||||
var currentUid = $('#tt-content').text()
|
||||
|
||||
$('#tt-outer').trigger('mouseleave')
|
||||
assert.strictEqual(currentUid, $('#tt-content').text())
|
||||
|
||||
assert.ok(obj._hoverState === 'out', 'the tooltip hoverState should be set to "out"')
|
||||
|
||||
$('#tt-outer').trigger('mouseenter')
|
||||
assert.ok(obj._hoverState === 'show', 'the tooltip hoverState should be set to "show"')
|
||||
|
||||
assert.strictEqual(currentUid, $('#tt-content').text())
|
||||
})
|
||||
|
||||
QUnit.test('should do nothing when an attempt is made to hide an uninitialized tooltip', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
var $tooltip = $('<span data-toggle="tooltip" title="some tip">some text</span>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.tooltip shown.bs.tooltip', function () {
|
||||
assert.ok(false, 'should not fire any tooltip events')
|
||||
})
|
||||
.bootstrapTooltip('hide')
|
||||
assert.strictEqual(typeof $tooltip.data('bs.tooltip'), 'undefined', 'should not initialize the tooltip')
|
||||
})
|
||||
|
||||
QUnit.test('should not remove tooltip if multiple triggers are set and one is still active', function (assert) {
|
||||
assert.expect(41)
|
||||
var $el = $('<button>Trigger</button>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
trigger: 'click hover focus',
|
||||
animation: false
|
||||
})
|
||||
var tooltip = $el.data('bs.tooltip')
|
||||
var $tooltip = $(tooltip.getTipElement())
|
||||
|
||||
function showingTooltip() {
|
||||
return $tooltip.hasClass('show') || tooltip._hoverState === 'show'
|
||||
}
|
||||
|
||||
var tests = [
|
||||
['mouseenter', 'mouseleave'],
|
||||
|
||||
['focusin', 'focusout'],
|
||||
|
||||
['click', 'click'],
|
||||
|
||||
['mouseenter', 'focusin', 'focusout', 'mouseleave'],
|
||||
['mouseenter', 'focusin', 'mouseleave', 'focusout'],
|
||||
|
||||
['focusin', 'mouseenter', 'mouseleave', 'focusout'],
|
||||
['focusin', 'mouseenter', 'focusout', 'mouseleave'],
|
||||
|
||||
['click', 'focusin', 'mouseenter', 'focusout', 'mouseleave', 'click'],
|
||||
['mouseenter', 'click', 'focusin', 'focusout', 'mouseleave', 'click'],
|
||||
['mouseenter', 'focusin', 'click', 'click', 'mouseleave', 'focusout']
|
||||
]
|
||||
|
||||
assert.ok(!showingTooltip())
|
||||
|
||||
$.each(tests, function (idx, triggers) {
|
||||
for (var i = 0, len = triggers.length; i < len; i++) {
|
||||
$el.trigger(triggers[i])
|
||||
assert.equal(i < len - 1, showingTooltip())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.test('should show on first trigger after hide', function (assert) {
|
||||
assert.expect(3)
|
||||
var $el = $('<a href="#" rel="tooltip" title="Test tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip({
|
||||
trigger: 'click hover focus',
|
||||
animation: false
|
||||
})
|
||||
|
||||
var tooltip = $el.data('bs.tooltip')
|
||||
var $tooltip = $(tooltip.getTipElement())
|
||||
|
||||
function showingTooltip() {
|
||||
return $tooltip.hasClass('show') || tooltip._hoverState === 'show'
|
||||
}
|
||||
|
||||
$el.trigger('click')
|
||||
assert.ok(showingTooltip(), 'tooltip is faded in')
|
||||
|
||||
$el.bootstrapTooltip('hide')
|
||||
assert.ok(!showingTooltip(), 'tooltip was faded out')
|
||||
|
||||
$el.trigger('click')
|
||||
assert.ok(showingTooltip(), 'tooltip is faded in again')
|
||||
})
|
||||
|
||||
QUnit.test('should hide tooltip when their containing modal is closed', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var templateHTML = '<div id="modal-test" class="modal">' +
|
||||
'<div class="modal-dialog" role="document">' +
|
||||
'<div class="modal-content">' +
|
||||
'<div class="modal-body">' +
|
||||
'<a id="tooltipTest" href="#" data-toggle="tooltip" title="Some tooltip text!">Tooltip</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
||||
$(templateHTML).appendTo('#qunit-fixture')
|
||||
$('#tooltipTest')
|
||||
.bootstrapTooltip({
|
||||
trigger: 'manuel'
|
||||
})
|
||||
.on('shown.bs.tooltip', function () {
|
||||
$('#modal-test').modal('hide')
|
||||
})
|
||||
.on('hide.bs.tooltip', function () {
|
||||
assert.ok(true, 'tooltip hide')
|
||||
done()
|
||||
})
|
||||
|
||||
$('#modal-test')
|
||||
.on('shown.bs.modal', function () {
|
||||
$('#tooltipTest').bootstrapTooltip('show')
|
||||
})
|
||||
.modal('show')
|
||||
})
|
||||
|
||||
QUnit.test('should reset tip classes when hidden event triggered', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
var $el = $('<a href="#" rel="tooltip" title="Test tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip('show')
|
||||
.on('hidden.bs.tooltip', function () {
|
||||
var tooltip = $el.data('bs.tooltip')
|
||||
var $tooltip = $(tooltip.getTipElement())
|
||||
assert.ok($tooltip.hasClass('tooltip'))
|
||||
assert.ok($tooltip.hasClass('fade'))
|
||||
done()
|
||||
})
|
||||
|
||||
$el.bootstrapTooltip('hide')
|
||||
})
|
||||
|
||||
QUnit.test('should convert number in title to string', function (assert) {
|
||||
assert.expect(1)
|
||||
var done = assert.async()
|
||||
var $el = $('<a href="#" rel="tooltip" title="7"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
var tooltip = $el.data('bs.tooltip')
|
||||
var $tooltip = $(tooltip.getTipElement())
|
||||
assert.strictEqual($tooltip.children().text(), '7')
|
||||
done()
|
||||
})
|
||||
|
||||
$el.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
QUnit.test('tooltip should be shown right away after the call of disable/enable', function (assert) {
|
||||
assert.expect(2)
|
||||
var done = assert.async()
|
||||
|
||||
var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip()
|
||||
.on('shown.bs.tooltip', function () {
|
||||
assert.strictEqual($('.tooltip').hasClass('show'), true)
|
||||
done()
|
||||
})
|
||||
|
||||
$trigger.bootstrapTooltip('disable')
|
||||
$trigger.trigger($.Event('click'))
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($('.tooltip').length === 0, true)
|
||||
$trigger.bootstrapTooltip('enable')
|
||||
$trigger.trigger($.Event('click'))
|
||||
}, 200)
|
||||
})
|
||||
|
||||
QUnit.test('should call Popper.js to update', function (assert) {
|
||||
assert.expect(2)
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip()
|
||||
|
||||
var tooltip = $tooltip.data('bs.tooltip')
|
||||
tooltip.show()
|
||||
assert.ok(tooltip._popper)
|
||||
|
||||
var spyPopper = sinon.spy(tooltip._popper, 'scheduleUpdate')
|
||||
tooltip.update()
|
||||
assert.ok(spyPopper.called)
|
||||
})
|
||||
|
||||
QUnit.test('should not call Popper.js to update', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip()
|
||||
|
||||
var tooltip = $tooltip.data('bs.tooltip')
|
||||
tooltip.update()
|
||||
|
||||
assert.ok(tooltip._popper === null)
|
||||
})
|
||||
|
||||
QUnit.test('should use Popper.js to get the tip on placement change', function (assert) {
|
||||
assert.expect(1)
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.bootstrapTooltip()
|
||||
|
||||
var $tipTest = $('<div class="bs-tooltip" />')
|
||||
.appendTo('#qunit-fixture')
|
||||
|
||||
var tooltip = $tooltip.data('bs.tooltip')
|
||||
tooltip.tip = null
|
||||
|
||||
tooltip._handlePopperPlacementChange({
|
||||
instance: {
|
||||
popper: $tipTest[0]
|
||||
},
|
||||
placement: 'auto'
|
||||
})
|
||||
|
||||
assert.ok(tooltip.tip === $tipTest[0])
|
||||
})
|
||||
})
|
104
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/util.js
vendored
Normal file
104
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/js/tests/unit/util.js
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
$(function () {
|
||||
'use strict'
|
||||
|
||||
window.Util = typeof bootstrap !== 'undefined' ? bootstrap.Util : Util
|
||||
|
||||
QUnit.module('util', {
|
||||
afterEach: function () {
|
||||
$('#qunit-fixture').html('')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('Util.getSelectorFromElement should return the correct element', function (assert) {
|
||||
assert.expect(2)
|
||||
|
||||
var $el = $('<div data-target="body"></div>').appendTo($('#qunit-fixture'))
|
||||
assert.strictEqual(Util.getSelectorFromElement($el[0]), 'body')
|
||||
|
||||
// Not found element
|
||||
var $el2 = $('<div data-target="#fakeDiv"></div>').appendTo($('#qunit-fixture'))
|
||||
assert.strictEqual(Util.getSelectorFromElement($el2[0]), null)
|
||||
})
|
||||
|
||||
QUnit.test('Util.typeCheckConfig should thrown an error when a bad config is passed', function (assert) {
|
||||
assert.expect(1)
|
||||
var namePlugin = 'collapse'
|
||||
var defaultType = {
|
||||
toggle: 'boolean',
|
||||
parent: '(string|element)'
|
||||
}
|
||||
var config = {
|
||||
toggle: true,
|
||||
parent: 777
|
||||
}
|
||||
|
||||
try {
|
||||
Util.typeCheckConfig(namePlugin, config, defaultType)
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('Util.isElement should check if we passed an element or not', function (assert) {
|
||||
assert.expect(3)
|
||||
var $div = $('<div id="test"></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
assert.strictEqual(Util.isElement($div), 1)
|
||||
assert.strictEqual(Util.isElement($div[0]), 1)
|
||||
assert.strictEqual(typeof Util.isElement({}) === 'undefined', true)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getTransitionDurationFromElement should accept transition durations in milliseconds', function (assert) {
|
||||
assert.expect(1)
|
||||
var $div = $('<div style="transition: all 300ms ease-out;"></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
assert.strictEqual(Util.getTransitionDurationFromElement($div[0]), 300)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getTransitionDurationFromElement should accept transition durations in seconds', function (assert) {
|
||||
assert.expect(1)
|
||||
var $div = $('<div style="transition: all .4s ease-out;"></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
assert.strictEqual(Util.getTransitionDurationFromElement($div[0]), 400)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getTransitionDurationFromElement should get the first transition duration if multiple transition durations are defined', function (assert) {
|
||||
assert.expect(1)
|
||||
var $div = $('<div style="transition: transform .3s ease-out, opacity .2s;"></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
assert.strictEqual(Util.getTransitionDurationFromElement($div[0]), 300)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getTransitionDurationFromElement should return 0 if transition duration is not defined', function (assert) {
|
||||
assert.expect(1)
|
||||
var $div = $('<div></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
assert.strictEqual(Util.getTransitionDurationFromElement($div[0]), 0)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getTransitionDurationFromElement should return 0 if element is not found in DOM', function (assert) {
|
||||
assert.expect(1)
|
||||
var $div = $('#fake-id')
|
||||
|
||||
assert.strictEqual(Util.getTransitionDurationFromElement($div[0]), 0)
|
||||
})
|
||||
|
||||
QUnit.test('Util.getUID should generate a new id uniq', function (assert) {
|
||||
assert.expect(2)
|
||||
var id = Util.getUID('test')
|
||||
var id2 = Util.getUID('test')
|
||||
|
||||
assert.ok(id !== id2, id + ' !== ' + id2)
|
||||
|
||||
id = Util.getUID('test')
|
||||
$('<div id="' + id + '"></div>').appendTo($('#qunit-fixture'))
|
||||
|
||||
id2 = Util.getUID('test')
|
||||
assert.ok(id !== id2, id + ' !== ' + id2)
|
||||
})
|
||||
|
||||
QUnit.test('Util.supportsTransitionEnd should return true', function (assert) {
|
||||
assert.expect(1)
|
||||
assert.ok(Util.supportsTransitionEnd())
|
||||
})
|
||||
})
|
@ -0,0 +1,58 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Alert</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Alert <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong>Holy guacamole!</strong> You should check in on some of those fields below.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<p>
|
||||
<strong>Oh snap!</strong> <a href="#" class="alert-link">Change a few things up</a> and try submitting again.
|
||||
</p>
|
||||
<p>
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
<button type="button" class="btn btn-secondary">Secondary</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<p>
|
||||
<strong>Oh snap!</strong> <a href="#" class="alert-link">Change a few things up</a> and try submitting again. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum.
|
||||
</p>
|
||||
<p>
|
||||
<button type="button" class="btn btn-danger">Take this action</button>
|
||||
<button type="button" class="btn btn-primary">Or do this</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning alert-dismissible fade show" role="alert" style="transition-duration: 5s;">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
This alert will take 5 seconds to fade out.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/alert.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,51 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Button</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Button <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<button type="button" class="btn btn-primary" data-toggle="button" aria-pressed="false" autocomplete="off">
|
||||
Single toggle
|
||||
</button>
|
||||
|
||||
<p>For checkboxes and radio buttons, ensure that keyboard behavior is functioning correctly.</p>
|
||||
<p>Navigate to the checkboxes with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>), and ensure that <kbd>SPACE</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>SPACE</kbd> toggles the checkbox again.</p>
|
||||
|
||||
<div class="btn-group" data-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
<input type="checkbox" checked autocomplete="off"> Checkbox 1 (pre-checked)
|
||||
</label>
|
||||
<label class="btn btn-primary">
|
||||
<input type="checkbox" autocomplete="off"> Checkbox 2
|
||||
</label>
|
||||
<label class="btn btn-primary">
|
||||
<input type="checkbox" autocomplete="off"> Checkbox 3
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p>Navigate to the radio button group with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>TAB</kbd> or "backwards" using <kbd>SHIFT + TAB</kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd>←</kbd> and <kbd>→</kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd>←</kbd> and <kbd>→</kbd> change the selected radio button again.</p>
|
||||
|
||||
<div class="btn-group" data-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
<input type="radio" name="options" id="option1" autocomplete="off" checked> Radio 1 (preselected)
|
||||
</label>
|
||||
<label class="btn btn-primary">
|
||||
<input type="radio" name="options" id="option2" autocomplete="off"> Radio 2
|
||||
</label>
|
||||
<label class="btn btn-primary">
|
||||
<input type="radio" name="options" id="option3" autocomplete="off"> Radio 3
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/button.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,66 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Carousel</title>
|
||||
<style>
|
||||
.carousel-item {
|
||||
transition: transform 2s ease, opacity .5s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Carousel <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<p>The transition duration should be around 2s. Also, the carousel shouldn't slide when its window/tab is hidden. Check the console log.</p>
|
||||
|
||||
<div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
|
||||
<ol class="carousel-indicators">
|
||||
<li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
|
||||
<li data-target="#carousel-example-generic" data-slide-to="1"></li>
|
||||
<li data-target="#carousel-example-generic" data-slide-to="2"></li>
|
||||
</ol>
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item active">
|
||||
<img src="https://i.imgur.com/iEZgY7Y.jpg" alt="First slide">
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<img src="https://i.imgur.com/eNWn1Xs.jpg" alt="Second slide">
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<img src="https://i.imgur.com/Nm7xoti.jpg" alt="Third slide">
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#carousel-example-generic" role="button" data-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#carousel-example-generic" role="button" data-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Next</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/carousel.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
var t0, t1;
|
||||
|
||||
// Test to show that the carousel doesn't slide when the current tab isn't visible
|
||||
// Test to show that transition-duration can be changed with css
|
||||
$('#carousel-example-generic').on('slid.bs.carousel', function(event) {
|
||||
t1 = performance.now()
|
||||
console.log('transition-duration took' + (t1 - t0) + 'ms, slid at ', event.timeStamp)
|
||||
}).on('slide.bs.carousel', function() {
|
||||
t0 = performance.now()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,78 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Collapse</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Collapse <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<div id="accordion" role="tablist">
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingOne">
|
||||
<h5 class="mb-0">
|
||||
<a data-toggle="collapse" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Collapsible Group Item #1
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div id="collapseOne" class="collapse show" data-parent="#accordion" role="tabpanel" aria-labelledby="headingOne">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingTwo">
|
||||
<h5 class="mb-0">
|
||||
<a class="collapsed" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Collapsible Group Item #2
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseTwo" class="collapse" data-parent="#accordion" role="tabpanel" aria-labelledby="headingTwo">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingThree">
|
||||
<h5 class="mb-0">
|
||||
<a class="collapsed" data-toggle="collapse" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Collapsible Group Item #3
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseThree" class="collapse" data-parent="#accordion" role="tabpanel" aria-labelledby="headingThree">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingFour">
|
||||
<h5 class="mb-0">
|
||||
<a class="collapsed" data-toggle="collapse" href="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
Collapsible Group Item with XSS in data-parent
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseFour" class="collapse" data-parent="<img src=1 onerror=alert(123) />" role="tabpanel" aria-labelledby="headingFour">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/collapse.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,212 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Dropdown</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Dropdown <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">Navbar</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<ul class="nav nav-pills mt-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">Active</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="https://example.com" id="dropdown2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown2">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 mt-4">
|
||||
<div class="btn-group dropup">
|
||||
<button type="button" class="btn btn-secondary">Dropup split</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Dropup split</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group dropup">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropup</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
This dropdown's menu is right-aligned
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 mt-4">
|
||||
<div class="btn-group dropup" role="group">
|
||||
<a href="#" class="btn btn-secondary">Dropup split align right</a>
|
||||
<button type="button" id="dropdown-page-subheader-button-3" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Product actions</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here with a long text</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropup align right</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here with a long text</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 mt-4">
|
||||
<div class="btn-group dropright" role="group">
|
||||
<a href="#" class="btn btn-secondary">Dropright split</a>
|
||||
<button type="button" id="dropdown-page-subheader-button-4" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Product actions</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here with a long text</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group dropright">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuRight" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Dropright
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuRight">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- dropleft -->
|
||||
<div class="btn-group dropleft" role="group">
|
||||
<a href="#" class="btn btn-secondary">Dropleft split</a>
|
||||
<button type="button" id="dropdown-page-subheader-button-5" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Product actions</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here with a long text</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group dropleft">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropleftMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Dropleft
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropleftMenu">
|
||||
<button class="dropdown-item" type="button">Action</button>
|
||||
<button class="dropdown-item" type="button">Another action</button>
|
||||
<button class="dropdown-item" type="button">Something else here</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-3 mt-4">
|
||||
<div class="btn-group dropdown">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" data-offset="10,20">Dropdown offset</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mt-4">
|
||||
<div class="btn-group dropdown">
|
||||
<button type="button" class="btn btn-secondary">Dropdown reference</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-reference="parent">
|
||||
<span class="sr-only">Dropdown split</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mt-4">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="false">
|
||||
Dropdown menu without Popper.js
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/dropdown.js"></script>
|
||||
<script src="../../dist/collapse.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,268 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Modal</title>
|
||||
<style>
|
||||
#tall {
|
||||
height: 1500px;
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-full navbar-dark bg-dark">
|
||||
<button class="navbar-toggler hidden-lg-up" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"></button>
|
||||
<div class="collapse navbar-expand-md" id="navbarResponsive">
|
||||
<a class="navbar-brand" href="#">This shouldn't jump!</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-3">
|
||||
<h1>Modal <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h4>Text in a modal</h4>
|
||||
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula.</p>
|
||||
|
||||
<h4>Popover in a modal</h4>
|
||||
<p>This <button type="button" class="btn btn-primary" data-toggle="popover" data-placement="left" title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">button</button> should trigger a popover on click.</p>
|
||||
|
||||
|
||||
<h4>Tooltips in a modal</h4>
|
||||
<p><a href="#" data-toggle="tooltip" data-placement="top" title="Tooltip on top">This link</a> and <a href="#" data-toggle="tooltip" data-placement="bottom" title="Tooltip on bottom">that link</a> should have tooltips on hover.</p>
|
||||
|
||||
<div id="accordion" role="tablist">
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingOne">
|
||||
<h5 class="mb-0">
|
||||
<a data-toggle="collapse" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Collapsible Group Item #1
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div id="collapseOne" class="collapse show" data-parent="#accordion" role="tabpanel" aria-labelledby="headingOne">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingTwo">
|
||||
<h5 class="mb-0">
|
||||
<a class="collapsed" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Collapsible Group Item #2
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseTwo" class="collapse" data-parent="#accordion" role="tabpanel" aria-labelledby="headingTwo">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" role="tab" id="headingThree">
|
||||
<h5 class="mb-0">
|
||||
<a class="collapsed" data-toggle="collapse" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Collapsible Group Item #3
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseThree" class="collapse" data-parent="#accordion" role="tabpanel" aria-labelledby="headingThree">
|
||||
<div class="card-body">
|
||||
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>Overflowing text to show scroll behavior</h4>
|
||||
<p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
|
||||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>
|
||||
<p>Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.</p>
|
||||
<p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
|
||||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>
|
||||
<p>Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.</p>
|
||||
<p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
|
||||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>
|
||||
<p>Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="firefoxModal" tabindex="-1" role="dialog" aria-labelledby="firefoxModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="firefoxModalLabel">Firefox Bug Test</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ol>
|
||||
<li>Ensure you're using Firefox.</li>
|
||||
<li>Open a new tab and then switch back to this tab.</li>
|
||||
<li>Click into this input: <input type="text" id="ff-bug-input"></li>
|
||||
<li>Switch to the other tab and then back to this tab.</li>
|
||||
</ol>
|
||||
<p>Test result: <strong id="ff-bug-test-result"></strong></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="slowModal" tabindex="-1" role="dialog" aria-labelledby="slowModalLabel" aria-hidden="true" style="transition-duration: 5s;">
|
||||
<div class="modal-dialog" role="document" style="transition-duration: inherit;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="slowModalLabel">Lorem slowly</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec sed odio dui. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
|
||||
Launch demo modal
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-primary btn-lg" id="tall-toggle">
|
||||
Toggle tall <body> content
|
||||
</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-lg" data-toggle="modal" data-target="#firefoxModal">
|
||||
Launch Firefox bug test modal
|
||||
</button>
|
||||
(<a href="https://github.com/twbs/bootstrap/issues/18365">See Issue #18365</a>)
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-lg" data-toggle="modal" data-target="#slowModal">
|
||||
Launch modal with slow transition
|
||||
</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div class="bg-dark text-white p-2" id="tall" style="display: none;">
|
||||
Tall body content to force the page to have a scrollbar.
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-lg" data-toggle="modal" data-target="<div class="modal fade the-bad" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><h4 class="modal-title">The Bad Modal</h4></div><div class="modal-body">This modal's HTTML source code is declared inline, inside the data-target attribute of it's show-button</div></div></div></div>">
|
||||
Modal with an XSS inside the data-target
|
||||
</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="button" class="btn btn-secondary btn-lg" id="btnPreventModal">
|
||||
Launch prevented modal on hide (to see the result open your console)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/modal.js"></script>
|
||||
<script src="../../dist/collapse.js"></script>
|
||||
<script src="../../dist/tooltip.js"></script>
|
||||
<script src="../../dist/popover.js"></script>
|
||||
|
||||
<script>
|
||||
var firefoxTestDone = false
|
||||
function reportFirefoxTestResult(result) {
|
||||
if (!firefoxTestDone) {
|
||||
$('#ff-bug-test-result')
|
||||
.addClass(result ? 'text-success' : 'text-danger')
|
||||
.text(result ? 'PASS' : 'FAIL')
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$('[data-toggle="popover"]').popover()
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
$('#tall-toggle').click(function () {
|
||||
$('#tall').toggle()
|
||||
})
|
||||
|
||||
$('#ff-bug-input').one('focus', function () {
|
||||
$('#firefoxModal').on('focus', reportFirefoxTestResult.bind(false))
|
||||
$('#ff-bug-input').on('focus', reportFirefoxTestResult.bind(true))
|
||||
})
|
||||
|
||||
$('#btnPreventModal').on('click', function () {
|
||||
$('#firefoxModal').one('shown.bs.modal', function () {
|
||||
$(this).modal('hide')
|
||||
})
|
||||
.one('hide.bs.modal', function (event) {
|
||||
event.preventDefault()
|
||||
if ($(this).data('bs.modal')._isTransitioning) {
|
||||
console.error('Modal plugin should not set _isTransitioning when hide event is prevented')
|
||||
} else {
|
||||
console.log('Test passed')
|
||||
$(this).modal('hide') // work as expected
|
||||
}
|
||||
})
|
||||
.modal('show')
|
||||
})
|
||||
|
||||
// Test transition duration
|
||||
var t0, t1;
|
||||
|
||||
$('#slowModal').on('shown.bs.modal', function(){
|
||||
t1 = performance.now()
|
||||
console.log('transition-duration took ' + (t1 - t0) + 'ms.')
|
||||
}).on('show.bs.modal', function(){
|
||||
t0 = performance.now()
|
||||
})
|
||||
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Popover</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Popover <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-container="body" data-toggle="popover" data-placement="auto" data-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
|
||||
Popover on auto
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-container="body" data-toggle="popover" data-placement="top" data-content="Default placement was on top but not enough place">
|
||||
Popover on top
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-container="body" data-toggle="popover" data-placement="right" data-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
|
||||
Popover on right
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-container="body" data-toggle="popover" data-placement="bottom" data-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
|
||||
Popover on bottom
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-container="body" data-toggle="popover" data-placement="left" data-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
|
||||
Popover on left
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/tooltip.js"></script>
|
||||
<script src="../../dist/popover.js"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="popover"]').popover()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,95 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Scrollspy</title>
|
||||
<style>
|
||||
body { padding-top: 70px; }
|
||||
</style>
|
||||
</head>
|
||||
<body data-spy="scroll" data-target=".navbar" data-offset="70">
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
<a class="navbar-brand" href="#">Scrollspy test</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#fat">@fat</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#mdo">@mdo</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown">
|
||||
<a class="dropdown-item" href="#one">One</a>
|
||||
<a class="dropdown-item" href="#two">Two</a>
|
||||
<a class="dropdown-item" href="#three">Three</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#final">Final</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<h2 id="fat">@fat</h2>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<hr>
|
||||
<h2 id="mdo">@mdo</h2>
|
||||
<p>Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard. Freegan beard aliqua cupidatat mcsweeney's vero. Cupidatat four loko nisi, ea helvetica nulla carles. Tattooed cosby sweater food truck, mcsweeney's quis non freegan vinyl. Lo-fi wes anderson +1 sartorial. Carles non aesthetic exercitation quis gentrify. Brooklyn adipisicing craft beer vice keytar deserunt.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<hr>
|
||||
<h2 id="one">one</h2>
|
||||
<p>Occaecat commodo aliqua delectus. Fap craft beer deserunt skateboard ea. Lomo bicycle rights adipisicing banh mi, velit ea sunt next level locavore single-origin coffee in magna veniam. High life id vinyl, echo park consequat quis aliquip banh mi pitchfork. Vero VHS est adipisicing. Consectetur nisi DIY minim messenger bag. Cred ex in, sustainable delectus consectetur fanny pack iphone.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<hr>
|
||||
<h2 id="two">two</h2>
|
||||
<p>In incididunt echo park, officia deserunt mcsweeney's proident master cleanse thundercats sapiente veniam. Excepteur VHS elit, proident shoreditch +1 biodiesel laborum craft beer. Single-origin coffee wayfarers irure four loko, cupidatat terry richardson master cleanse. Assumenda you probably haven't heard of them art party fanny pack, tattooed nulla cardigan tempor ad. Proident wolf nesciunt sartorial keffiyeh eu banh mi sustainable. Elit wolf voluptate, lo-fi ea portland before they sold out four loko. Locavore enim nostrud mlkshk brooklyn nesciunt.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<hr>
|
||||
<h2 id="three">three</h2>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Keytar twee blog, culpa messenger bag marfa whatever delectus food truck. Sapiente synth id assumenda. Locavore sed helvetica cliche irony, thundercats you probably haven't heard of them consequat hoodie gluten-free lo-fi fap aliquip. Labore elit placeat before they sold out, terry richardson proident brunch nesciunt quis cosby sweater pariatur keffiyeh ut helvetica artisan. Cardigan craft beer seitan readymade velit. VHS chambray laboris tempor veniam. Anim mollit minim commodo ullamco thundercats.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
|
||||
<hr>
|
||||
<h2 id="final">Final section</h2>
|
||||
<p>Ad leggings keytar, brunch id art party dolor labore.</p>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/scrollspy.js"></script>
|
||||
<script src="../../dist/dropdown.js"></script>
|
||||
<script src="../../dist/collapse.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,234 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Tab</title>
|
||||
<style>
|
||||
h4 {
|
||||
margin: 40px 0 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Tab <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<h4>Tabs without fade</h4>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#home" role="tab">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#profile" role="tab">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown">
|
||||
<a class="dropdown-item" data-toggle="tab" href="#fat" role="tab">@fat</a>
|
||||
<a class="dropdown-item" data-toggle="tab" href="#mdo" role="tab">@mdo</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" role="tablist">
|
||||
<div class="tab-pane active" id="home" role="tabpanel">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="profile" role="tabpanel">
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="fat" role="tabpanel">
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="mdo" role="tabpanel">
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Tabs with fade</h4>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#home2" role="tab">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#profile2" role="tab">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown2">
|
||||
<a class="dropdown-item" data-toggle="tab" href="#fat2" role="tab">@fat</a>
|
||||
<a class="dropdown-item" data-toggle="tab" href="#mdo2" role="tab">@mdo</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" role="tablist">
|
||||
<div class="tab-pane fade show active" id="home2" role="tabpanel">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="profile2" role="tabpanel">
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="fat2" role="tabpanel">
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="mdo2" role="tabpanel">
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Tabs without fade (no initially active pane)</h4>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#home3" role="tab">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#profile3" role="tab">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown3" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown3">
|
||||
<a class="dropdown-item" data-toggle="tab" href="#fat3" role="tab">@fat</a>
|
||||
<a class="dropdown-item" data-toggle="tab" href="#mdo3" role="tab">@mdo</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" role="tablist">
|
||||
<div class="tab-pane" id="home3" role="tabpanel">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="profile3" role="tabpanel">
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="fat3" role="tabpanel">
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="mdo3" role="tabpanel">
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Tabs with fade (no initially active pane)</h4>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#home4" role="tab">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#profile4" role="tab">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown4" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown4">
|
||||
<a class="dropdown-item" data-toggle="tab" href="#fat4" role="tab">@fat</a>
|
||||
<a class="dropdown-item" data-toggle="tab" href="#mdo4" role="tab">@mdo</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade" id="home4" role="tabpanel">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="profile4" role="tabpanel">
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="fat4" role="tabpanel">
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="mdo4" role="tabpanel">
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Tabs with nav (with fade)</h4>
|
||||
<nav class="nav nav-pills">
|
||||
<a class="nav-link nav-item active" data-toggle="tab" href="#home5">Home</a>
|
||||
<a class="nav-link nav-item" data-toggle="tab" href="#profile5">Profile</a>
|
||||
<div class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown5" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown5">
|
||||
<a class="dropdown-item" data-toggle="tab" href="#fat5">@fat</a>
|
||||
<a class="dropdown-item" data-toggle="tab" href="#mdo5">@mdo</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="nav-link nav-item disabled" href="#">Disabled</a>
|
||||
</nav>
|
||||
|
||||
<div class="tab-content" role="tabpanel">
|
||||
<div role="tabpanel" class="tab-pane fade show active" id="home5">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane fade" id="profile5">
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="fat5" role="tabpanel">
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="mdo5" role="tabpanel">
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Tabs with list-group (with fade)</h4>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="list-group" id="list-tab" role="tablist">
|
||||
<a class="list-group-item list-group-item-action active" id="list-home-list" data-toggle="tab" href="#list-home" role="tab" aria-controls="list-home">Home</a>
|
||||
<a class="list-group-item list-group-item-action" id="list-profile-list" data-toggle="tab" href="#list-profile" role="tab" aria-controls="list-profile">Profile</a>
|
||||
<a class="list-group-item list-group-item-action" id="list-messages-list" data-toggle="tab" href="#list-messages" role="tab" aria-controls="list-messages">Messages</a>
|
||||
<a class="list-group-item list-group-item-action" id="list-settings-list" data-toggle="tab" href="#list-settings" role="tab" aria-controls="list-settings">Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<div class="tab-pane fade show active" id="list-home" role="tabpanel" aria-labelledby="list-home-list">
|
||||
<p>Velit aute mollit ipsum ad dolor consectetur nulla officia culpa adipisicing exercitation fugiat tempor. Voluptate deserunt sit sunt nisi aliqua fugiat proident ea ut. Mollit voluptate reprehenderit occaecat nisi ad non minim tempor sunt voluptate consectetur exercitation id ut nulla. Ea et fugiat aliquip nostrud sunt incididunt consectetur culpa aliquip eiusmod dolor. Anim ad Lorem aliqua in cupidatat nisi enim eu nostrud do aliquip veniam minim.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="list-profile" role="tabpanel" aria-labelledby="list-profile-list">
|
||||
<p>Cupidatat quis ad sint excepteur laborum in esse qui. Et excepteur consectetur ex nisi eu do cillum ad laborum. Mollit et eu officia dolore sunt Lorem culpa qui commodo velit ex amet id ex. Officia anim incididunt laboris deserunt anim aute dolor incididunt veniam aute dolore do exercitation. Dolor nisi culpa ex ad irure in elit eu dolore. Ad laboris ipsum reprehenderit irure non commodo enim culpa commodo veniam incididunt veniam ad.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="list-messages" role="tabpanel" aria-labelledby="list-messages-list">
|
||||
<p>Ut ut do pariatur aliquip aliqua aliquip exercitation do nostrud commodo reprehenderit aute ipsum voluptate. Irure Lorem et laboris nostrud amet cupidatat cupidatat anim do ut velit mollit consequat enim tempor. Consectetur est minim nostrud nostrud consectetur irure labore voluptate irure. Ipsum id Lorem sit sint voluptate est pariatur eu ad cupidatat et deserunt culpa sit eiusmod deserunt. Consectetur et fugiat anim do eiusmod aliquip nulla laborum elit adipisicing pariatur cillum.</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="list-settings" role="tabpanel" aria-labelledby="list-settings-list">
|
||||
<p>Irure enim occaecat labore sit qui aliquip reprehenderit amet velit. Deserunt ullamco ex elit nostrud ut dolore nisi officia magna sit occaecat laboris sunt dolor. Nisi eu minim cillum occaecat aute est cupidatat aliqua labore aute occaecat ea aliquip sunt amet. Aute mollit dolor ut exercitation irure commodo non amet consectetur quis amet culpa. Quis ullamco nisi amet qui aute irure eu. Magna labore dolor quis ex labore id nostrud deserunt dolor eiusmod eu pariatur culpa mollit in irure.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/tab.js"></script>
|
||||
<script src="../../dist/dropdown.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,80 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../../../dist/css/bootstrap.min.css">
|
||||
<title>Tooltip</title>
|
||||
<style>
|
||||
#target {
|
||||
border: 1px solid;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
border: 1px solid;
|
||||
margin-left: 50px;
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Tooltip <small>Bootstrap Visual Test</small></h1>
|
||||
|
||||
<p class="text-muted">Tight pants next level keffiyeh <a href="#" data-toggle="tooltip" title="Default tooltip">you probably</a> haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american apparel <a href="#" data-toggle="tooltip" title="Another tooltip">have a</a> terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan <a href="#" data-toggle="tooltip" title="Another one here too">whatever keytar</a>, scenester farm-to-table banksy Austin <a href="#" data-toggle="tooltip" title="The last tip!">twitter handle</a> freegan cred raw denim single-origin coffee viral.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<p>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="auto" title="Tooltip on auto">
|
||||
Tooltip on auto
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top">
|
||||
Tooltip on top
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="right" title="Tooltip on right">
|
||||
Tooltip on right
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="bottom" title="Tooltip on bottom">
|
||||
Tooltip on bottom
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="left" title="Tooltip on left">
|
||||
Tooltip on left
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="left" title="Tooltip with XSS" data-container="<img src=1 onerror=alert(123) />">
|
||||
Tooltip with XSS
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="left" title="Tooltip with container" data-container="#customContainer">
|
||||
Tooltip with container
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-html="true" title="<em>Tooltip</em> <u>with</u> <b>HTML</b>">
|
||||
Tooltip with HTML
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div id="target" title="Test tooltip on transformed element"></div>
|
||||
<div id="customContainer"></div>
|
||||
</div>
|
||||
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../../site/docs/4.1/assets/js/vendor/popper.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/tooltip.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
$('#target').tooltip({
|
||||
placement : 'top',
|
||||
trigger : 'manual'
|
||||
}).tooltip('show')
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,18 @@
|
||||
# set env vars usually set by MyGet (enable for local testing)
|
||||
#$env:SourcesPath = '..'
|
||||
#$env:NuGet = "./nuget.exe" #https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
||||
|
||||
$nuget = $env:NuGet
|
||||
|
||||
# parse the version number out of package.json
|
||||
$bsversionParts = ((Get-Content $env:SourcesPath\package.json) -join "`n" | ConvertFrom-Json).version.split('-', 2) # split the version on the '-'
|
||||
$bsversion = $bsversionParts[0]
|
||||
|
||||
if ($bsversionParts.Length -gt 1)
|
||||
{
|
||||
$bsversion += '-' + $bsversionParts[1].replace('.', '').replace('-', '_') # strip out invalid chars from the PreRelease part
|
||||
}
|
||||
|
||||
# create packages
|
||||
& $nuget pack "$env:SourcesPath\nuget\bootstrap.nuspec" -Verbosity detailed -NonInteractive -NoPackageAnalysis -BasePath $env:SourcesPath -Version $bsversion
|
||||
& $nuget pack "$env:SourcesPath\nuget\bootstrap.sass.nuspec" -Verbosity detailed -NonInteractive -NoPackageAnalysis -BasePath $env:SourcesPath -Version $bsversion
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>bootstrap</id>
|
||||
<version>4.1.0</version>
|
||||
<title>Bootstrap CSS</title>
|
||||
<authors>The Bootstrap Authors, Twitter Inc.</authors>
|
||||
<owners>bootstrap</owners>
|
||||
<description>The most popular front-end framework for developing responsive, mobile first projects on the web.</description>
|
||||
<releaseNotes>https://blog.getbootstrap.com/</releaseNotes>
|
||||
<summary>Bootstrap framework in CSS. Includes fonts and JavaScript</summary>
|
||||
<language>en-us</language>
|
||||
<projectUrl>https://getbootstrap.com/</projectUrl>
|
||||
<iconUrl>https://getbootstrap.com/docs/4.1/assets/img/favicons/apple-touch-icon.png</iconUrl>
|
||||
<licenseUrl>https://github.com/twbs/bootstrap/blob/master/LICENSE</licenseUrl>
|
||||
<copyright>Copyright 2017-2018</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<dependencies>
|
||||
<dependency id="jQuery" version="[3.0.0,4)" />
|
||||
<dependency id="popper.js" version="[1.14.0,2)" />
|
||||
</dependencies>
|
||||
<tags>css mobile-first responsive front-end framework web</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="dist\css\*.*" target="content\Content" />
|
||||
<file src="dist\js\bootstrap*.js" target="content\Scripts" />
|
||||
<file src="dist\js\bootstrap*.js.map" target="content\Scripts" />
|
||||
|
||||
<file src="dist\css\*.*" target="contentFiles\Content" />
|
||||
<file src="dist\js\bootstrap*.js" target="contentFiles\Scripts" />
|
||||
<file src="dist\js\bootstrap*.js.map" target="contentFiles\Scripts" />
|
||||
</files>
|
||||
</package>
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>bootstrap.sass</id>
|
||||
<version>4.1.0</version>
|
||||
<title>Bootstrap Sass</title>
|
||||
<authors>The Bootstrap Authors, Twitter Inc.</authors>
|
||||
<owners>bootstrap</owners>
|
||||
<description>The most popular front-end framework for developing responsive, mobile first projects on the web.</description>
|
||||
<releaseNotes>https://blog.getbootstrap.com/</releaseNotes>
|
||||
<summary>Bootstrap framework in Sass. Includes fonts and JavaScript</summary>
|
||||
<language>en-us</language>
|
||||
<projectUrl>https://getbootstrap.com/</projectUrl>
|
||||
<iconUrl>https://getbootstrap.com/docs/4.1/assets/img/favicons/apple-touch-icon.png</iconUrl>
|
||||
<licenseUrl>https://github.com/twbs/bootstrap/blob/master/LICENSE</licenseUrl>
|
||||
<copyright>Copyright 2017-2018</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<dependencies>
|
||||
<dependency id="jQuery" version="[3.0.0,4)" />
|
||||
<dependency id="popper.js" version="[1.14.0,2)" />
|
||||
</dependencies>
|
||||
<tags>css sass mobile-first responsive front-end framework web</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="scss\**\*.scss" target="content\Content\bootstrap" />
|
||||
<file src="dist\js\bootstrap*.js" target="content\Scripts" />
|
||||
<file src="dist\js\bootstrap*.js.map" target="content\Scripts" />
|
||||
|
||||
<file src="scss\**\*.scss" target="contentFiles\Content\bootstrap" />
|
||||
<file src="dist\js\bootstrap*.js" target="contentFiles\Scripts" />
|
||||
<file src="dist\js\bootstrap*.js.map" target="contentFiles\Scripts" />
|
||||
</files>
|
||||
</package>
|
12814
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/package-lock.json
generated
Normal file
12814
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/package.js
vendored
Normal file
19
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/package.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// package metadata file for Meteor.js
|
||||
|
||||
/* eslint-env meteor */
|
||||
|
||||
Package.describe({
|
||||
name: 'twbs:bootstrap', // https://atmospherejs.com/twbs/bootstrap
|
||||
summary: 'The most popular front-end framework for developing responsive, mobile first projects on the web.',
|
||||
version: '4.1.3',
|
||||
git: 'https://github.com/twbs/bootstrap.git'
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.versionsFrom('METEOR@1.0');
|
||||
api.use('jquery', 'client');
|
||||
api.addFiles([
|
||||
'dist/css/bootstrap.css',
|
||||
'dist/js/bootstrap.js'
|
||||
], 'client');
|
||||
});
|
@ -0,0 +1,217 @@
|
||||
{
|
||||
"name": "bootstrap",
|
||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
||||
"version": "4.1.3",
|
||||
"keywords": [
|
||||
"css",
|
||||
"sass",
|
||||
"mobile-first",
|
||||
"responsive",
|
||||
"front-end",
|
||||
"framework",
|
||||
"web"
|
||||
],
|
||||
"homepage": "https://getbootstrap.com/",
|
||||
"author": "The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)",
|
||||
"contributors": [
|
||||
"Twitter, Inc."
|
||||
],
|
||||
"scripts": {
|
||||
"start": "npm-run-all --parallel watch docs-serve",
|
||||
"blc": "blc --exclude-external --filter-level 3 --get --ordered --recursive --host-requests 4 --input http://localhost:3000/",
|
||||
"http-server": "http-server --silent -p 3000",
|
||||
"bundlesize": "bundlesize",
|
||||
"check-broken-links": "npm-run-all --parallel --race \"http-server -- _gh_pages/\" blc",
|
||||
"css": "npm-run-all --parallel css-lint* css-compile* --sequential css-prefix* css-minify*",
|
||||
"css-main": "npm-run-all --parallel css-lint css-compile --sequential css-prefix css-minify css-copy",
|
||||
"css-docs": "npm-run-all --parallel css-lint-docs css-compile-docs --sequential css-prefix-docs css-minify-docs",
|
||||
"css-compile": "node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/bootstrap.scss dist/css/bootstrap.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/bootstrap-grid.scss dist/css/bootstrap-grid.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/bootstrap-reboot.scss dist/css/bootstrap-reboot.css",
|
||||
"css-compile-docs": "node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 site/docs/4.1/assets/scss/docs.scss site/docs/4.1/assets/css/docs.min.css",
|
||||
"css-copy": "shx mkdir -p site/docs/4.1/dist/ && shx cp -r dist/css/ site/docs/4.1/dist/",
|
||||
"css-lint": "stylelint --syntax scss \"scss/**/*.scss\"",
|
||||
"css-lint-docs": "stylelint --syntax scss \"site/docs/4.1/assets/scss/*.scss\" && stylelint \"docs/**/*.css\"",
|
||||
"css-lint-vars": "node build/lint-vars.js scss/ site/docs/4.1/assets/scss/",
|
||||
"css-prefix": "postcss --config build/postcss.config.js --replace \"dist/css/*.css\" \"!dist/css/*.min.css\"",
|
||||
"css-prefix-docs": "postcss --config build/postcss.config.js --replace \"site/docs/4.1/assets/css/docs.min.css\" \"docs/**/*.css\"",
|
||||
"css-minify": "cleancss --level 1 --source-map --source-map-inline-sources --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss --level 1 --source-map --source-map-inline-sources --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss --level 1 --source-map --source-map-inline-sources --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css",
|
||||
"css-minify-docs": "cleancss --level 1 --source-map --source-map-inline-sources --output site/docs/4.1/assets/css/docs.min.css site/docs/4.1/assets/css/docs.min.css",
|
||||
"js": "npm-run-all js-lint* js-compile js-minify js-copy",
|
||||
"js-copy": "shx mkdir -p site/docs/4.1/dist/ && shx cp -r dist/js/ site/docs/4.1/dist/",
|
||||
"js-main": "npm-run-all js-lint js-compile js-minify",
|
||||
"js-docs": "npm-run-all js-lint-docs js-minify-docs",
|
||||
"js-lint": "eslint js/src js/tests build/",
|
||||
"js-lint-docs": "eslint site/docs/4.1/assets/js/ site/sw.js",
|
||||
"js-compile": "npm-run-all --parallel js-compile-* --sequential js-copy",
|
||||
"js-compile-standalone": "rollup --environment BUNDLE:false --config build/rollup.config.js --sourcemap",
|
||||
"js-compile-bundle": "rollup --environment BUNDLE:true --config build/rollup.config.js --sourcemap",
|
||||
"js-compile-plugins": "node build/build-plugins.js",
|
||||
"js-compile-plugins-coverage": "cross-env NODE_ENV=test node build/build-plugins.js",
|
||||
"js-minify": "npm-run-all --parallel js-minify-*",
|
||||
"js-minify-standalone": "uglifyjs --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
|
||||
"js-minify-bundle": "uglifyjs --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
|
||||
"js-minify-docs": "uglifyjs --mangle --comments \"/^!/\" --output site/docs/4.1/assets/js/docs.min.js site/docs/4.1/assets/js/vendor/anchor.min.js site/docs/4.1/assets/js/vendor/clipboard.min.js site/docs/4.1/assets/js/vendor/holder.min.js \"site/docs/4.1/assets/js/src/*.js\"",
|
||||
"js-test": "npm-run-all js-test-karma*",
|
||||
"js-test-karma": "karma start js/tests/karma.conf.js",
|
||||
"js-test-karma-old": "cross-env USE_OLD_JQUERY=true npm run js-test-karma",
|
||||
"js-test-karma-bundle": "cross-env karma start js/tests/karma-bundle.conf.js",
|
||||
"js-test-karma-bundle-old": "cross-env USE_OLD_JQUERY=true npm run js-test-karma-bundle",
|
||||
"js-test-cloud": "npm-run-all --parallel --race http-server saucelabs-test",
|
||||
"coveralls": "shx cat js/coverage/lcov.info | coveralls",
|
||||
"docs": "npm-run-all --parallel css-docs js-docs --sequential docs-compile docs-lint",
|
||||
"docs-compile": "bundle exec jekyll build",
|
||||
"postdocs-compile": "npm run docs-workbox-precache",
|
||||
"docs-github": "shx echo \"github: true\" > twbsconfig.yml && npm run docs-compile -- --config _config.yml,twbsconfig.yml && shx rm ./twbsconfig.yml",
|
||||
"docs-github-serve": "npm run docs-serve -- --skip-initial-build --no-watch",
|
||||
"docs-lint": "npm-run-all docs-lint-*",
|
||||
"docs-lint-htmllint": "htmllint --rc build/.htmllintrc \"_gh_pages/**/*.html\" \"js/tests/**/*.html\"",
|
||||
"docs-lint-vnu-jar": "node build/vnu-jar.js",
|
||||
"docs-serve": "bundle exec jekyll serve",
|
||||
"docs-workbox-precache": "node build/workbox.js",
|
||||
"maintenance-dependencies": "ncu -a -x jquery && npm update && bundle update && shx echo \"Manually update site/docs/4.1/assets/js/vendor/*, js/tests/vendor/* and .travis.yml\"",
|
||||
"release-sri": "node build/generate-sri.js",
|
||||
"release-version": "node build/change-version.js",
|
||||
"release-zip": "cd dist/ && zip -r9 bootstrap-$npm_package_version-dist.zip * && shx mv bootstrap-$npm_package_version-dist.zip ..",
|
||||
"saucelabs-test": "node build/saucelabs-unit-test.js",
|
||||
"dist": "npm-run-all --parallel css-main js",
|
||||
"test": "npm-run-all dist js-test docs-compile docs-lint bundlesize",
|
||||
"watch": "npm-run-all --parallel watch-*",
|
||||
"watch-css-main": "nodemon --watch scss/ --ext scss --exec \"npm run css-main\"",
|
||||
"watch-css-docs": "nodemon --watch site/docs/4.1/assets/scss/ --ext scss --exec \"npm run css-docs\"",
|
||||
"watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm run js-compile\"",
|
||||
"watch-js-docs": "nodemon --watch site/docs/4.1/assets/js/src/ --ext js --exec \"npm run js-docs\""
|
||||
},
|
||||
"style": "dist/css/bootstrap.css",
|
||||
"sass": "scss/bootstrap.scss",
|
||||
"main": "dist/js/bootstrap",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/twbs/bootstrap.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/twbs/bootstrap/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"jquery": "1.9.1 - 3",
|
||||
"popper.js": "^1.14.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.52",
|
||||
"@babel/core": "7.0.0-beta.52",
|
||||
"@babel/preset-env": "7.0.0-beta.52",
|
||||
"autoprefixer": "^8.6.5",
|
||||
"babel-eslint": "^8.2.5",
|
||||
"babel-plugin-istanbul": "^4.1.6",
|
||||
"broken-link-checker": "^0.7.8",
|
||||
"bundlesize": "^0.15.3",
|
||||
"clean-css-cli": "^4.1.11",
|
||||
"coveralls": "^3.0.2",
|
||||
"cross-env": "^5.2.0",
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-plugin-compat": "^2.4.0",
|
||||
"glob": "^7.1.2",
|
||||
"htmllint-cli": "^0.0.7",
|
||||
"http-server": "^0.11.1",
|
||||
"jsunitsaucelabs": "^1.3.2",
|
||||
"karma": "^2.0.4",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.0.1",
|
||||
"karma-detect-browsers": "^2.3.2",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-qunit": "^2.1.0",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"node-sass": "^4.9.1",
|
||||
"nodemon": "^1.17.5",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"popper.js": "^1.14.3",
|
||||
"postcss-cli": "^5.0.1",
|
||||
"qunit": "^2.6.1",
|
||||
"rollup": "^0.62.0",
|
||||
"rollup-plugin-babel": "4.0.0-beta.5",
|
||||
"rollup-plugin-node-resolve": "^3.3.0",
|
||||
"shelljs": "^0.8.2",
|
||||
"shx": "^0.3.1",
|
||||
"sinon": "^6.1.2",
|
||||
"sri-toolbox": "^0.2.0",
|
||||
"stylelint": "^9.3.0",
|
||||
"stylelint-config-recommended-scss": "^3.2.0",
|
||||
"stylelint-config-standard": "^18.2.0",
|
||||
"stylelint-order": "^0.8.1",
|
||||
"stylelint-scss": "^3.1.3",
|
||||
"uglify-js": "^3.4.3",
|
||||
"vnu-jar": "^18.3.0",
|
||||
"workbox-build": "^3.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"js/{src,dist}/",
|
||||
"scss/"
|
||||
],
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/css/bootstrap-grid.css",
|
||||
"maxSize": "5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-grid.min.css",
|
||||
"maxSize": "5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.css",
|
||||
"maxSize": "3 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.min.css",
|
||||
"maxSize": "3 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.css",
|
||||
"maxSize": "25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.min.css",
|
||||
"maxSize": "21 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.js",
|
||||
"maxSize": "45 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.min.js",
|
||||
"maxSize": "25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.js",
|
||||
"maxSize": "21 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.min.js",
|
||||
"maxSize": "15 kB"
|
||||
}
|
||||
],
|
||||
"jspm": {
|
||||
"registry": "npm",
|
||||
"main": "js/bootstrap",
|
||||
"directories": {
|
||||
"lib": "dist"
|
||||
},
|
||||
"shim": {
|
||||
"js/bootstrap": {
|
||||
"deps": [
|
||||
"jquery",
|
||||
"popper.js"
|
||||
],
|
||||
"exports": "$"
|
||||
}
|
||||
},
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"jquery": "1.9.1 - 3",
|
||||
"popper.js": "^1.14.1"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "bootstrap",
|
||||
"description": "The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.",
|
||||
"tags": ["bootstrap", "grid", "typography", "buttons", "ui", "responsive-web-design"]
|
||||
}
|
51
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/scss/_alert.scss
vendored
Normal file
51
YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/scss/_alert.scss
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.alert {
|
||||
position: relative;
|
||||
padding: $alert-padding-y $alert-padding-x;
|
||||
margin-bottom: $alert-margin-bottom;
|
||||
border: $alert-border-width solid transparent;
|
||||
@include border-radius($alert-border-radius);
|
||||
}
|
||||
|
||||
// Headings for larger alerts
|
||||
.alert-heading {
|
||||
// Specified to prevent conflicts of changing $headings-color
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
// Provide class for links that match alerts
|
||||
.alert-link {
|
||||
font-weight: $alert-link-font-weight;
|
||||
}
|
||||
|
||||
|
||||
// Dismissible alerts
|
||||
//
|
||||
// Expand the right padding and account for the close button's positioning.
|
||||
|
||||
.alert-dismissible {
|
||||
padding-right: ($close-font-size + $alert-padding-x * 2);
|
||||
|
||||
// Adjust close link position
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: $alert-padding-y $alert-padding-x;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Alternate styles
|
||||
//
|
||||
// Generate contextual modifier classes for colorizing the alert.
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.alert-#{$color} {
|
||||
@include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user