From 566865a2630e536f9462d985ad770ee017c2c8f5 Mon Sep 17 00:00:00 2001
From: Michael Mintz
Date: Fri, 26 Mar 2021 17:10:32 -0400
Subject: [PATCH 01/10] Refactor browser_launcher.py
---
seleniumbase/core/browser_launcher.py | 249 +++++++++++---------------
1 file changed, 102 insertions(+), 147 deletions(-)
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 26ed84d4..c3c8f48c 100755
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -1,21 +1,16 @@
-import json
import logging
import os
-import random
import re
import sys
import time
import urllib3
import warnings
from selenium import webdriver
-from selenium.common.exceptions import WebDriverException
-from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from seleniumbase.config import proxy_list
from seleniumbase.config import settings
from seleniumbase.core import download_helper
from seleniumbase.core import proxy_helper
from seleniumbase.fixtures import constants
-from seleniumbase.fixtures import page_utils
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
from seleniumbase import extensions # browser extensions storage folder
urllib3.disable_warnings()
@@ -98,6 +93,7 @@ def _add_chrome_proxy_extension(
""" Implementation of https://stackoverflow.com/a/35293284 for
https://stackoverflow.com/questions/12848327/
(Run Selenium on a proxy server that requires authentication.) """
+ import random
arg_join = " ".join(sys.argv)
if not ("-n" in sys.argv or "-n=" in arg_join or arg_join == "-c"):
# Single-threaded
@@ -267,7 +263,7 @@ def _set_chrome_options(
if proxy_auth:
chrome_options = _add_chrome_proxy_extension(
chrome_options, proxy_string, proxy_user, proxy_pass)
- chrome_options.add_argument('--proxy-server=%s' % proxy_string)
+ chrome_options.add_argument("--proxy-server=%s" % proxy_string)
if headless:
if not proxy_auth and not browser_name == constants.Browser.OPERA:
# Headless Chrome doesn't support extensions, which are
@@ -291,7 +287,7 @@ def _set_chrome_options(
# To access the Remote Debugger, go to: http://localhost:9222
# while a Chromium driver is running.
# Info: https://chromedevtools.github.io/devtools-protocol/
- chrome_options.add_argument('--remote-debugging-port=9222')
+ chrome_options.add_argument("--remote-debugging-port=9222")
if swiftshader:
chrome_options.add_argument("--use-gl=swiftshader")
else:
@@ -320,37 +316,38 @@ def _set_safari_capabilities():
return safari_capabilities
-def _create_firefox_profile(
- downloads_path, locale_code, proxy_string, user_agent, disable_csp):
- profile = webdriver.FirefoxProfile()
- profile.accept_untrusted_certs = True
- profile.set_preference("reader.parse-on-load.enabled", False)
- profile.set_preference("pdfjs.disabled", True)
- profile.set_preference("app.update.auto", False)
- profile.set_preference("app.update.enabled", False)
- profile.set_preference("app.update.silent", True)
- profile.set_preference("browser.formfill.enable", False)
- profile.set_preference("browser.privatebrowsing.autostart", True)
- profile.set_preference("devtools.errorconsole.enabled", True)
- profile.set_preference("dom.webnotifications.enabled", False)
- profile.set_preference("dom.disable_beforeunload", True)
- profile.set_preference("browser.contentblocking.database.enabled", False)
- profile.set_preference("extensions.allowPrivateBrowsingByDefault", True)
- profile.set_preference("extensions.PrivateBrowsing.notification", False)
- profile.set_preference("extensions.systemAddon.update.enabled", False)
- profile.set_preference("extensions.update.autoUpdateDefault", False)
- profile.set_preference("extensions.update.enabled", False)
- profile.set_preference("extensions.update.silent", True)
- profile.set_preference(
+def _set_firefox_options(
+ downloads_path, headless, locale_code,
+ proxy_string, user_agent, disable_csp):
+ options = webdriver.FirefoxOptions()
+ options.accept_untrusted_certs = True
+ options.set_preference("reader.parse-on-load.enabled", False)
+ options.set_preference("pdfjs.disabled", True)
+ options.set_preference("app.update.auto", False)
+ options.set_preference("app.update.enabled", False)
+ options.set_preference("app.update.silent", True)
+ options.set_preference("browser.formfill.enable", False)
+ options.set_preference("browser.privatebrowsing.autostart", True)
+ options.set_preference("devtools.errorconsole.enabled", True)
+ options.set_preference("dom.webnotifications.enabled", False)
+ options.set_preference("dom.disable_beforeunload", True)
+ options.set_preference("browser.contentblocking.database.enabled", False)
+ options.set_preference("extensions.allowPrivateBrowsingByDefault", True)
+ options.set_preference("extensions.PrivateBrowsing.notification", False)
+ options.set_preference("extensions.systemAddon.update.enabled", False)
+ options.set_preference("extensions.update.autoUpdateDefault", False)
+ options.set_preference("extensions.update.enabled", False)
+ options.set_preference("extensions.update.silent", True)
+ options.set_preference(
"datareporting.healthreport.logging.consoleEnabled", False)
- profile.set_preference("datareporting.healthreport.service.enabled", False)
- profile.set_preference(
+ options.set_preference("datareporting.healthreport.service.enabled", False)
+ options.set_preference(
"datareporting.healthreport.service.firstRun", False)
- profile.set_preference("datareporting.healthreport.uploadEnabled", False)
- profile.set_preference("datareporting.policy.dataSubmissionEnabled", False)
- profile.set_preference(
+ options.set_preference("datareporting.healthreport.uploadEnabled", False)
+ options.set_preference("datareporting.policy.dataSubmissionEnabled", False)
+ options.set_preference(
"datareporting.policy.dataSubmissionPolicyAccepted", False)
- profile.set_preference("toolkit.telemetry.unified", False)
+ options.set_preference("toolkit.telemetry.unified", False)
if proxy_string:
socks_proxy = False
socks_ver = 0
@@ -366,44 +363,46 @@ def _create_firefox_profile(
else:
proxy_server = proxy_string.split(':')[0]
proxy_port = proxy_string.split(':')[1]
- profile.set_preference("network.proxy.type", 1)
+ options.set_preference("network.proxy.type", 1)
if socks_proxy:
- profile.set_preference('network.proxy.socks', proxy_server)
- profile.set_preference('network.proxy.socks_port', int(proxy_port))
- profile.set_preference('network.proxy.socks_version', socks_ver)
+ options.set_preference('network.proxy.socks', proxy_server)
+ options.set_preference('network.proxy.socks_port', int(proxy_port))
+ options.set_preference('network.proxy.socks_version', socks_ver)
else:
- profile.set_preference("network.proxy.http", proxy_server)
- profile.set_preference("network.proxy.http_port", int(proxy_port))
- profile.set_preference("network.proxy.ssl", proxy_server)
- profile.set_preference("network.proxy.ssl_port", int(proxy_port))
+ options.set_preference("network.proxy.http", proxy_server)
+ options.set_preference("network.proxy.http_port", int(proxy_port))
+ options.set_preference("network.proxy.ssl", proxy_server)
+ options.set_preference("network.proxy.ssl_port", int(proxy_port))
if user_agent:
- profile.set_preference("general.useragent.override", user_agent)
- profile.set_preference(
+ options.set_preference("general.useragent.override", user_agent)
+ options.set_preference(
"security.mixed_content.block_active_content", False)
if settings.DISABLE_CSP_ON_FIREFOX or disable_csp:
- profile.set_preference("security.csp.enable", False)
- profile.set_preference(
+ options.set_preference("security.csp.enable", False)
+ options.set_preference(
"browser.download.manager.showAlertOnComplete", False)
+ if headless:
+ options.add_argument("--headless")
if locale_code:
- profile.set_preference("intl.accept_languages", locale_code)
- profile.set_preference("browser.shell.checkDefaultBrowser", False)
- profile.set_preference("browser.startup.page", 0)
- profile.set_preference("browser.download.panel.shown", False)
- profile.set_preference(
+ options.set_preference("intl.accept_languages", locale_code)
+ options.set_preference("browser.shell.checkDefaultBrowser", False)
+ options.set_preference("browser.startup.page", 0)
+ options.set_preference("browser.download.panel.shown", False)
+ options.set_preference(
"browser.download.animateNotifications", False)
- profile.set_preference("browser.download.dir", downloads_path)
- profile.set_preference("browser.download.folderList", 2)
- profile.set_preference("browser.helperApps.alwaysAsk.force", False)
- profile.set_preference(
+ options.set_preference("browser.download.dir", downloads_path)
+ options.set_preference("browser.download.folderList", 2)
+ options.set_preference("browser.helperApps.alwaysAsk.force", False)
+ options.set_preference(
"browser.download.manager.showWhenStarting", False)
- profile.set_preference(
+ options.set_preference(
"browser.helperApps.neverAsk.saveToDisk",
("application/pdf, application/zip, application/octet-stream, "
"text/csv, text/xml, application/xml, text/plain, "
"text/octet-stream, application/x-gzip, application/x-tar "
"application/"
"vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
- return profile
+ return options
def display_proxy_warning(proxy_string):
@@ -422,6 +421,7 @@ def display_proxy_warning(proxy_string):
def validate_proxy_string(proxy_string):
+ from seleniumbase.fixtures import page_utils
if proxy_string in proxy_list.PROXY_LIST.keys():
proxy_string = proxy_list.PROXY_LIST[proxy_string]
if not proxy_string:
@@ -464,7 +464,7 @@ def validate_proxy_string(proxy_string):
def get_driver(browser_name, headless=False, locale_code=None,
- use_grid=False, servername='localhost', port=4444,
+ use_grid=False, servername="localhost", port=4444,
proxy_string=None, user_agent=None,
cap_file=None, cap_string=None,
disable_csp=None, enable_ws=None, enable_sync=None,
@@ -540,6 +540,7 @@ def get_remote_driver(
from seleniumbase.core import capabilities_parser
desired_caps = capabilities_parser.get_desired_capabilities(cap_file)
if cap_string:
+ import json
try:
extra_caps = json.loads(cap_string)
except Exception as e:
@@ -568,52 +569,28 @@ def get_remote_driver(
capabilities = chrome_options.to_capabilities()
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
keep_alive=True)
elif browser_name == constants.Browser.FIREFOX:
- try:
- # Use Geckodriver for Firefox if it's on the PATH
- profile = _create_firefox_profile(
- downloads_path, locale_code,
- proxy_string, user_agent, disable_csp)
- firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
- firefox_capabilities['marionette'] = True
- if headless:
- firefox_capabilities['moz:firefoxOptions'] = (
- {'args': ['-headless']})
- for key in desired_caps.keys():
- firefox_capabilities[key] = desired_caps[key]
- capabilities = firefox_capabilities
- warnings.simplefilter("ignore", category=DeprecationWarning)
- return webdriver.Remote(
- command_executor=address,
- desired_capabilities=capabilities,
- browser_profile=profile,
- keep_alive=True)
- except WebDriverException:
- # Don't use Geckodriver: Only works for old versions of Firefox
- profile = _create_firefox_profile(
- downloads_path, locale_code,
- proxy_string, user_agent, disable_csp)
- firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
- firefox_capabilities['marionette'] = False
- if headless:
- firefox_capabilities['moz:firefoxOptions'] = (
- {'args': ['-headless']})
- for key in desired_caps.keys():
- firefox_capabilities[key] = desired_caps[key]
- capabilities = firefox_capabilities
- return webdriver.Remote(
- command_executor=address,
- desired_capabilities=capabilities,
- browser_profile=profile,
- keep_alive=True)
+ firefox_options = _set_firefox_options(
+ downloads_path, headless, locale_code,
+ proxy_string, user_agent, disable_csp)
+ capabilities = firefox_options.to_capabilities()
+ for key in desired_caps.keys():
+ capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
+ return webdriver.Remote(
+ command_executor=address,
+ desired_capabilities=capabilities,
+ keep_alive=True)
elif browser_name == constants.Browser.INTERNET_EXPLORER:
capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
@@ -622,6 +599,7 @@ def get_remote_driver(
capabilities = webdriver.DesiredCapabilities.EDGE
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
@@ -630,6 +608,7 @@ def get_remote_driver(
capabilities = webdriver.DesiredCapabilities.SAFARI
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
@@ -638,6 +617,7 @@ def get_remote_driver(
capabilities = webdriver.DesiredCapabilities.OPERA
for key in desired_caps.keys():
capabilities[key] = desired_caps[key]
+ warnings.simplefilter("ignore", category=DeprecationWarning)
return webdriver.Remote(
command_executor=address,
desired_capabilities=capabilities,
@@ -699,56 +679,31 @@ def get_local_driver(
downloads_path = download_helper.get_downloads_folder()
if browser_name == constants.Browser.FIREFOX:
- try:
+ firefox_options = _set_firefox_options(
+ downloads_path, headless, locale_code,
+ proxy_string, user_agent, disable_csp)
+ if LOCAL_GECKODRIVER and os.path.exists(LOCAL_GECKODRIVER):
try:
- # Use Geckodriver for Firefox if it's on the PATH
- profile = _create_firefox_profile(
- downloads_path, locale_code,
- proxy_string, user_agent, disable_csp)
- firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
- firefox_capabilities['marionette'] = True
- options = webdriver.FirefoxOptions()
- if headless:
- options.add_argument('-headless')
- firefox_capabilities['moz:firefoxOptions'] = (
- {'args': ['-headless']})
- if LOCAL_GECKODRIVER and os.path.exists(LOCAL_GECKODRIVER):
- try:
- make_driver_executable_if_not(LOCAL_GECKODRIVER)
- except Exception as e:
- logging.debug("\nWarning: Could not make geckodriver"
- " executable: %s" % e)
- elif not is_geckodriver_on_path():
- args = " ".join(sys.argv)
- if not ("-n" in sys.argv or "-n=" in args or args == "-c"):
- # (Not multithreaded)
- from seleniumbase.console_scripts import sb_install
- sys_args = sys.argv # Save a copy of current sys args
- print("\nWarning: geckodriver not found!"
- " Installing now:")
- try:
- sb_install.main(override="geckodriver")
- except Exception as e:
- print("\nWarning: Could not install geckodriver: "
- "%s" % e)
- sys.argv = sys_args # Put back the original sys args
- firefox_driver = webdriver.Firefox(
- firefox_profile=profile,
- capabilities=firefox_capabilities,
- options=options)
- except Exception:
- profile = _create_firefox_profile(
- downloads_path, locale_code,
- proxy_string, user_agent, disable_csp)
- firefox_capabilities = DesiredCapabilities.FIREFOX.copy()
- firefox_driver = webdriver.Firefox(
- firefox_profile=profile,
- capabilities=firefox_capabilities)
- return firefox_driver
- except Exception as e:
- if headless:
- raise Exception(e)
- return webdriver.Firefox()
+ make_driver_executable_if_not(LOCAL_GECKODRIVER)
+ except Exception as e:
+ logging.debug("\nWarning: Could not make geckodriver"
+ " executable: %s" % e)
+ elif not is_geckodriver_on_path():
+ args = " ".join(sys.argv)
+ if not ("-n" in sys.argv or "-n=" in args or args == "-c"):
+ # (Not multithreaded)
+ from seleniumbase.console_scripts import sb_install
+ sys_args = sys.argv # Save a copy of current sys args
+ print("\nWarning: geckodriver not found!"
+ " Installing now:")
+ try:
+ sb_install.main(override="geckodriver")
+ except Exception as e:
+ print("\nWarning: Could not install geckodriver: "
+ "%s" % e)
+ sys.argv = sys_args # Put back the original sys args
+ warnings.simplefilter("ignore", category=DeprecationWarning)
+ return webdriver.Firefox(options=firefox_options)
elif browser_name == constants.Browser.INTERNET_EXPLORER:
if not IS_WINDOWS:
raise Exception(
@@ -898,7 +853,7 @@ def get_local_driver(
if proxy_auth:
edge_options = _add_chrome_proxy_extension(
edge_options, proxy_string, proxy_user, proxy_pass)
- edge_options.add_argument('--proxy-server=%s' % proxy_string)
+ edge_options.add_argument("--proxy-server=%s" % proxy_string)
edge_options.add_argument("--test-type")
edge_options.add_argument("--log-level=3")
edge_options.add_argument("--no-first-run")
@@ -915,7 +870,7 @@ def get_local_driver(
# To access the Remote Debugger, go to: http://localhost:9222
# while a Chromium driver is running.
# Info: https://chromedevtools.github.io/devtools-protocol/
- edge_options.add_argument('--remote-debugging-port=9222')
+ edge_options.add_argument("--remote-debugging-port=9222")
if swiftshader:
edge_options.add_argument("--use-gl=swiftshader")
else:
From 3f506aa34c2c0c1fc7bc285c43a549e65df3c097 Mon Sep 17 00:00:00 2001
From: Michael Mintz
Date: Fri, 26 Mar 2021 17:15:42 -0400
Subject: [PATCH 02/10] Have "assert_text_not_visible()" return True if
successful
---
seleniumbase/fixtures/base_case.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 27a20009..0e7ee1ad 100755
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -6174,7 +6174,8 @@ class BaseCase(unittest.TestCase):
timeout = settings.SMALL_TIMEOUT
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
- self.wait_for_text_not_visible(text, selector, by=by, timeout=timeout)
+ return self.wait_for_text_not_visible(
+ text, selector, by=by, timeout=timeout)
############
From 4bffe7e471c88bc5e193682d879e0e6f2c30f47f Mon Sep 17 00:00:00 2001
From: Michael Mintz
Date: Fri, 26 Mar 2021 17:40:36 -0400
Subject: [PATCH 03/10] Update "is_chromium()" so that it still works with
selenium 4
---
seleniumbase/fixtures/base_case.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 0e7ee1ad..279e6ff9 100755
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -3371,7 +3371,7 @@ class BaseCase(unittest.TestCase):
""" Return True if the browser is Chrome, Edge, or Opera. """
self.__check_scope()
chromium = False
- browser_name = self.driver.__dict__["capabilities"]["browserName"]
+ browser_name = self.driver.capabilities["browserName"]
if browser_name in ("chrome", "edge", "msedge", "opera"):
chromium = True
return chromium
From e8be9f696e3837c44f6c328e8f636edb342b1067 Mon Sep 17 00:00:00 2001
From: Michael Mintz
Date: Fri, 26 Mar 2021 17:42:59 -0400
Subject: [PATCH 04/10] Refactor seleniumbase resource files
---
seleniumbase/resources/ReadMe.md | 14 +-
.../bootstrap-tour-standalone.min.css | 26 --
.../bootstrap-tour-standalone.min.js | 22 --
.../resources/driverjs/driver.min.css | 1 -
seleniumbase/resources/driverjs/driver.min.js | 2 -
.../resources/hopscotch/hopscotch.min.css | 17 --
.../resources/hopscotch/hopscotch.min.js | 17 --
.../html_inspector/html-inspector.min.js | 11 +-
seleniumbase/resources/introjs/intro.min.js | 1 -
.../resources/introjs/introjs.min.css | 1 -
.../jquery_confirm/jquery-confirm.min.css | 3 +-
.../resources/prettify/run_prettify.js | 64 -----
seleniumbase/resources/reveal/reveal.min.css | 8 -
seleniumbase/resources/reveal/reveal.min.js | 1 -
seleniumbase/resources/reveal/serif.min.css | 2 -
seleniumbase/resources/reveal/simple.min.css | 2 -
seleniumbase/resources/reveal/sky.min.css | 2 -
seleniumbase/resources/reveal/white.min.css | 2 -
.../resources/shepherd/backbone-min.js | 2 -
.../shepherd/shepherd-theme-arrows-fix.css | 5 -
.../shepherd/shepherd-theme-arrows.css | 229 ----------------
.../shepherd/shepherd-theme-dark.css | 246 -----------------
.../shepherd/shepherd-theme-default.css | 246 -----------------
.../shepherd/shepherd-theme-square-dark.css | 248 ------------------
.../shepherd/shepherd-theme-square.css | 248 ------------------
.../resources/shepherd/shepherd.min.js | 1 -
seleniumbase/resources/shepherd/tether.min.js | 1 -
.../resources/shepherd/underscore-min.js | 5 -
28 files changed, 22 insertions(+), 1405 deletions(-)
delete mode 100644 seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.css
delete mode 100644 seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.js
delete mode 100644 seleniumbase/resources/driverjs/driver.min.css
delete mode 100644 seleniumbase/resources/driverjs/driver.min.js
delete mode 100644 seleniumbase/resources/hopscotch/hopscotch.min.css
delete mode 100644 seleniumbase/resources/hopscotch/hopscotch.min.js
delete mode 100644 seleniumbase/resources/introjs/intro.min.js
delete mode 100644 seleniumbase/resources/introjs/introjs.min.css
delete mode 100644 seleniumbase/resources/prettify/run_prettify.js
delete mode 100644 seleniumbase/resources/reveal/reveal.min.css
delete mode 100644 seleniumbase/resources/reveal/reveal.min.js
delete mode 100644 seleniumbase/resources/reveal/serif.min.css
delete mode 100644 seleniumbase/resources/reveal/simple.min.css
delete mode 100644 seleniumbase/resources/reveal/sky.min.css
delete mode 100644 seleniumbase/resources/reveal/white.min.css
delete mode 100644 seleniumbase/resources/shepherd/backbone-min.js
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-arrows-fix.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-arrows.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-dark.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-default.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-square-dark.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd-theme-square.css
delete mode 100644 seleniumbase/resources/shepherd/shepherd.min.js
delete mode 100644 seleniumbase/resources/shepherd/tether.min.js
delete mode 100644 seleniumbase/resources/shepherd/underscore-min.js
diff --git a/seleniumbase/resources/ReadMe.md b/seleniumbase/resources/ReadMe.md
index 296cde13..6d9356db 100755
--- a/seleniumbase/resources/ReadMe.md
+++ b/seleniumbase/resources/ReadMe.md
@@ -1,6 +1,6 @@
-## Resources Help
+
Resource Files
-SeleniumBase uses some JavaScript libraries for optional advanced features such as website tours, messenger, HTML validation, highlighting elements on a page, and other jQuery actions. In some cases, you may want to host these JavaScript and CSS files from your own websites. For simplicity and convenience, these resources have been downloaded into the "resources" folder. If you decide to use your local versions, you may need to update the corresponding URLs in [base_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py).
+SeleniumBase uses JavaScript libraries for bonus features such as the Website Tour Maker, Presentation Maker, Chart Maker, Demo Mode, HTML Inspector, and more. In general, SeleniumBase retrieves these resources via CDN link. In some cases, you may want to host these JavaScript and CSS files from your own CDN. For simplicity and convenience, some of these resources have been downloaded into the "resources" folder. If you decide to use your own CDN, you may need to update links in [base_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py) and [constants.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/constants.py).
Here are some of the resource files you'll find here:
@@ -12,7 +12,15 @@ Here are some of the resource files you'll find here:
**html_inspector/** - Files in this folder are used for the HTML Inspector, which validates website pages.
-**shepherd/** - Files in this folder are used for creating website tours using the Shepherd JavaScript library. (This is the default tour library.)
+--------
+
+The remaining resources have been moved into [github.com/seleniumbase/resource-files](https://github.com/seleniumbase/resource-files) in order to reduce the size of SeleniumBase:
+
+**reveal/** - Files in this folder are used for the HTML Presentation Maker.
+
+**prettify/** - Files in this folder are used to assist the HTML Presentation Maker.
+
+**shepherd/** - Files in this folder are used for creating website tours using the Shepherd JavaScript library.
**bootstrap_tour/** - Files in this folder are used for creating website tours using the Bootstrap Tour JavaScript library.
diff --git a/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.css b/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.css
deleted file mode 100644
index d81614f0..00000000
--- a/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.css
+++ /dev/null
@@ -1,26 +0,0 @@
-/* ========================================================================
- * bootstrap-tour - v0.12.0
- * http://bootstraptour.com
- * ========================================================================
- * Copyright 2012-2015 Ulrich Sossou
- *
- * ========================================================================
- * Licensed under the MIT License (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://opensource.org/licenses/MIT
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================================
- */
-
-/*!
- * Bootstrap v3.1.0 (http://getbootstrap.com)
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.tour-backdrop{position:absolute;z-index:1100;background-color:#000;opacity:.8;filter:alpha(opacity=80)}.popover[class*="tour-"]{z-index:1102}.popover[class*="tour-"] .popover-navigation{padding:9px 14px;overflow:hidden}.popover[class*="tour-"] .popover-navigation *[data-role="end"]{float:right}.popover[class*="tour-"] .popover-navigation *[data-role="prev"],.popover[class*="tour-"] .popover-navigation *[data-role="next"],.popover[class*="tour-"] .popover-navigation *[data-role="end"]{cursor:pointer}.popover[class*="tour-"] .popover-navigation *[data-role="prev"].disabled,.popover[class*="tour-"] .popover-navigation *[data-role="next"].disabled,.popover[class*="tour-"] .popover-navigation *[data-role="end"].disabled{cursor:default}.popover[class*="tour-"].orphan{position:fixed;margin-top:0}.popover[class*="tour-"].orphan .arrow{display:none}
\ No newline at end of file
diff --git a/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.js b/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.js
deleted file mode 100644
index c16fa7bb..00000000
--- a/seleniumbase/resources/bootstrap_tour/bootstrap-tour-standalone.min.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* ========================================================================
- * bootstrap-tour - v0.12.0
- * http://bootstraptour.com
- * ========================================================================
- * Copyright 2012-2015 Ulrich Sossou
- *
- * ========================================================================
- * Licensed under the MIT License (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://opensource.org/licenses/MIT
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================================
- */
-
-+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var o in e)if(void 0!==t.style[o])return{end:e[o]};return!1}t.fn.emulateTransitionEnd=function(e){var o=!1,n=this;t(this).one("bsTransitionEnd",function(){o=!0});return setTimeout(function(){o||t(n).trigger(t.support.transition.end)},e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){if(t(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}})})}(jQuery),function(t){"use strict";var e=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};e.VERSION="3.3.7",e.TRANSITION_DURATION=150,e.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},e.prototype.init=function(e,o,n){if(this.enabled=!0,this.type=e,this.$element=t(o),this.options=this.getOptions(n),this.$viewport=this.options.viewport&&t(t.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var i=this.options.trigger.split(" "),r=i.length;r--;){var s=i[r];if("click"==s)this.$element.on("click."+this.type,this.options.selector,t.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",p="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,t.proxy(this.enter,this)),this.$element.on(p+"."+this.type,this.options.selector,t.proxy(this.leave,this))}}this.options.selector?this._options=t.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},e.prototype.getDefaults=function(){return e.DEFAULTS},e.prototype.getOptions=function(e){return(e=t.extend({},this.getDefaults(),this.$element.data(),e)).delay&&"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),e},e.prototype.getDelegateOptions=function(){var e={},o=this.getDefaults();return this._options&&t.each(this._options,function(t,n){o[t]!=n&&(e[t]=n)}),e},e.prototype.enter=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);if(o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusin"==e.type?"focus":"hover"]=!0),o.tip().hasClass("in")||"in"==o.hoverState)o.hoverState="in";else{if(clearTimeout(o.timeout),o.hoverState="in",!o.options.delay||!o.options.delay.show)return o.show();o.timeout=setTimeout(function(){"in"==o.hoverState&&o.show()},o.options.delay.show)}},e.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},e.prototype.leave=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);if(o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusout"==e.type?"focus":"hover"]=!1),!o.isInStateTrue()){if(clearTimeout(o.timeout),o.hoverState="out",!o.options.delay||!o.options.delay.hide)return o.hide();o.timeout=setTimeout(function(){"out"==o.hoverState&&o.hide()},o.options.delay.hide)}},e.prototype.show=function(){var o=t.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(o);var n=t.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(o.isDefaultPrevented()||!n)return;var i=this,r=this.tip(),s=this.getUID(this.type);this.setContent(),r.attr("id",s),this.$element.attr("aria-describedby",s),this.options.animation&&r.addClass("fade");var a="function"==typeof this.options.placement?this.options.placement.call(this,r[0],this.$element[0]):this.options.placement,p=/\s?auto?\s?/i,h=p.test(a);h&&(a=a.replace(p,"")||"top"),r.detach().css({top:0,left:0,display:"block"}).addClass(a).data("bs."+this.type,this),this.options.container?r.appendTo(this.options.container):r.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),u=r[0].offsetWidth,c=r[0].offsetHeight;if(h){var f=a,d=this.getPosition(this.$viewport);a="bottom"==a&&l.bottom+c>d.bottom?"top":"top"==a&&l.top-cd.width?"left":"left"==a&&l.left-us.top+s.height&&(i.top=s.top+s.height-p)}else{var h=e.left-r,l=e.left+r+o;hs.right&&(i.left=s.left+s.width-l)}return i},e.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},e.prototype.getUID=function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},e.prototype.tip=function(){if(!this.$tip&&(this.$tip=t(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},e.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},e.prototype.enable=function(){this.enabled=!0},e.prototype.disable=function(){this.enabled=!1},e.prototype.toggleEnabled=function(){this.enabled=!this.enabled},e.prototype.toggle=function(e){var o=this;e&&((o=t(e.currentTarget).data("bs."+this.type))||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o))),e?(o.inState.click=!o.inState.click,o.isInStateTrue()?o.enter(o):o.leave(o)):o.tip().hasClass("in")?o.leave(o):o.enter(o)},e.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})};var o=t.fn.tooltip;t.fn.tooltip=function(o){return this.each(function(){var n=t(this),i=n.data("bs.tooltip"),r="object"==typeof o&&o;!i&&/destroy|hide/.test(o)||(i||n.data("bs.tooltip",i=new e(this,r)),"string"==typeof o&&i[o]())})},t.fn.tooltip.Constructor=e,t.fn.tooltip.noConflict=function(){return t.fn.tooltip=o,this}}(jQuery),function(t){"use strict";var e=function(t,e){this.init("popover",t,e)};if(!t.fn.tooltip)throw new Error("Popover requires tooltip.js");e.VERSION="3.3.7",e.DEFAULTS=t.extend({},t.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'