From cad5e1017f1a6aa61027e67dd4879902f97fb50e Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 13:56:10 +1000 Subject: [PATCH 01/72] Write stubs for app and user tests --- tests/test_app.py | 29 +++++++++++++++++++++++++++++ tests/test_users.py | 23 +++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/test_app.py create mode 100644 tests/test_users.py diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..157eeba --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,29 @@ +""" +test_app.py -- test module for sw_app.py +""" + +import os +import shutil +import pytest +import sciris as sc +import scirisweb as sw + + + +def test_update_config(app): + + # Update default configuration + app.config['SERVER_PORT'] = 8888 + app.config['USE_DATASTORE'] = True + app.config['MATPLOTLIB_BACKEND'] = 'qtagg' + + # Check that updates were made + assert app.config['SERVER_PORT'] == 8888 + assert app.config['USE_DATASTORE'] == True + assert app.config['MATPLOTLIB_BACKEND'] == 'qtagg' + + +if __name__ == '__main__': + # Make default app + sw_app = sw.ScirisApp(__name__, name="test_app") + test_update_config(sw_app) diff --git a/tests/test_users.py b/tests/test_users.py new file mode 100644 index 0000000..3af4c80 --- /dev/null +++ b/tests/test_users.py @@ -0,0 +1,23 @@ +""" +test_users.py -- test module for sw_users.py +""" + +import os +import pytest +import sciris as sc +import scirisweb as sw + + + +def test_new_user(): + """ + Create a new User and then check email, and that password is encrypted + """ + user = sw.User(username='gremlin', email='scirisweb@gremlinmail.com', raw_password='SudoMakeMeASandwich') + assert user.email == 'scirisweb@gremlinmail.com' + assert user.password != user.raw_password + assert user.is_admin == False + +if __name__ == '__main__': + # Test user stuff + test_new_user() From 2dcb6f947667490a37e35bf760e086ebeee0d976 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 14:53:12 +1000 Subject: [PATCH 02/72] Save users to datastore --- tests/test_users.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_users.py b/tests/test_users.py index 3af4c80..ce40ef3 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -9,7 +9,15 @@ -def test_new_user(): +@pytest.fixture(scope='module') +def make_ds(): + db_folder = 'temp_test_datastore_users' + db_url = f'file://{db_folder}/' + ds = sw.make_datastore(db_url) + return ds + + +def test_new_user(ds): """ Create a new User and then check email, and that password is encrypted """ @@ -18,6 +26,13 @@ def test_new_user(): assert user.password != user.raw_password assert user.is_admin == False + # Save user to datastore + ds.saveuser(user) + # Load user to datastore + loaded_user = ds.loaduser(username='gremlin') + assert user.username == loaded_user.username + + if __name__ == '__main__': # Test user stuff test_new_user() From 35cc043263db96ebebd2396f0dcddcb8fd8a4ee8 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 15:32:04 +1000 Subject: [PATCH 03/72] Generate a user, save it to app datastore and load it --- tests/test_users.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index ce40ef3..d26782c 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -7,32 +7,35 @@ import sciris as sc import scirisweb as sw +# Define some nondefaults for the app +class TestingUsersConfig(object): + USE_USERS = True + USE_DATASTORE = True + DATASTORE_URL = f'sqlite:///:memory:' +def make_app(): + app = sw.ScirisApp(__name__, config=TestingUsersConfig(), name='test_users_app') + return app -@pytest.fixture(scope='module') -def make_ds(): - db_folder = 'temp_test_datastore_users' - db_url = f'file://{db_folder}/' - ds = sw.make_datastore(db_url) - return ds +@pytest.fixture(name='app') +def app(): + return make_app() -def test_new_user(ds): + +def test_new_user(app): """ Create a new User and then check email, and that password is encrypted """ user = sw.User(username='gremlin', email='scirisweb@gremlinmail.com', raw_password='SudoMakeMeASandwich') + print(user) assert user.email == 'scirisweb@gremlinmail.com' - assert user.password != user.raw_password assert user.is_admin == False # Save user to datastore - ds.saveuser(user) + app.datastore.saveuser(user) # Load user to datastore - loaded_user = ds.loaduser(username='gremlin') + loaded_user = app.datastore.loaduser(username='gremlin') assert user.username == loaded_user.username -if __name__ == '__main__': - # Test user stuff - test_new_user() From f79d3a361c5a5c3c75148269f64b867e5c278812 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 16:03:58 +1000 Subject: [PATCH 04/72] Use app context to save and load user to ds --- tests/test_users.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index d26782c..7be75c4 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -32,10 +32,11 @@ def test_new_user(app): assert user.email == 'scirisweb@gremlinmail.com' assert user.is_admin == False - # Save user to datastore - app.datastore.saveuser(user) - # Load user to datastore - loaded_user = app.datastore.loaduser(username='gremlin') - assert user.username == loaded_user.username + # Save + with app.flask_app.app_context(): + sw.save_user(user) + loaded_user = sw.load_user(username='gremlin') + loaded_user = app.datastore.loaduser(username='gremlin') + assert user.username == loaded_user.username From c93c282936969c9178724c068de52c995ded3167 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 16:34:54 +1000 Subject: [PATCH 05/72] Test default users --- tests/test_users.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 7be75c4..e39bbad 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -4,15 +4,27 @@ import os import pytest +import json import sciris as sc import scirisweb as sw -# Define some nondefaults for the app + +def isjson(json_input): + try: + json.loads(json_input) + except ValueError as e: + return False + return True + + class TestingUsersConfig(object): + """ # Define some nondefaults for the app """ + TESTING=True USE_USERS = True USE_DATASTORE = True DATASTORE_URL = f'sqlite:///:memory:' + def make_app(): app = sw.ScirisApp(__name__, config=TestingUsersConfig(), name='test_users_app') return app @@ -25,18 +37,27 @@ def app(): def test_new_user(app): """ - Create a new User and then check email, and that password is encrypted + Create a new User and then check email, whther the user is admin (it should not by default) """ user = sw.User(username='gremlin', email='scirisweb@gremlinmail.com', raw_password='SudoMakeMeASandwich') - print(user) + + # Check a few basic things about user assert user.email == 'scirisweb@gremlinmail.com' assert user.is_admin == False + assert user.get_id() == user.username - # Save with app.flask_app.app_context(): + # Save and load the new users we just made sw.save_user(user) loaded_user = sw.load_user(username='gremlin') - loaded_user = app.datastore.loaduser(username='gremlin') assert user.username == loaded_user.username +def test_make_default_users(app): + """ Test """ + sw.make_default_users(app, include_admin=True) + + +# def test_jsonify(): +# """ Test JSON representation""" +# pass From 6658b1267e524eb7cf08daf9300af2ae30f1cd9b Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 16:48:36 +1000 Subject: [PATCH 06/72] Test users jsonify() --- tests/test_users.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index e39bbad..21ea086 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -58,6 +58,10 @@ def test_make_default_users(app): sw.make_default_users(app, include_admin=True) -# def test_jsonify(): -# """ Test JSON representation""" -# pass +def test_jsonify(): + """ Test JSON representation""" + user = sw.User() + output = user.jsonify() + output_json = sc.sanitizejson(output['user'], tostring=True) + assert isjson(output_json) == True +pass From c7aafd8bbe14baa306c349ee1e668780b8de3d11 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 16:52:58 +1000 Subject: [PATCH 07/72] Test admin user --- tests/test_users.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_users.py b/tests/test_users.py index 21ea086..8fdbeed 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -56,6 +56,10 @@ def test_new_user(app): def test_make_default_users(app): """ Test """ sw.make_default_users(app, include_admin=True) + with app.flask_app.app_context(): + # Load admin + admin_user = sw.load_user(username='admin') + assert admin_user.is_admin == True def test_jsonify(): From c33b978128e81a99ce1f78183b50cfc9fc475b64 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 16:53:20 +1000 Subject: [PATCH 08/72] Test admin user --- tests/test_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_users.py b/tests/test_users.py index 8fdbeed..91fd754 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -54,7 +54,7 @@ def test_new_user(app): def test_make_default_users(app): - """ Test """ + """ Test default users including admin""" sw.make_default_users(app, include_admin=True) with app.flask_app.app_context(): # Load admin From 066e0d601530ce3bb43deeecf91803330599a915 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 17:44:17 +1000 Subject: [PATCH 09/72] Test user registration --- tests/test_users.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 91fd754..d81ef77 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -18,11 +18,12 @@ def isjson(json_input): class TestingUsersConfig(object): - """ # Define some nondefaults for the app """ + """ Define some nondefaults for the app """ TESTING=True USE_USERS = True USE_DATASTORE = True DATASTORE_URL = f'sqlite:///:memory:' + REGISTER_AUTOACTIVATE = True # Otherwise user register fails def make_app(): @@ -53,6 +54,24 @@ def test_new_user(app): assert user.username == loaded_user.username +def test_user_register(app): + sw.make_default_users(app, include_admin=True) + + with app.flask_app.app_context(): + admin_user = sw.load_user(username='admin') + response_admin = sw.user_register(admin_user.username, admin_user.password, + admin_user.displayname, admin_user.email + ) + + response_gremlin = sw.user_register('gremlin', 'Banana', + 'gremlin', 'scirisweb@gremlinmail.com' + ) + + # We already created and saved admin + assert not response_admin == 'success' + assert response_gremlin == 'success' + + def test_make_default_users(app): """ Test default users including admin""" sw.make_default_users(app, include_admin=True) @@ -68,4 +87,3 @@ def test_jsonify(): output = user.jsonify() output_json = sc.sanitizejson(output['user'], tostring=True) assert isjson(output_json) == True -pass From 53d2ba05c5fec20c4e79e69ee874ef1932a41e94 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 17:59:32 +1000 Subject: [PATCH 10/72] Test user registration --- tests/test_users.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index d81ef77..505204b 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -4,6 +4,7 @@ import os import pytest +from flask_login.utils import login_required, current_user, login_user import json import sciris as sc import scirisweb as sw @@ -59,13 +60,15 @@ def test_user_register(app): with app.flask_app.app_context(): admin_user = sw.load_user(username='admin') + + # Register a user who is already saved in the datastore response_admin = sw.user_register(admin_user.username, admin_user.password, admin_user.displayname, admin_user.email ) - + # Register a new user response_gremlin = sw.user_register('gremlin', 'Banana', - 'gremlin', 'scirisweb@gremlinmail.com' - ) + 'gremlin', 'scirisweb@gremlinmail.com' + ) # We already created and saved admin assert not response_admin == 'success' From cf8c5a8d3762d539154293493067b4e000b65c08 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 19:18:21 +1000 Subject: [PATCH 11/72] Test admin actions --- tests/test_users.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_users.py b/tests/test_users.py index 505204b..f2c7fd8 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -75,6 +75,31 @@ def test_user_register(app): assert response_gremlin == 'success' +def test_admin_actions(app): + sw.make_default_users(app, include_admin=True) + + with app.flask_app.app_context(): + + # Make default user activate + response_activate = sw.admin_activate_account('demo') + default_user = sw.load_user(username='demo') + default_is_active = default_user.is_active + + # We already created and saved admin + assert not response_activate == 'success' + assert default_is_active == True + + + # Make default user inactive + response_deactivate = sw.admin_deactivate_account('demo') + default_user = sw.load_user(username='demo') + default_is_inactive = not(default_user.is_active) + + # This is failing + # assert not response_deactivate == 'success' + # assert default_is_inactive == True + + def test_make_default_users(app): """ Test default users including admin""" sw.make_default_users(app, include_admin=True) From 163d248f38c247824a1f3c762625f03dbd8d0fdb Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 19:25:13 +1000 Subject: [PATCH 12/72] Test admin actions --- tests/test_users.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index f2c7fd8..0387cda 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -76,6 +76,7 @@ def test_user_register(app): def test_admin_actions(app): + success_str = 'success' # write it once sw.make_default_users(app, include_admin=True) with app.flask_app.app_context(): @@ -86,18 +87,26 @@ def test_admin_actions(app): default_is_active = default_user.is_active # We already created and saved admin - assert not response_activate == 'success' + assert not response_activate == success_str assert default_is_active == True - # Make default user inactive response_deactivate = sw.admin_deactivate_account('demo') default_user = sw.load_user(username='demo') default_is_inactive = not(default_user.is_active) - # This is failing - # assert not response_deactivate == 'success' - # assert default_is_inactive == True + # TODO: understand why deactivation is failing + # assert not response_deactivate == 'success' + # assert default_is_inactive == True + + response_make_admin = sw.admin_grant_admin('demo') + assert response_make_admin == success_str + + response_revoke_admin = sw.admin_revoke_admin('demo') + assert response_revoke_admin == success_str + + response_reset_passwd = sw.admin_reset_password('demo') + assert response_reset_passwd == success_str def test_make_default_users(app): From cd6f8bdd29bf874df1f8d5a38f99ce42a022a011 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 19:33:45 +1000 Subject: [PATCH 13/72] Cover admin actions when matching users is None --- tests/test_users.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 0387cda..2797217 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -77,6 +77,8 @@ def test_user_register(app): def test_admin_actions(app): success_str = 'success' # write it once + failure_str = 'failure' # write it once + sw.make_default_users(app, include_admin=True) with app.flask_app.app_context(): @@ -100,13 +102,20 @@ def test_admin_actions(app): # assert default_is_inactive == True response_make_admin = sw.admin_grant_admin('demo') + response_make_admin_none = sw.admin_grant_admin('gizmo') assert response_make_admin == success_str + assert response_make_admin_none == failure_str response_revoke_admin = sw.admin_revoke_admin('demo') - assert response_revoke_admin == success_str + response_revoke_admin_none = sw.admin_revoke_admin('gizmo') + assert response_revoke_admin == success_str + assert response_revoke_admin_none == failure_str response_reset_passwd = sw.admin_reset_password('demo') - assert response_reset_passwd == success_str + response_reset_passwd_none = sw.admin_reset_password('gizmo') + assert response_reset_passwd == success_str + assert response_reset_passwd_none == failure_str + def test_make_default_users(app): From 7ce758c7dea446934b47ca12cbd92776b7bba4f2 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 19:48:14 +1000 Subject: [PATCH 14/72] Add case for sw.serve() --- tests/test_server.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_server.py b/tests/test_server.py index 235772e..483dfcf 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -11,7 +11,7 @@ 'blank', # An empty figure 'browser', # Simple example of three figures 'advanced', # Illustrates separate legend plotting and direct editing of the JSON -] +'html'] if 'doplot' not in locals(): doplot = True @@ -55,4 +55,9 @@ def make_fig(): json2['axes'][0]['paths'] = [] # Remove the box around the legend if doplot: - sw.browser([json1, json2]) \ No newline at end of file + sw.browser([json1, json2]) + + if 'html' in torun: + html = + handle_srv1 = sw.serve('Hello, tests 8888!!') + handle_srv2 = sw.serve('Hello, tests 8080!!', port=8080) \ No newline at end of file From 70a5216d83f9b6fdbabb19079b92c636e658dfab Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 20:00:13 +1000 Subject: [PATCH 15/72] Remove unfinished line --- tests/test_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_server.py b/tests/test_server.py index 483dfcf..36845f2 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -58,6 +58,5 @@ def make_fig(): sw.browser([json1, json2]) if 'html' in torun: - html = handle_srv1 = sw.serve('Hello, tests 8888!!') handle_srv2 = sw.serve('Hello, tests 8080!!', port=8080) \ No newline at end of file From 7c7c8c40a01a40b8debf9bba6ea82337c9fbc84c Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 20:12:53 +1000 Subject: [PATCH 16/72] Make some default app configs --- scirisweb/sw_appconfig.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 scirisweb/sw_appconfig.py diff --git a/scirisweb/sw_appconfig.py b/scirisweb/sw_appconfig.py new file mode 100644 index 0000000..4f66eda --- /dev/null +++ b/scirisweb/sw_appconfig.py @@ -0,0 +1,28 @@ +""" +sw_appconfig.py -- classes for quick configuration of Sciris (Flask-based) apps + +""" + +__all__ = ['Config', 'DevelopmentAppConfig', 'TestingAppConfig', 'TestingUsersAppConfig'] + + +class Config(object): + TESTING = False + + +class DevelopmentAppConfig(Config): + USE_DATASTORE = True + DATASTORE_URL =f"sqlite:///datastore.db" + + +class TestingAppConfig(Config): + """ Define some nondefaults for testing""" + TESTING = True + USE_DATASTORE = True + DATASTORE_URL = 'sqlite:///:memory:' + + +class TestingUsersAppConfig(TestingAppConfig): + """ Define some nondefaults parameters for test_users.py """ + USE_USERS = True + REGISTER_AUTOACTIVATE = True # Otherwise user register fails From b637ca9d10f7ceba64b85925281975070dce23fa Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 20:13:24 +1000 Subject: [PATCH 17/72] Use Testing config from sw_appconfig --- tests/test_users.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 2797217..264df9f 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -18,17 +18,17 @@ def isjson(json_input): return True -class TestingUsersConfig(object): - """ Define some nondefaults for the app """ - TESTING=True - USE_USERS = True - USE_DATASTORE = True - DATASTORE_URL = f'sqlite:///:memory:' - REGISTER_AUTOACTIVATE = True # Otherwise user register fails +# class TestingUsersConfig(object): +# """ Define some nondefaults for the app """ +# TESTING=True +# USE_USERS = True +# USE_DATASTORE = True +# DATASTORE_URL = f'sqlite:///:memory:' +# REGISTER_AUTOACTIVATE = True # Otherwise user register fails def make_app(): - app = sw.ScirisApp(__name__, config=TestingUsersConfig(), name='test_users_app') + app = sw.ScirisApp(__name__, config=sw.TestingUsersAppConfig(), name='test_users_app') return app From 7741d09ac79c8c3f71a34486bbcc64f687704862 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 20:13:47 +1000 Subject: [PATCH 18/72] Import sw_appconfig --- scirisweb/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scirisweb/__init__.py b/scirisweb/__init__.py index 3790841..62f2289 100644 --- a/scirisweb/__init__.py +++ b/scirisweb/__init__.py @@ -15,6 +15,7 @@ from .sw_tasks import * # analysis:ignore from .sw_datastore import * # analysis:ignore from .sw_app import * # analysis:ignore +from .sw_appconfig import * # analysis:ignore from .sw_server import * # analysis:ignore # Print the license...or not From 43e144ea02cdd2f8f3dcbdbfc30c073e2d1bc83e Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Wed, 11 May 2022 20:32:32 +1000 Subject: [PATCH 19/72] Rework basic tests --- tests/test_app.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/test_app.py b/tests/test_app.py index 157eeba..33819e0 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -3,27 +3,34 @@ """ import os -import shutil import pytest import sciris as sc import scirisweb as sw -def test_update_config(app): +def make_app(): + app = sw.ScirisApp(__name__, config=sw.TestingUsersAppConfig(), name='test_app') + return app + - # Update default configuration - app.config['SERVER_PORT'] = 8888 - app.config['USE_DATASTORE'] = True - app.config['MATPLOTLIB_BACKEND'] = 'qtagg' +@pytest.fixture(name='app') +def app(): + return make_app() + +def test_update_config(app): + + update_dict = dict(SERVER_PORT=8888) + app._update_config_defaults(**update_dict) # Check that updates were made assert app.config['SERVER_PORT'] == 8888 - assert app.config['USE_DATASTORE'] == True - assert app.config['MATPLOTLIB_BACKEND'] == 'qtagg' + +def test_defaults(): + app = sw.ScirisApp(__name__, config=sw.TestingUsersAppConfig()) + assert app.name == 'default' + + app = sw.ScirisApp(__name__, config=sw.Config()) + assert isinstance(app.datastore, sw.DataDir) -if __name__ == '__main__': - # Make default app - sw_app = sw.ScirisApp(__name__, name="test_app") - test_update_config(sw_app) From b4dd4a624d788da64a424b757bf069d0d8137f98 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Thu, 12 May 2022 22:18:18 +1000 Subject: [PATCH 20/72] Test basic app creation functinalit --- tests/test_app.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_app.py b/tests/test_app.py index 33819e0..0349d4c 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -26,7 +26,9 @@ def test_update_config(app): # Check that updates were made assert app.config['SERVER_PORT'] == 8888 + def test_defaults(): + app = sw.ScirisApp(__name__, config=sw.TestingUsersAppConfig()) assert app.name == 'default' @@ -34,3 +36,26 @@ def test_defaults(): assert isinstance(app.datastore, sw.DataDir) +def test_logfile(): + + logfile = 'temp_logfile.log' + app = sw.ScirisApp(__name__, config=sw.Config(), logfile=logfile) + os.remove(logfile) + + ds = sw.DataDir() + with pytest.raises(Exception) as e: + app = sw.ScirisApp(__name__, config=sw.Config(), logfile=ds.tempdir_obj.name) + + +def test_robustjsonify(app): + + info = {"data1": "Hello world", + "data2": "Good morning", + "data3": "See you tomorrow"} + + with app.flask_app.app_context(): + response = sw.robustjsonify(info) + assert response.status_code == 200 + assert response.is_json == True + + From b972977f7f28674a1fe931ebcdf7ed00dc634057 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Thu, 12 May 2022 22:47:09 +1000 Subject: [PATCH 21/72] Test route --- tests/test_app.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_app.py b/tests/test_app.py index 0349d4c..4a3c986 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -6,6 +6,7 @@ import pytest import sciris as sc import scirisweb as sw +import pylab as pl @@ -59,3 +60,21 @@ def test_robustjsonify(app): assert response.is_json == True +def test_run(app): + @app.route('/showgraph') + def showgraph(n=1000): + # Make graph + fig = pl.figure() + ax = fig.add_subplot(111) + xdata = pl.randn(n) + ydata = pl.randn(n) + colors = sc.vectocolor(pl.sqrt(xdata ** 2 + ydata ** 2)) + ax.scatter(xdata, ydata, c=colors) + + graphjson = sw.mpld3ify(fig) # Convert to dict + return graphjson # Return the JSON representation of the Matplotlib figure + + with app.flask_app.test_client() as client: + response = client.get('/showgraph') + + assert response.status_code == 200 From 04c2e6549600fd2a6126b782c1747cb709f2d9b4 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 12:44:31 +1000 Subject: [PATCH 22/72] Test make datastore default params --- tests/test_datastore.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index aed5708..ccca973 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -32,6 +32,10 @@ def test_datastore(url): ds = sw.make_datastore(url) assert len(ds.keys()) == 1 # There should be a datastore settings key present + ds.flushdb() + ds = sw.make_datastore() + assert len(ds.keys()) == 2 + # Basic CRUD functionality # CREATE From 08ead52ff49c5949c230b6f6dc80d0143cb5489b Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 13:11:51 +1000 Subject: [PATCH 23/72] Clean up assertions --- tests/test_users.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 264df9f..52356c4 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -45,7 +45,7 @@ def test_new_user(app): # Check a few basic things about user assert user.email == 'scirisweb@gremlinmail.com' - assert user.is_admin == False + assert not user.is_admin assert user.get_id() == user.username with app.flask_app.app_context(): @@ -90,7 +90,7 @@ def test_admin_actions(app): # We already created and saved admin assert not response_activate == success_str - assert default_is_active == True + assert default_is_active # Make default user inactive response_deactivate = sw.admin_deactivate_account('demo') @@ -99,7 +99,7 @@ def test_admin_actions(app): # TODO: understand why deactivation is failing # assert not response_deactivate == 'success' - # assert default_is_inactive == True + # assert default_is_inactive response_make_admin = sw.admin_grant_admin('demo') response_make_admin_none = sw.admin_grant_admin('gizmo') @@ -124,7 +124,7 @@ def test_make_default_users(app): with app.flask_app.app_context(): # Load admin admin_user = sw.load_user(username='admin') - assert admin_user.is_admin == True + assert admin_user.is_admin def test_jsonify(): @@ -132,4 +132,4 @@ def test_jsonify(): user = sw.User() output = user.jsonify() output_json = sc.sanitizejson(output['user'], tostring=True) - assert isjson(output_json) == True + assert isjson(output_json) From da16218c3f34e59225a18de7776fafb721fd25fb Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 13:12:07 +1000 Subject: [PATCH 24/72] Test copy datastore --- tests/test_datastore.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index ccca973..f4ba834 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -83,6 +83,19 @@ def test_datastore(url): pass +def test_copy_datastore(url): + src_url = f'sqlite:///datastore1.db' + dst_url = f'sqlite:///datastore2.db' + + # Make source datastore + src_ds = sw.make_datastore(src_url) + # Save some data + src_ds.saveblob(obj='teststr', key='foo') + # Copy + dst_ds = sw.copy_datastore(src_ds.url, dst_url) + assert {'foo'}.issubset(set(dst_ds.keys())) + + if __name__ == '__main__': for url in urls: test_datastore(url) From 9c1f9f7f52070637f3e71e6121fa59ff2227cd92 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 13:46:24 +1000 Subject: [PATCH 25/72] Test misc functionality, fix assertion --- tests/test_datastore.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index f4ba834..5d2ca31 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -59,7 +59,7 @@ def test_datastore(url): # DELETE ds.delete(key_in) - assert 'foo' not in ds.keys() # Check it was successfully deleted + assert not {'foo'} in ds.keys() # Check it was successfully deleted ds.delete('nonexistent') # If a key doesn't exist, an error should not occur # TEST KEY LISTING AND FILTERING @@ -83,7 +83,7 @@ def test_datastore(url): pass -def test_copy_datastore(url): +def test_copy_datastore(): src_url = f'sqlite:///datastore1.db' dst_url = f'sqlite:///datastore2.db' @@ -96,6 +96,17 @@ def test_copy_datastore(url): assert {'foo'}.issubset(set(dst_ds.keys())) +#@pytest.mark.parametrize('url', urls) +def test_misc(): + ds = sw.make_datastore() + # Save some data + ds.saveblob(obj='teststr', key='foo') + with pytest.raises(Exception): + ds.saveblob(obj='teststr', key='foo', overwrite=False) + + + if __name__ == '__main__': for url in urls: test_datastore(url) + From 8ef35e902cba40cbaa509a1bdd02fa6abf707f43 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 14:12:45 +1000 Subject: [PATCH 26/72] Test failing: Need to flush default redis db manually --- tests/test_datastore.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index 5d2ca31..407dc96 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -34,7 +34,7 @@ def test_datastore(url): ds.flushdb() ds = sw.make_datastore() - assert len(ds.keys()) == 2 + assert len(ds.keys()) == 1 # Basic CRUD functionality @@ -73,6 +73,10 @@ def test_datastore(url): assert ds.exists('foo') assert not ds.exists('nonexistent') + # Flush default redis ds + # redis.cli -h 127.0.0.1 -p 6379 FLUSHALL + ds.flushdb() + # Tidy up cleanup = {db_file:os.remove, db_folder:shutil.rmtree} for fn,func in cleanup.items(): From 5cbd924917f03cadf206933e02b7e7598e9b0eed Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 14:19:35 +1000 Subject: [PATCH 27/72] Flush default redis ds at the end of tests --- tests/test_datastore.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index 407dc96..530d70c 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -73,7 +73,7 @@ def test_datastore(url): assert ds.exists('foo') assert not ds.exists('nonexistent') - # Flush default redis ds + # Manually flush default redis ds # redis.cli -h 127.0.0.1 -p 6379 FLUSHALL ds.flushdb() @@ -88,8 +88,10 @@ def test_datastore(url): def test_copy_datastore(): - src_url = f'sqlite:///datastore1.db' - dst_url = f'sqlite:///datastore2.db' + src_name = 'datastore_src.db' + dst_name = 'datastore_dst.db' + src_url = f'sqlite:///{src_name}' + dst_url = f'sqlite:///{dst_name}' # Make source datastore src_ds = sw.make_datastore(src_url) @@ -99,8 +101,16 @@ def test_copy_datastore(): dst_ds = sw.copy_datastore(src_ds.url, dst_url) assert {'foo'}.issubset(set(dst_ds.keys())) + # Tidy up + cleanup = {src_name: os.remove, dst_name: os.remove} + for fn, func in cleanup.items(): + try: + func(fn) + print('Removed %s' % fn) + except: + pass + -#@pytest.mark.parametrize('url', urls) def test_misc(): ds = sw.make_datastore() # Save some data @@ -108,7 +118,8 @@ def test_misc(): with pytest.raises(Exception): ds.saveblob(obj='teststr', key='foo', overwrite=False) - + ds.flushdb() + ds.delete() if __name__ == '__main__': for url in urls: From f81def2a8c97660ec12f6e45ad66cd174362dd5e Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 14:27:09 +1000 Subject: [PATCH 28/72] Test exception blocks --- tests/test_datastore.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index 530d70c..7e665ff 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -73,7 +73,7 @@ def test_datastore(url): assert ds.exists('foo') assert not ds.exists('nonexistent') - # Manually flush default redis ds + # Manually flush default redis ds if any test fails before reaching this point # redis.cli -h 127.0.0.1 -p 6379 FLUSHALL ds.flushdb() @@ -115,9 +115,17 @@ def test_misc(): ds = sw.make_datastore() # Save some data ds.saveblob(obj='teststr', key='foo') + # Try to save the same object again with pytest.raises(Exception): ds.saveblob(obj='teststr', key='foo', overwrite=False) + # Load an object that does not exist + with pytest.raises(Exception): + ds.loadblob(key='bar', objtype='Unknown') + + with pytest.raises(Exception): + ds.loadblob(key='bar') + ds.flushdb() ds.delete() From d2a6551d8f4ede74391090320a7ff49606b96265 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 14:32:13 +1000 Subject: [PATCH 29/72] Test save and load tasks --- tests/test_datastore.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_datastore.py b/tests/test_datastore.py index 7e665ff..2a6392c 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -126,9 +126,18 @@ def test_misc(): with pytest.raises(Exception): ds.loadblob(key='bar') + my_task = sw.Task(42) + ds.savetask(my_task, key='my_task') + + load_task = ds.loadtask(key='my_task') + assert my_task.uid == load_task.uid + ds.flushdb() ds.delete() + + + if __name__ == '__main__': for url in urls: test_datastore(url) From c8bbaa431312e7c04d9c02289684816ce47a6e38 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 15:03:27 +1000 Subject: [PATCH 30/72] Pretty print flask config --- scirisweb/sw_app.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scirisweb/sw_app.py b/scirisweb/sw_app.py index ebb0432..8345c2d 100644 --- a/scirisweb/sw_app.py +++ b/scirisweb/sw_app.py @@ -589,8 +589,13 @@ def _do_RPC(self, verbose=False): output = robustjsonify(result) if verbose: print('RPC(): RPC finished, returning result') return output - - + + + def show_config(self): + """ Pretty print flask config dict """ + sc.pp(self.config) + + class ScirisResource(Resource): isLeaf = True From b0975e9f2e9a5db39708c45548b936380ec3fd14 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 15:06:36 +1000 Subject: [PATCH 31/72] Define config for testing tasks --- scirisweb/sw_appconfig.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scirisweb/sw_appconfig.py b/scirisweb/sw_appconfig.py index 4f66eda..3ac828c 100644 --- a/scirisweb/sw_appconfig.py +++ b/scirisweb/sw_appconfig.py @@ -26,3 +26,8 @@ class TestingUsersAppConfig(TestingAppConfig): """ Define some nondefaults parameters for test_users.py """ USE_USERS = True REGISTER_AUTOACTIVATE = True # Otherwise user register fails + + +class TestingTasksAppConfig(TestingAppConfig): + """ Define some nondefaults parameters for test_tasks.py """ + USE_TASKS = True From 885089896e528bec4f5f4b55b7b0bad0ba061966 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 15:07:30 +1000 Subject: [PATCH 32/72] Define config for testing tasks --- scirisweb/sw_appconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scirisweb/sw_appconfig.py b/scirisweb/sw_appconfig.py index 3ac828c..772375d 100644 --- a/scirisweb/sw_appconfig.py +++ b/scirisweb/sw_appconfig.py @@ -3,7 +3,7 @@ """ -__all__ = ['Config', 'DevelopmentAppConfig', 'TestingAppConfig', 'TestingUsersAppConfig'] +__all__ = ['Config', 'DevelopmentAppConfig', 'TestingAppConfig', 'TestingUsersAppConfig', 'TestingTasksAppConfig'] class Config(object): From 2585f3964c393bdf3e38f399cd5a2bb6a0a1661e Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 20:12:08 +1000 Subject: [PATCH 33/72] Return destination ds --- scirisweb/sw_datastore.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scirisweb/sw_datastore.py b/scirisweb/sw_datastore.py index 41f6f53..197d59a 100644 --- a/scirisweb/sw_datastore.py +++ b/scirisweb/sw_datastore.py @@ -141,6 +141,7 @@ def copy_datastore(src, dst): value = src_ds._get(key) dst_ds._set(key, value) + return dst_ds # Return destination datastore, for testing purposes class DataStoreSettings(sc.prettyobj): ''' Global settings for the DataStore ''' From a32824ed574e6e637cf689903b5bf0a3af6594a4 Mon Sep 17 00:00:00 2001 From: Paula Sanz-Leon Date: Mon, 16 May 2022 20:14:07 +1000 Subject: [PATCH 34/72] Get started with testing tasks --- tests/test_tasks.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/test_tasks.py diff --git a/tests/test_tasks.py b/tests/test_tasks.py new file mode 100644 index 0000000..eb1a770 --- /dev/null +++ b/tests/test_tasks.py @@ -0,0 +1,23 @@ +""" +test_tasks.py -- test module for sw_tasks.py +""" + +import os +import pytest +import sciris as sc +import scirisweb as sw + + +def make_app(): + app = sw.ScirisApp(__name__, config=sw.TestingTasksAppConfig(), name='test_tasks_app') + return app + + +@pytest.fixture(name='app') +def app(): + return make_app() + + +def test_misc(app): + ds = sw.get_datastore(config=app.config) + assert ds.url == app.datastore.url From be642d6abfc55662c892220c67d2e921e438e475 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 16:54:04 -0800 Subject: [PATCH 35/72] update version --- scirisweb/sw_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scirisweb/sw_version.py b/scirisweb/sw_version.py index 26bc832..9929be6 100644 --- a/scirisweb/sw_version.py +++ b/scirisweb/sw_version.py @@ -1,5 +1,5 @@ __all__ = ['__version__', '__versiondate__', '__license__'] -__version__ = '0.17.1' -__versiondate__ = '2022-02-02' +__version__ = '1.0.0' +__versiondate__ = '2023-12-12' __license__ = 'ScirisWeb %s (%s) -- (c) Sciris.org' % (__version__, __versiondate__) From f1c898d6e548ab6b1e4ad4182592c53c27a8d03e Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 16:58:28 -0800 Subject: [PATCH 36/72] update requirements and setup --- requirements.txt | 21 ++++++++++++------ setup.py | 55 +++++++++++++++--------------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/requirements.txt b/requirements.txt index 80c728a..55093e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,14 @@ -# Dependencies for testing/documentation -pytest -pytest-xdist -# nbsphinx -# ipykernel -pandoc -sphinx>=1.8 \ No newline at end of file +sciris # Basic tools, including numpy, pandas, etc. as dependencies +decorator # For API calls +redis # Database -- Redis >=3.0 breaks Celery unfortunately +mpld3 # Rendering plots in the browser +werkzeug # HTTP tools +flask # Creating the webapp +flask-login # Handling users +flask-session # use redis for sessions +celery # Task manager +twisted # Server +service_identity # Identity manager for Celery (not installed with Celery though) +pyasn1 # Required for service_identity (but not listed as a dependency!) +pyparsing # Also for processing requests +sqlalchemy # For databases \ No newline at end of file diff --git a/setup.py b/setup.py index 5b98a82..fc33a2e 100644 --- a/setup.py +++ b/setup.py @@ -7,71 +7,50 @@ being generally useful for scientific computing. ''' -from setuptools import setup, find_packages import os import sys import runpy +from setuptools import setup, find_packages # Get the current folder cwd = os.path.abspath(os.path.dirname(__file__)) -# Define the requirements and extras -requirements = [ - 'sciris', # Basic tools -- NB, this includes numpy, scipy, pandas, and matplotlib as dependencies - 'decorator>=4.1.2', # For API calls - 'redis==2.10.6', # Database -- Redis >=3.0 breaks Celery unfortunately - 'mpld3', # Rendering plots in the browser - 'werkzeug', # HTTP tools - 'flask>=1.0.0', # Creating the webapp - 'flask-login>=0.4.1', # Handling users - 'flask-session>=0.3.1', # use redis for sessions - 'celery>=4.2', # Task manager - 'twisted>=18.4.0', # Server - 'service_identity', # Identity manager for Celery (not installed with Celery though) - 'pyasn1', # Required for service_identity (but not listed as a dependency!) - 'pyparsing', # Also for processing requests - 'sqlalchemy', - ], - -# Optionally define extras -if 'minimal' in sys.argv: - print('Performing minimal installation -- celery, twisted, and redis excluded') - sys.argv.remove('minimal') - requirements = [ - 'sciris', # Basic tools -- NB, this includes numpy, scipy, pandas, and matplotlib as dependencies - 'decorator>=4.1.2', # For API calls - 'mpld3', # Rendering plots in the browser - 'flask>=1.0.0', # Creating the webapp - 'pyparsing', # Also for processing requests - ], +# Load requirements from txt file +with open('requirements.txt') as f: + requirements = f.read().splitlines() # Get version versionpath = os.path.join(cwd, 'scirisweb', 'sw_version.py') version = runpy.run_path(versionpath)['__version__'] # Get the documentation -with open(os.path.join(cwd, 'README.md'), "r") as fh: +with open(os.path.join(cwd, 'README.rst'), "r") as fh: long_description = fh.read() CLASSIFIERS = [ - 'Environment :: Console', + 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Development Status :: 4 - Beta', - 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Topic :: Software Development', + 'Topic :: Scientific/Engineering', ] setup( name='scirisweb', version=version, - author='ScirisOrg', + author='Sciris Development Team', author_email='info@sciris.org', description='Scientific webapps for Python', long_description=long_description, - long_description_content_type="text/markdown", + long_description_content_type="text/x-rst", url='http://github.com/sciris/scirisweb', keywords=['scientific', 'webapp', 'framework'], platforms=['OS Independent'], @@ -79,4 +58,4 @@ packages=find_packages(), include_package_data=True, install_requires=requirements - ) +) From 5e744f1f84afc860484b9059c52011d245147382 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:06:13 -0800 Subject: [PATCH 37/72] working with flask updates --- scirisweb/sw_app.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scirisweb/sw_app.py b/scirisweb/sw_app.py index ebb0432..45b73ed 100644 --- a/scirisweb/sw_app.py +++ b/scirisweb/sw_app.py @@ -1,7 +1,5 @@ """ -sw_app.py -- classes for Sciris (Flask-based) apps - -Last update: 2018nov14 +ScirisWeb wrapper for a Flask app """ # Imports @@ -24,7 +22,7 @@ from twisted.web.server import Site from twisted.web.static import File from twisted.web.wsgi import WSGIResource -from werkzeug.serving import run_with_reloader +from werkzeug import run_simple from werkzeug.exceptions import HTTPException from werkzeug.utils import secure_filename @@ -290,13 +288,13 @@ def run_twisted(port=8080, flask_app=None, client_dir=None, do_log=False, reacto # Display the logo port = int(self.config['SERVER_PORT']) # Not sure if casting to int is necessary appstring = 'ScirisApp "%s" is now running on port %s' % (self.name, port) - borderstr = '='*len(appstring) - logostr = '''\ - ___ ___ %s - / __|/ __| %s - \__ \ |__ %s - |___/\___| %s - %s''' % (' '*(len(appstring)+4), borderstr, appstring, borderstr, ' '*(len(appstring)+5)) + borderstr = '—'*len(appstring) + logostr = f''' + ___ ___ {(' '*(len(appstring)+5))} + / __|/ __| {borderstr} + \__ \ |__ {appstring} + |___/\___| {borderstr} + {' '*(len(appstring)+5)}''' logocolors = ['gray','bgblue'] # ['gray','bgblue'] if show_logo: print('') @@ -321,7 +319,7 @@ def run_twisted(port=8080, flask_app=None, client_dir=None, do_log=False, reacto run_fcn = lambda: run_twisted(**twisted_args) if autoreload: - run_fcn = run_with_reloader(run_fcn) + run_fcn = run_simple(run_fcn, use_reloader=True) return run_fcn() From 0bece2952976438dc8928ac94333dbe6f69f6ce7 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:16:19 -0800 Subject: [PATCH 38/72] remove return none --- scirisweb/sw_app.py | 21 ++++++------- scirisweb/sw_datastore.py | 54 ++++++++++++++++---------------- scirisweb/sw_tasks.py | 65 ++++++++++++++++++++------------------- scirisweb/sw_users.py | 9 +++--- 4 files changed, 75 insertions(+), 74 deletions(-) diff --git a/scirisweb/sw_app.py b/scirisweb/sw_app.py index 45b73ed..15b17f9 100644 --- a/scirisweb/sw_app.py +++ b/scirisweb/sw_app.py @@ -168,15 +168,14 @@ def load_user(userid): self._init_tasks() # Initialize the users. self.add_RPC_dict(tasks.RPC_dict) # Register the RPCs in the user.py module. - return None # End of __init__ + return def _init_logger(self): self.flask_app.logger.setLevel(logging.DEBUG) - return None + return def _set_config_defaults(self): if 'CLIENT_DIR' not in self.config: self.config['CLIENT_DIR'] = '.' -# if 'CLIENT_DIR' not in self.config: self.config['CLIENT_DIR'] = None if 'LOGGING_MODE' not in self.config: self.config['LOGGING_MODE'] = 'FULL' if 'SERVER_PORT' not in self.config: self.config['SERVER_PORT'] = 8080 if 'USE_DATASTORE' not in self.config: self.config['USE_DATASTORE'] = False @@ -184,7 +183,7 @@ def _set_config_defaults(self): if 'USE_TASKS' not in self.config: self.config['USE_TASKS'] = False if 'MATPLOTLIB_BACKEND' not in self.config: self.config['MATPLOTLIB_BACKEND'] = 'Agg' if 'SLACK' not in self.config: self.config['SLACK'] = None - return None + return def _update_config_defaults(self, **kwargs): ''' Used to update config with command-line arguments ''' @@ -199,7 +198,7 @@ def _update_config_defaults(self, **kwargs): for validkey in sorted(self.config.keys()): warningmsg += ' %s\n' % validkey print(warningmsg) - return None + return def _init_file_dirs(self): @@ -212,7 +211,7 @@ def _init_file_dirs(self): if not os.path.isabs(self.config['CLIENT_DIR']): self.config['CLIENT_DIR'] = os.path.join(self.config['ROOT_ABS_DIR'], self.config['CLIENT_DIR']) - return None + return def _init_datastore(self, use_db=True): if use_db: @@ -233,7 +232,7 @@ def _init_datastore(self, use_db=True): print(' Key %02i: %s' % (k,key)) else: self.datastore = ds.DataDir() # Initialize with a simple temp data directory instead - return None + return def _init_tasks(self): # Have the tasks.py module make the Celery app to connect to the worker, passing in the config parameters. @@ -249,7 +248,7 @@ def run_twisted(port=8080, flask_app=None, client_dir=None, do_log=False, reacto if (flask_app is None) and (client_dir is None): print('ERROR: Neither client or server are defined.') - return None + return if do_log: # Set up logging. globalLogBeginner.beginLoggingTo([FileLogObserver(sys.stdout, lambda _: formatEvent(_) + "\n")]) if client_dir is not None: # If there is a client path, set up the base resource. @@ -271,7 +270,7 @@ def run_twisted(port=8080, flask_app=None, client_dir=None, do_log=False, reacto endpoint = serverFromString(reactor, "tcp:port=" + str(port)) # Create the endpoint we want to listen on, and point it to the site. endpoint.listen(site) reactor.run(**reactor_args) # Start the reactor. - return None + return # To allow arguments to be passed to the run function if run_args is None: @@ -329,7 +328,7 @@ def define_endpoint_layout(self, rule, layout): # Set up the callback, to point to the _layout_render() function. self.flask_app.add_url_rule(rule, 'layout_render', self._layout_render) - return None + return def slacknotification(self, message=None): ''' Send a message on Slack ''' @@ -340,7 +339,7 @@ def slacknotification(self, message=None): sc.slacknotification(message=message, webhook=slack_webhook, to=slack_to, fromuser=slack_from, die=False) else: print('Cannot send Slack message "%s": Slack not enabled in config file' % message) - return None + return def route(self, rule, methods=None, *args, **kwargs): ''' Shortcut to Flask route decorator ''' diff --git a/scirisweb/sw_datastore.py b/scirisweb/sw_datastore.py index 41f6f53..338ceb7 100644 --- a/scirisweb/sw_datastore.py +++ b/scirisweb/sw_datastore.py @@ -61,7 +61,7 @@ def __init__(self, obj=None, key=None, objtype=None, uid=None, force=True): self.created = sc.now() self.modified = [self.created] self.obj = obj - return None + return def update(self): ''' When the object is updated, append the current time to the modified list ''' @@ -73,7 +73,7 @@ def save(self, obj): ''' Save new object to the Blob ''' self.obj = obj self.update() - return None + return def load(self): ''' Load data from the Blob ''' @@ -176,7 +176,7 @@ def __init__(self, settings=None, tempfolder=None, separator=None): if not self.separator: self.separator = sep - return None + return @@ -202,7 +202,7 @@ def __init__(self, tempfolder=None, separator=None, settingskey=None, verbose=Tr self.verbose = verbose self.settings(settingskey=settingskey, tempfolder=tempfolder, separator=separator) # Set or get the settings if self.verbose: print(self) - return None + return ### DATASTORE BACKEND-SPECIFIC METHODS, THAT NEED TO BE DEFINED IN DERIVED CLASSES FOR SPECIFIC STORAGE MECHANISMS E.G. REDIS @@ -298,7 +298,7 @@ def set(self, key=None, obj=None, objtype=None, uid=None): key = self.getkey(key=key, objtype=objtype, uid=uid, obj=obj) objstr = sc.dumpstr(obj) self._set(key, objstr) - return None + return def get(self, key=None, obj=None, objtype=None, uid=None, notnone=False, die=False): @@ -327,7 +327,7 @@ def get(self, key=None, obj=None, objtype=None, uid=None, notnone=False, die=Fal errormsg = 'Datastore key "%s" not found (obj=%s, objtype=%s, uid=%s)' % (key, obj, objtype, uid) raise KeyError(errormsg) elif objstr is None: - return None + return try: output = sc.loadstr(objstr, die=die) @@ -355,7 +355,7 @@ def delete(self, key=None, obj=None, objtype=None, uid=None, die=None): key = self.getkey(key=key, objtype=objtype, uid=uid, obj=obj) self._delete(key) if self.verbose: print('DataStore: deleted key %s' % key) - return None + return def exists(self, key): @@ -376,7 +376,7 @@ def exists(self, key): def flushdb(self): self._flushdb() if self.verbose: print('DataStore flushed.') - return None + return def keys(self, pattern=None): @@ -423,7 +423,7 @@ def _rmtempfolder(self): if os.path.exists(self.tempfolder): if self.verbose: print('Removing up temporary folder at %s' % self.tempfolder) shutil.rmtree(self.tempfolder) - return None + return def makekey(self, objtype, uid): @@ -531,7 +531,7 @@ def _checktype(self, key, obj, objtype): errormsg = 'Cannot load %s as a %s since it is %s' % (key, objtype, type(obj)) raise Exception(errormsg) - return None + return def saveblob(self, obj, key=None, objtype=None, uid=None, overwrite=None, forcetype=None, die=None): @@ -573,7 +573,7 @@ def loadblob(self, key=None, objtype=None, uid=None, forcetype=None, die=None): return obj else: if self.verbose: print('DataStore: Blob "%s" not found' % key) - return None + return def saveuser(self, user, overwrite=True, forcetype=None, die=None): @@ -607,7 +607,7 @@ def loaduser(self, username=None, key=None, forcetype=None, die=None): return user else: if self.verbose: print('DataStore: User "%s" not found' % key) - return None + return def savetask(self, task, key=None, uid=None, overwrite=None, forcetype=None): @@ -638,7 +638,7 @@ def loadtask(self, key=None, uid=None, forcetype=None, die=None): return task else: if self.verbose: print('DataStore: Task "%s" not found' % key) - return None + return @@ -667,7 +667,7 @@ def __init__(self, url=None, redisargs=None, *args, **kwargs): super(RedisDataStore, self).__init__(*args, **kwargs) else: super().__init__(*args, **kwargs) - return None + return ### DEFINE MANDATORY FUNCTIONS @@ -690,12 +690,12 @@ def _get(self, key): def _delete(self, key): self.redis.delete(key) - return None + return def _flushdb(self): self.redis.flushdb() - return None + return def _keys(self): @@ -766,7 +766,7 @@ class SQLBlob(Base): super(SQLDataStore, self).__init__(*args, **kwargs) else: super().__init__(*args, **kwargs) - return None + return ### DEFINE MANDATORY FUNCTIONS @@ -785,7 +785,7 @@ def _set(self, key, objstr): obj.content = objstr session.commit() session.close() - return None + return def _get(self, key): @@ -793,7 +793,7 @@ def _get(self, key): obj = session.query(self.datatype).get(key) session.close() if obj is None: - return None + return else: return obj.content @@ -803,7 +803,7 @@ def _delete(self, key): session.query(self.datatype).filter(self.datatype.key==key).delete() session.commit() session.close() - return None + return def _flushdb(self): @@ -811,7 +811,7 @@ def _flushdb(self): # when the datastore is next instantiated self.datatype.__table__.drop(self.engine) self.engine.dispose() - return None + return def _keys(self): @@ -842,7 +842,7 @@ def __init__(self, url=None, suffix=None, prefix=None, dir=None, *args, **kwargs super(FileDataStore, self).__init__(*args, **kwargs) else: super().__init__(*args, **kwargs) - return None + return ### DEFINE MANDATORY FUNCTIONS @@ -854,7 +854,7 @@ def __repr__(self): def _set(self, key, objstr): with open(self.path + key,'wb') as f: f.write(objstr) - return None + return def _get(self, key): @@ -863,19 +863,19 @@ def _get(self, key): objstr = f.read() return objstr else: - return None + return def _delete(self, key): if os.path.exists(self.path + key): os.remove(self.path + key) - return None + return def _flushdb(self): shutil.rmtree(self.path) os.mkdir(self.path) - return None + return def _keys(self): @@ -907,4 +907,4 @@ def __init__(self, die=False, *args, **kwargs): self.tempfolder = errormsg # Store the error message in lieu of the folder name if die: raise Exception(errormsg) else: print(errormsg) # Try to proceed if no datastore and the temporary directory can't be created - return None \ No newline at end of file + return \ No newline at end of file diff --git a/scirisweb/sw_tasks.py b/scirisweb/sw_tasks.py index c8ea0df..a2eeec8 100644 --- a/scirisweb/sw_tasks.py +++ b/scirisweb/sw_tasks.py @@ -70,41 +70,44 @@ def __init__(self, task_id): self.stop_time = None self.pending_time = None # Start the pending and execution times at None. self.execution_time = None - return None + return def show(self): - print('-----------------------------') - print(' Task ID: %s' % self.task_id) - print(' Status: %s' % self.status) - print(' Error msg: %s' % self.error_msg) - print('Error traceback: %s' % self.error_text) - print(' Function name: %s' % self.func_name) - print(' Function args: %s' % self.args) - print('Function kwargs: %s' % self.kwargs) - print(' Result ID: %s' % self.result_id) - print(' Queue time: %s' % self.queue_time) - print(' Start time: %s' % self.start_time) - print(' Stop time: %s' % self.stop_time) - print(' Pending time: %s s' % self.pending_time) - print(' Execution time: %s s' % self.execution_time) - print('-----------------------------') + string = ''' +----------------------------- + Task ID: {self.task_id} + Status: {self.status} + Error message: {self.error_msg} +Error traceback: {self.error_text} + Function name: {self.func_name} + Function args: {self.args} +Function kwargs: {self.kwargs} + Result ID: {self.result_id} + Queue time: {self.queue_time} + Start time: {self.start_time} + Stop time: {self.stop_time} + Pending time: {self.pending_time} s + Execution time: {self.execution_time} s +-----------------------------''' + print(string) + return def jsonify(self): output = {'task': {'UID': self.uid, - 'taskId': self.task_id, + 'task_id': self.task_id, 'status': self.status, - 'errorMsg': self.error_msg, - 'errorText': self.error_text, - 'funcName': self.func_name, - 'funcArgs': self.args, - 'funcKwargs': self.kwargs, - 'resultId': self.result_id, - 'queueTime': self.queue_time, - 'startTime': self.start_time, - 'stopTime': self.stop_time, - 'pendingTime': self.pending_time, - 'executionTime': self.execution_time + 'error_msg': self.error_msg, + 'error_text': self.error_text, + 'func_name': self.func_name, + 'func_args': self.args, + 'func_kwargs': self.kwargs, + 'result_id': self.result_id, + 'queue_time': self.queue_time, + 'start_time': self.start_time, + 'stop_time': self.stop_time, + 'pending_time': self.pending_time, + 'execution_time': self.execution_time } } return output @@ -164,14 +167,14 @@ def lock_run_task(task_id): sleep(sleepduration) # sleep before trying again if verbose: print('C>> No lock detected on %s, locking run_task()' % task_id) run_task_lock = True # Set the lock to keep other run_task() instances from co-occurring on the same Celery worker. - return None + return def unlock_run_task(task_id): global run_task_lock if verbose: print('C>> Unlocking run_task() for %s' % task_id) run_task_lock = False # Remove the lock for this Celery worker. - return None + return @celery_instance.task @@ -311,7 +314,7 @@ def add_task_funcs(new_task_funcs): global task_func_dict for key in new_task_funcs: # For all of the keys in the dict passed in, put the key/value pairs in the global dict. task_func_dict[key] = new_task_funcs[key] - return None + return @RPC(validation='named') diff --git a/scirisweb/sw_users.py b/scirisweb/sw_users.py index 8a477ed..8dad00e 100644 --- a/scirisweb/sw_users.py +++ b/scirisweb/sw_users.py @@ -67,7 +67,7 @@ def __init__(self, username=None, password=None, displayname=None, email=None, if six.PY3: raw_password = raw_password.encode('utf-8') password = sc.sha(raw_password).hexdigest() self.password = password - return None + return def get_id(self): @@ -91,7 +91,7 @@ def show(self, verbose=False): print(' Is anonymous: %s' % self.is_anonymous) print(' Is admin: %s' % self.is_admin) print('---------------------') - return None + return def jsonify(self, verbose=False): @@ -122,11 +122,10 @@ def jsonify(self, verbose=False): __all__ += ['admin_deactivate_account', 'admin_grant_admin', 'admin_revoke_admin', 'admin_reset_password', 'make_default_users'] - def save_user(user): ''' Save the specified user to the DataStore ''' app.datastore.saveuser(user) - return None + return def load_user(username=None): @@ -164,7 +163,7 @@ def user_login(username, password, verbose=False): def user_logout(): logout_user() # Log the user out and set the session to having an anonymous user. session.clear() # Clear the session cookie. - return None # Return nothing. + return # Return nothing. @RPC() From 4ce5e624e68b91a0b876ba8b99d68aed4cb80773 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:25:21 -0800 Subject: [PATCH 39/72] update docstrings --- scirisweb/sw_server.py | 30 ++++++++++++++---------------- scirisweb/sw_users.py | 30 ++++++++++++++++++------------ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/scirisweb/sw_server.py b/scirisweb/sw_server.py index c250969..a86b613 100644 --- a/scirisweb/sw_server.py +++ b/scirisweb/sw_server.py @@ -54,23 +54,20 @@ def find_open_port(ip, port, n=50): def serve(html, ip='127.0.0.1', port=8888, n_retries=50): - """Start a server serving the given HTML, and open a browser - - Example: + """ + Start a server serving the given HTML, and open a browser. + + Args: + html (str): HTML to serve + ip (str, default = '127.0.0.1'): IP address at which the HTML will be served. + port (int, default = 8888): the port at which to serve the HTML + n_retries (int, default = 50): the number of nearby ports to search if the specified port is in use. + + Example:: + html = 'Hello, world!' import scirisweb as sw sw.serve(html) - - Parameters - ---------- - html : string - HTML to serve - ip : string (default = '127.0.0.1') - ip address at which the HTML will be served. - port : int (default = 8888) - the port at which to serve the HTML - n_retries : int (default = 50) - the number of nearby ports to search if the specified port is in use. """ port = find_open_port(ip, port, n_retries) Handler = generate_handler(html) @@ -118,14 +115,15 @@ def browser(figs=None, doserve=True, legacy=False, jquery_url=None, d3_url=None, ''' Create an MPLD3 GUI and display in the browser. - Usage example: + Usage example:: + import pylab as pl figs = [] for n in [10, 50]: fig = pl.figure() pl.plot(pl.rand(n), pl.rand(n)) figs.append(fig) - qs.browser(figs) + sw.browser(figs) figs can be a single figure or a list of figures. diff --git a/scirisweb/sw_users.py b/scirisweb/sw_users.py index 2fc65ef..6dbe5d1 100644 --- a/scirisweb/sw_users.py +++ b/scirisweb/sw_users.py @@ -12,7 +12,7 @@ ############################################################## -### Globals +#%% Globals ############################################################## __all__ = ['RPC_dict'] @@ -23,7 +23,7 @@ ############################################################## -### Classes +#%% Classes ############################################################## __all__ += ['User'] @@ -99,10 +99,10 @@ def show(self, verbose=False): def jsonify(self, verbose=False): ''' Return a JSON-friendly representation of a user ''' output = {'user': - {'username': self.username, - 'displayname': self.displayname, - 'email': self.email, - 'uid': self.uid} + {'username': self.username, + 'displayname': self.displayname, + 'email': self.email, + 'uid': self.uid} } if verbose: output['user'].update({'is_authenticated': self.is_authenticated, @@ -116,7 +116,7 @@ def jsonify(self, verbose=False): ############################################################## -### Functions and RPCs +#%% Functions and RPCs ############################################################## __all__ += ['save_user', 'load_user', 'user_login', 'user_logout', 'user_register'] @@ -146,6 +146,7 @@ def load_user(username=None): @RPC() def user_login(username, password, verbose=False): + """ Log the user in """ matching_user = app.datastore.loaduser(username, die=False) # Get the matching user (if any). # If we have a match and the password matches, and the account is active, also, log in the user and return success; otherwise, return failure. @@ -165,6 +166,7 @@ def user_login(username, password, verbose=False): @RPC(validation='named') def user_logout(): + """ Log the user out """ logout_user() # Log the user out and set the session to having an anonymous user. session.clear() # Clear the session cookie. return None # Return nothing. @@ -172,6 +174,7 @@ def user_logout(): @RPC() def user_register(username, password, displayname, email): + """ Register a new user """ matching_user = app.datastore.loaduser(username, die=False) # Get the matching user (if any). if matching_user is not None: # If we have a match, fail because we don't want to register an existing user. errormsg = 'User registration failed: user "%s" already exists' % username @@ -184,6 +187,7 @@ def user_register(username, password, displayname, email): @RPC(validation='named') def user_change_info(username, password, displayname, email): + """ Change a user's username, password, display name, or email """ the_user = app.datastore.loaduser(current_user.username) # Reload the current user from the database if password != the_user.password: # If the password entered doesn't match the current user password, fail. errormsg = 'User info change failed: password for user "%s" is incorrect.' % username @@ -208,6 +212,7 @@ def user_change_info(username, password, displayname, email): @RPC(validation='named') def user_change_password(oldpassword, newpassword): + """ Change a user's password """ the_user = app.datastore.loaduser(current_user.username) # Reload the current user from the database # If the password entered doesn't match the current user password, fail. @@ -222,6 +227,7 @@ def user_change_password(oldpassword, newpassword): @RPC(validation='admin') def admin_delete_user(username): + """ Remove a user """ matching_user = app.datastore.loaduser(username) # Get the matching user (if any). if matching_user is None: return 'failure' # If we don't have a match, fail. app.datastore.delete(objtype='user', uid=username) # Delete the user from the dictionary. @@ -230,7 +236,7 @@ def admin_delete_user(username): @RPC(validation='admin') def admin_activate_account(username): - # Get the matching user (if any). + """ Re-enable a user account """ matching_user = app.datastore.loaduser(username) # If we don't have a match, fail. @@ -250,7 +256,7 @@ def admin_activate_account(username): @RPC(validation='admin') def admin_deactivate_account(username): - # Get the matching user (if any). + """ Disable a user account """ matching_user = app.datastore.loaduser(username) # If we don't have a match, fail. @@ -270,7 +276,7 @@ def admin_deactivate_account(username): @RPC(validation='admin') def admin_grant_admin(username): - # Get the matching user (if any). + """ Add admin privileges """ matching_user = app.datastore.loaduser(username) # If we don't have a match, fail. @@ -290,7 +296,7 @@ def admin_grant_admin(username): @RPC(validation='admin') def admin_revoke_admin(username): - # Get the matching user (if any). + """ Remove admin privileges """ matching_user = app.datastore.loaduser(username) # If we don't have a match, fail. @@ -310,7 +316,7 @@ def admin_revoke_admin(username): @RPC(validation='admin') def admin_reset_password(username): - # Get the matching user (if any). + """ Reset the admin password """ matching_user = app.datastore.loaduser(username) # If we don't have a match, fail. From 56ea84449acf558ff2d5d1f59959be8608f0d0dd Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:36:28 -0800 Subject: [PATCH 40/72] add app test --- tests/test_app.py | 27 +++++++++++++++++++++++++++ tests/test_basic.py | 8 -------- 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 tests/test_app.py delete mode 100644 tests/test_basic.py diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..404f831 --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,27 @@ +""" +Test that the API works +""" + +import scirisweb as sw + + +def test_api(): + + app = sw.ScirisApp(__name__, name="helloworld") + + @app.route("/") + def hello(): + output = 'Hello world!' + return output + + client = app.flask_app.test_client() + response = client.get('/') + + assert response.status_code == 200 + assert 'Hello' in response.text + + return client + + +if __name__ == '__main__': + client = test_api() \ No newline at end of file diff --git a/tests/test_basic.py b/tests/test_basic.py deleted file mode 100644 index b69ec60..0000000 --- a/tests/test_basic.py +++ /dev/null @@ -1,8 +0,0 @@ -def test_import(): - # Just check that we can import everything - import sciris as sc - import scirisweb as sw - print('Import OK') - -if __name__ == '__main__': - test_import() \ No newline at end of file From a46a0f95dada806ba76c08b82ad99edbeef0b065 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:40:50 -0800 Subject: [PATCH 41/72] updating tests --- tests/requirements_test.txt | 2 +- tests/run_tests | 2 +- tests/test_server.py | 83 +++++++++++++++++-------------------- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/tests/requirements_test.txt b/tests/requirements_test.txt index 8f9e9b3..07dd842 100644 --- a/tests/requirements_test.txt +++ b/tests/requirements_test.txt @@ -1,3 +1,3 @@ pytest -pytest-parallel +pytest-xdist coverage \ No newline at end of file diff --git a/tests/run_tests b/tests/run_tests index 6d19a3b..49cc2a2 100755 --- a/tests/run_tests +++ b/tests/run_tests @@ -2,4 +2,4 @@ # Run the integration tests. Requires pytest-parallel: # pip install -r requirements_test.txt -pytest test_*.py --workers auto --durations=0 \ No newline at end of file +pytest test_*.py -n auto --durations=0 \ No newline at end of file diff --git a/tests/test_server.py b/tests/test_server.py index 235772e..9f7c8a3 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -7,52 +7,43 @@ import scirisweb as sw -torun = [ -'blank', # An empty figure -'browser', # Simple example of three figures -'advanced', # Illustrates separate legend plotting and direct editing of the JSON -] - -if 'doplot' not in locals(): doplot = True +def test_blank(): + return sw.browser() + + +def test_browser(): + figs = [] + for n in [10, 50]: + fig = pl.figure() + pl.plot(pl.rand(n), pl.rand(n)) + figs.append(fig) + barfig = pl.figure() + pl.bar(pl.arange(10), pl.rand(10)) + barjson = sw.mpld3ify(barfig) + return sw.browser(figs=figs+[barjson]) + + +def test_advanced(): + + def make_fig(): + fig = pl.figure() + ax = fig.add_subplot(111) + ax.plot([1,4,3,4,], label='mish') + ax.plot([8,4,3,2], label='mashed potatoes') + return fig,ax + + fig,ax = make_fig() + legend = sc.separatelegend(ax, figsettings={'figsize':(4.8,4.8)}) + + json1 = sw.mpld3ify(fig, jsonify=False) + json2 = sw.mpld3ify(legend, jsonify=False) + json2['axes'][0]['texts'][1]['text'] = 'mashed potatoes' # Change legend text + json2['axes'][0]['paths'] = [] # Remove the box around the legend + + return sw.browser([json1, json2]) if __name__ == '__main__': - - if 'blank' in torun and doplot: - if doplot: - sw.browser() - - - if 'browser' in torun: - figs = [] - for n in [10, 50]: - fig = pl.figure() - pl.plot(pl.rand(n), pl.rand(n)) - figs.append(fig) - barfig = pl.figure() - pl.bar(pl.arange(10), pl.rand(10)) - barjson = sw.mpld3ify(barfig) - if doplot: - sw.browser(figs=figs+[barjson]) - - - - if 'advanced' in torun: - - def make_fig(): - fig = pl.figure() - ax = fig.add_subplot(111) - ax.plot([1,4,3,4,], label='mish') - ax.plot([8,4,3,2], label='mashed potatoes') - return fig,ax - - fig,ax = make_fig() - legend = sc.separatelegend(ax, figsettings={'figsize':(4.8,4.8)}) - - json1 = sw.mpld3ify(fig, jsonify=False) - json2 = sw.mpld3ify(legend, jsonify=False) - json2['axes'][0]['texts'][1]['text'] = 'mashed potatoes' # Change legend text - json2['axes'][0]['paths'] = [] # Remove the box around the legend - - if doplot: - sw.browser([json1, json2]) \ No newline at end of file + test_blank() + test_browser() + test_advanced() \ No newline at end of file From 7ff9b4fa841a01608c093aa11d9d576a556c03a3 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:52:37 -0800 Subject: [PATCH 42/72] datastore tests pass now, partly --- tests/test_app.py | 6 +++--- tests/test_datastore.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/test_app.py b/tests/test_app.py index 4a3c986..6942f74 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -40,12 +40,12 @@ def test_defaults(): def test_logfile(): logfile = 'temp_logfile.log' - app = sw.ScirisApp(__name__, config=sw.Config(), logfile=logfile) + sw.ScirisApp(__name__, config=sw.Config(), logfile=logfile) os.remove(logfile) ds = sw.DataDir() - with pytest.raises(Exception) as e: - app = sw.ScirisApp(__name__, config=sw.Config(), logfile=ds.tempdir_obj.name) + with pytest.raises(Exception): + sw.ScirisApp(__name__, config=sw.Config(), logfile=ds.tempdir_obj.name) def test_robustjsonify(app): diff --git a/tests/test_datastore.py b/tests/test_datastore.py index 2a6392c..e01970a 100644 --- a/tests/test_datastore.py +++ b/tests/test_datastore.py @@ -11,7 +11,10 @@ db_file = 'datastore.db' db_folder = './temp_test_datastore' -urls = [f'sqlite:///{db_file}', f'file://{db_folder}/'] +sql_url = f'sqlite:///{db_file}' +file_url = f'file://{db_folder}/' + +urls = [sql_url, file_url] # Some examples of other URIS # urls += [ @@ -33,7 +36,7 @@ def test_datastore(url): assert len(ds.keys()) == 1 # There should be a datastore settings key present ds.flushdb() - ds = sw.make_datastore() + ds = sw.make_datastore(url) assert len(ds.keys()) == 1 # Basic CRUD functionality @@ -112,7 +115,7 @@ def test_copy_datastore(): def test_misc(): - ds = sw.make_datastore() + ds = sw.make_datastore(file_url) # Save some data ds.saveblob(obj='teststr', key='foo') # Try to save the same object again @@ -136,9 +139,9 @@ def test_misc(): ds.delete() - - if __name__ == '__main__': for url in urls: test_datastore(url) + test_misc() + test_copy_datastore() From 970d01dc71e335689142d6a0b704cb83c981802d Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 17:59:47 -0800 Subject: [PATCH 43/72] tests pass --- tests/test_users.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index 52356c4..b154255 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -2,9 +2,7 @@ test_users.py -- test module for sw_users.py """ -import os import pytest -from flask_login.utils import login_required, current_user, login_user import json import sciris as sc import scirisweb as sw @@ -13,19 +11,11 @@ def isjson(json_input): try: json.loads(json_input) - except ValueError as e: + except ValueError: return False return True -# class TestingUsersConfig(object): -# """ Define some nondefaults for the app """ -# TESTING=True -# USE_USERS = True -# USE_DATASTORE = True -# DATASTORE_URL = f'sqlite:///:memory:' -# REGISTER_AUTOACTIVATE = True # Otherwise user register fails - def make_app(): app = sw.ScirisApp(__name__, config=sw.TestingUsersAppConfig(), name='test_users_app') @@ -66,13 +56,13 @@ def test_user_register(app): admin_user.displayname, admin_user.email ) # Register a new user - response_gremlin = sw.user_register('gremlin', 'Banana', - 'gremlin', 'scirisweb@gremlinmail.com' + response_goblin = sw.user_register('goblin', 'Banana', + 'goblin', 'scirisweb@goblinmail.com' ) # We already created and saved admin assert not response_admin == 'success' - assert response_gremlin == 'success' + assert response_goblin == 'success' def test_admin_actions(app): @@ -102,19 +92,15 @@ def test_admin_actions(app): # assert default_is_inactive response_make_admin = sw.admin_grant_admin('demo') - response_make_admin_none = sw.admin_grant_admin('gizmo') + with pytest.raises(Exception): + sw.admin_grant_admin('does_not_exist') assert response_make_admin == success_str - assert response_make_admin_none == failure_str response_revoke_admin = sw.admin_revoke_admin('demo') - response_revoke_admin_none = sw.admin_revoke_admin('gizmo') assert response_revoke_admin == success_str - assert response_revoke_admin_none == failure_str response_reset_passwd = sw.admin_reset_password('demo') - response_reset_passwd_none = sw.admin_reset_password('gizmo') assert response_reset_passwd == success_str - assert response_reset_passwd_none == failure_str @@ -133,3 +119,14 @@ def test_jsonify(): output = user.jsonify() output_json = sc.sanitizejson(output['user'], tostring=True) assert isjson(output_json) + + +if __name__ == '__main__': + + sw_app = make_app() + + test_new_user(sw_app) + test_user_register(sw_app) + test_admin_actions(sw_app) + test_make_default_users(sw_app) + test_jsonify() \ No newline at end of file From a527938a487acafa3f48b823ff6f9a22c5e63d5a Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 18:02:17 -0800 Subject: [PATCH 44/72] update github tests --- .github/workflows/tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8a7a4df..29392a3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -9,7 +9,7 @@ jobs: fail-fast: false max-parallel: 8 matrix: - python-version: ['3.9'] + python-version: ['3.11'] name: Run tests steps: - name: Checkout sources @@ -25,4 +25,4 @@ jobs: run: pip install -r requirements_test.txt - name: Run integration tests working-directory: ./tests - run: pytest -v test_*.py --workers auto --durations=0 + run: pytest -v test_*.py -n auto --durations=0 From 22ba52730c729eb2ee68267720a4bf0d3ef2e85e Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Tue, 12 Dec 2023 18:04:57 -0800 Subject: [PATCH 45/72] disable browser tests --- tests/{test_server.py => test_browser.py} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename tests/{test_server.py => test_browser.py} (81%) diff --git a/tests/test_server.py b/tests/test_browser.py similarity index 81% rename from tests/test_server.py rename to tests/test_browser.py index 5e6ad5a..83ca09b 100644 --- a/tests/test_server.py +++ b/tests/test_browser.py @@ -1,5 +1,5 @@ """ -Server tests. Do not run with pytest since relies on browser support. +Browser tests. Run locally, not with pytest, since relies on browser support. """ import pylab as pl @@ -7,11 +7,11 @@ import scirisweb as sw -def test_blank(): +def local_test_blank(): return sw.browser() -def test_browser(): +def local_test_browser(): figs = [] for n in [10, 50]: fig = pl.figure() @@ -23,7 +23,7 @@ def test_browser(): return sw.browser(figs=figs+[barjson]) -def test_advanced(): +def local_test_advanced(): def make_fig(): fig = pl.figure() @@ -44,6 +44,6 @@ def make_fig(): if __name__ == '__main__': - test_blank() - test_browser() - test_advanced() + local_test_blank() + local_test_browser() + local_test_advanced() From bed57a4e7dead8f9c09b2eda3572527f3962bd94 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Wed, 13 Dec 2023 09:45:14 -0800 Subject: [PATCH 46/72] update exception types --- scirisweb/sw_app.py | 12 ++++++++---- scirisweb/sw_datastore.py | 22 +++++++++++----------- scirisweb/sw_users.py | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/scirisweb/sw_app.py b/scirisweb/sw_app.py index 5024549..73f5c22 100644 --- a/scirisweb/sw_app.py +++ b/scirisweb/sw_app.py @@ -58,7 +58,8 @@ def robustjsonify(response, fallback=False, verbose=True): flaskjson = flask_jsonify(sc.sanitizejson(response)) except Exception as E: errormsg = 'Flask jsonification of "%s" failed: %s' % (response, str(E)) - raise Exception(errormsg) + exc = type(E) + raise exc(errormsg) from E return flaskjson @@ -107,8 +108,9 @@ def __init__(self, filepath=None, config=None, name=None, RPC_dict=None, logfil print('Redirecting output to %s' % logfile) sys.stdout = open(logfile, 'a+', 1) # Open file for appending, with buffer size of 1 except Exception as E: + exc = type(E) errormsg = 'Could not open logfile "%s": %s' % (logfile, str(E)) - raise Exception(errormsg) + raise exc(errormsg) from E # Decide whether to use colorization -- yes unless a logfile is being used if colorize is None: @@ -456,13 +458,15 @@ def _do_RPC(self, verbose=False): try: uploaded_fname = os.path.join(self.datastore.tempfolder, filename) # Generate a full upload path/file name. except Exception as E: + exc = type(E) errormsg = 'Could not create filename for uploaded file: %s' % str(E) - raise Exception(errormsg) + raise exc(errormsg) from E try: thisfile.save(uploaded_fname) # Save the file to the uploads directory except Exception as E: + exc = type(E) errormsg = 'Could not save uploaded file: %s' % str(E) - raise Exception(errormsg) + raise exc(errormsg) from E args.insert(0, uploaded_fname) # Prepend the file name to the args list. # Show the call of the function. diff --git a/scirisweb/sw_datastore.py b/scirisweb/sw_datastore.py index 7ffa564..136e289 100644 --- a/scirisweb/sw_datastore.py +++ b/scirisweb/sw_datastore.py @@ -51,7 +51,7 @@ def __init__(self, obj=None, key=None, objtype=None, uid=None, force=True): uid = sc.uuid() else: errormsg = 'DataStore: Not creating a new Blob UUID since force is set to False: key=%s, objtype=%s, uid=%s, obj=%s' % (key, objtype, uid, obj) - raise Exception(errormsg) + raise ValueError(errormsg) if not key: key = '%s%s%s' % (objtype, default_separator, uid) # Set attributes @@ -401,7 +401,7 @@ def settings(self, settingskey=None, tempfolder=None, separator=None, die=False) except Exception as E: origsettings = None errormsg = 'Datastore: warning, could not load settings, using defaults: %s' % str(E) - if die: raise Exception(errormsg) + if die: raise ValueError(errormsg) else: print(errormsg) settings = DataStoreSettings(settings=origsettings, tempfolder=tempfolder, separator=separator) self.tempfolder = settings.tempfolder @@ -500,7 +500,7 @@ def getkey(self, key=None, objtype=None, uid=None, obj=None, fulloutput=None, fo final['key'] = self.makekey(objtype=final['objtype'], uid=final['key']) if len(final['key']) > max_key_length: - raise Exception('Key is too long') + raise ValueError('Key is too long') # Return what we need to return if fulloutput: return final['key'], final['objtype'], final['uid'] @@ -522,15 +522,15 @@ def _checktype(self, key, obj, objtype): elif objtype == 'Task': objclass = Task else: errormsg = 'Unrecognized type "%s": must be Blob, User, or Task' - raise Exception(errormsg) + raise ValueError(errormsg) if obj is None: errormsg = 'Cannot load %s as a %s: key not found' % (key, objtype) - raise Exception(errormsg) + raise sc.KeyNotFoundError(errormsg) if not isinstance(obj, objclass): errormsg = 'Cannot load %s as a %s since it is %s' % (key, objtype, type(obj)) - raise Exception(errormsg) + raise TypeError(errormsg) return @@ -553,7 +553,7 @@ def saveblob(self, obj, key=None, objtype=None, uid=None, overwrite=None, forcet blob.save(obj) else: errormsg = 'DataStore: Blob %s already exists and overwrite is set to False' % key - if die: raise Exception(errormsg) + if die: raise RuntimeError(errormsg) else: print(errormsg) else: blob = Blob(key=key, objtype=objtype, uid=uid, obj=obj) @@ -586,7 +586,7 @@ def saveuser(self, user, overwrite=True, forcetype=None, die=None): olduser = self.get(key) if olduser and not overwrite: errormsg = 'DataStore: User %s already exists, not overwriting' % key - if die: raise Exception(errormsg) + if die: raise RuntimeError(errormsg) else: print(errormsg) else: self._checktype(key, user, 'User') @@ -620,7 +620,7 @@ def savetask(self, task, key=None, uid=None, overwrite=None, forcetype=None): oldtask = self.get(key) if oldtask and not overwrite: errormsg = 'DataStore: Task %s already exists' % key - raise Exception(errormsg) + raise RuntimeError(errormsg) else: self._checktype(key, task, 'Task') self.set(key=key, obj=task) @@ -745,7 +745,7 @@ def __init__(self, url, sqlargs=None, *args, **kwargs): self.url = url else: errormsg = 'To create an SQL DataStore, you must supply the URL' - raise Exception(errormsg) + raise ValueError(errormsg) # Define the internal class that is mapped to the SQL database from sqlalchemy.ext.declarative import declarative_base @@ -906,6 +906,6 @@ def __init__(self, die=False, *args, **kwargs): exception = traceback.format_exc() # Grab the trackback stack errormsg = 'Could not create temporary folder: %s' % exception self.tempfolder = errormsg # Store the error message in lieu of the folder name - if die: raise Exception(errormsg) + if die: raise OSError(errormsg) else: print(errormsg) # Try to proceed if no datastore and the temporary directory can't be created return \ No newline at end of file diff --git a/scirisweb/sw_users.py b/scirisweb/sw_users.py index bf7b0b2..11867f7 100644 --- a/scirisweb/sw_users.py +++ b/scirisweb/sw_users.py @@ -138,7 +138,7 @@ def load_user(username=None): username = current_user.get_id() except Exception as E: errormsg = 'No username supplied and could not get current user: %s' % str(E) - raise Exception(errormsg) + raise ValueError(errormsg) user = app.datastore.loaduser(username) return user From a5ad14c50ea4f97cc3f650c1099ffd1b923a3527 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Wed, 13 Dec 2023 10:12:04 -0800 Subject: [PATCH 47/72] making some progress --- examples/hellograph/index.html | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/hellograph/index.html b/examples/hellograph/index.html index fc763db..a56dc26 100644 --- a/examples/hellograph/index.html +++ b/examples/hellograph/index.html @@ -4,10 +4,15 @@ Hello (random) world - - - - + @@ -21,9 +26,9 @@

Hello, graph!

- \ No newline at end of file From 1bd08bdc67294175449ad122d4ccc7637db8c73b Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Wed, 13 Dec 2023 10:12:16 -0800 Subject: [PATCH 48/72] working --- examples/hellograph/index.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/hellograph/index.html b/examples/hellograph/index.html index a56dc26..5fb70c6 100644 --- a/examples/hellograph/index.html +++ b/examples/hellograph/index.html @@ -5,14 +5,15 @@ Hello (random) world + + + + From cb8674b6f0e96e795b0adb3dc25bfc16fa58be4b Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Wed, 13 Dec 2023 10:17:39 -0800 Subject: [PATCH 49/72] working now --- examples/hellograph/index.html | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/examples/hellograph/index.html b/examples/hellograph/index.html index 5fb70c6..095615b 100644 --- a/examples/hellograph/index.html +++ b/examples/hellograph/index.html @@ -4,16 +4,10 @@ Hello (random) world - + - - + + From 04996d85fee9687ec3eab45d29ddd48a96c04241 Mon Sep 17 00:00:00 2001 From: Cliff Kerr Date: Wed, 13 Dec 2023 10:18:32 -0800 Subject: [PATCH 50/72] add assets --- examples/hellograph/assets/README.md | 3 + examples/hellograph/assets/background.jpg | Bin 0 -> 245550 bytes examples/hellograph/assets/bootstrap-vue.css | 641 + examples/hellograph/assets/bootstrap-vue.js | 18962 ++++++++++++++++ examples/hellograph/assets/bootstrap.min.css | 7 + examples/hellograph/assets/d3.v5.min.js | 2 + .../hellograph/assets/mpld3.v0.4.1.min.js | 2 + examples/hellograph/assets/polyfill.min.js | 4 + examples/hellograph/assets/sciris-js.js | 16938 ++++++++++++++ examples/hellograph/assets/vue | 11912 ++++++++++ 10 files changed, 48471 insertions(+) create mode 100644 examples/hellograph/assets/README.md create mode 100644 examples/hellograph/assets/background.jpg create mode 100644 examples/hellograph/assets/bootstrap-vue.css create mode 100644 examples/hellograph/assets/bootstrap-vue.js create mode 100644 examples/hellograph/assets/bootstrap.min.css create mode 100644 examples/hellograph/assets/d3.v5.min.js create mode 100644 examples/hellograph/assets/mpld3.v0.4.1.min.js create mode 100644 examples/hellograph/assets/polyfill.min.js create mode 100644 examples/hellograph/assets/sciris-js.js create mode 100644 examples/hellograph/assets/vue diff --git a/examples/hellograph/assets/README.md b/examples/hellograph/assets/README.md new file mode 100644 index 0000000..a2bc6f5 --- /dev/null +++ b/examples/hellograph/assets/README.md @@ -0,0 +1,3 @@ +# Assets + +This folder is not used, but uses frozen versions of key libraries to help with debugging. \ No newline at end of file diff --git a/examples/hellograph/assets/background.jpg b/examples/hellograph/assets/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a561d6578167e118586898c5fd3c7d3c88e6678a GIT binary patch literal 245550 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 z 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 (xv6#Y-R9~1CINw*;*A~oo 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 .input-group-prepend > .btn-group > .btn, +.input-group > .input-group-append:not(:last-child) > .btn-group > .btn, +.input-group > .input-group-append:last-child > .btn-group:not(:last-child):not(.dropdown-toggle) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .input-group-append > .btn-group > .btn, +.input-group > .input-group-prepend:not(:first-child) > .btn-group > .btn, +.input-group > .input-group-prepend:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.was-validated .form-control:invalid, +.was-validated .form-control:valid, .form-control.is-invalid, .form-control.is-valid { + background-position: right calc(0.375em + 0.1875rem) center; +} + +input[type="color"].form-control { + height: calc(1.5em + 0.75rem + 2px); + padding: 0.125rem 0.25rem; +} + +input[type="color"].form-control.form-control-sm, +.input-group-sm input[type="color"].form-control { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.125rem 0.25rem; +} + +input[type="color"].form-control.form-control-lg, +.input-group-lg input[type="color"].form-control { + height: calc(1.5em + 1rem + 2px); + padding: 0.125rem 0.25rem; +} + +input[type="color"].form-control:disabled { + background-color: #adb5bd; + opacity: 0.65; +} + +/* Base .input-group > .custom-range styling (no PR yet on BS V4) */ +.input-group > .custom-range { + position: relative; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0; +} + +.input-group > .custom-range + .form-control, +.input-group > .custom-range + .form-control-plaintext, +.input-group > .custom-range + .custom-select, +.input-group > .custom-range + .custom-range, +.input-group > .custom-range + .custom-file { + margin-left: -1px; +} + +.input-group > .form-control + .custom-range, +.input-group > .form-control-plaintext + .custom-range, +.input-group > .custom-select + .custom-range, +.input-group > .custom-range + .custom-range, +.input-group > .custom-file + .custom-range { + margin-left: -1px; +} + +.input-group > .custom-range:focus { + z-index: 3; +} + +.input-group > .custom-range:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .custom-range:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .custom-range { + height: calc(1.5em + 0.75rem + 2px); + padding: 0 0.75rem; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + height: calc(1.5em + 0.75rem + 2px); + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .input-group > .custom-range { + transition: none; + } +} + +.input-group > .custom-range:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.input-group > .custom-range:disabled, .input-group > .custom-range[readonly] { + background-color: #e9ecef; +} + +.input-group-lg > .custom-range { + height: calc(1.5em + 1rem + 2px); + padding: 0 1rem; + border-radius: 0.3rem; +} + +.input-group-sm > .custom-range { + height: calc(1.5em + 0.5rem + 2px); + padding: 0 0.5rem; + border-radius: 0.2rem; +} + +/* b-form-input: custom-range validation styling - valid (no PR yet for BS V4.2) */ +.was-validated .input-group .custom-range:valid, .input-group .custom-range.is-valid { + border-color: #28a745; +} + +.was-validated .input-group .custom-range:valid:focus, .input-group .custom-range.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .custom-range:valid:focus::-webkit-slider-thumb, .custom-range.is-valid:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #9be7ac; +} + +.was-validated .custom-range:valid:focus::-moz-range-thumb, .custom-range.is-valid:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #9be7ac; +} + +.was-validated .custom-range:valid:focus::-ms-thumb, .custom-range.is-valid:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #9be7ac; +} + +.was-validated .custom-range:valid::-webkit-slider-thumb, .custom-range.is-valid::-webkit-slider-thumb { + background-color: #28a745; + background-image: none; +} + +.was-validated .custom-range:valid::-webkit-slider-thumb:active, .custom-range.is-valid::-webkit-slider-thumb:active { + background-color: #9be7ac; + background-image: none; +} + +.was-validated .custom-range:valid::-webkit-slider-runnable-track, .custom-range.is-valid::-webkit-slider-runnable-track { + background-color: rgba(40, 167, 69, 0.35); +} + +.was-validated .custom-range:valid::-moz-range-thumb, .custom-range.is-valid::-moz-range-thumb { + background-color: #28a745; + background-image: none; +} + +.was-validated .custom-range:valid::-moz-range-thumb:active, .custom-range.is-valid::-moz-range-thumb:active { + background-color: #9be7ac; + background-image: none; +} + +.was-validated .custom-range:valid::-moz-range-track, .custom-range.is-valid::-moz-range-track { + background: rgba(40, 167, 69, 0.35); +} + +.was-validated .custom-range:valid ~ .valid-feedback, +.was-validated .custom-range:valid ~ .valid-tooltip, .custom-range.is-valid ~ .valid-feedback, +.custom-range.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-range:valid::-ms-thumb, .custom-range.is-valid::-ms-thumb { + background-color: #28a745; + background-image: none; +} + +.was-validated .custom-range:valid::-ms-thumb:active, .custom-range.is-valid::-ms-thumb:active { + background-color: #9be7ac; + background-image: none; +} + +.was-validated .custom-range:valid::-ms-track-lower, .custom-range.is-valid::-ms-track-lower { + background: rgba(40, 167, 69, 0.35); +} + +.was-validated .custom-range:valid::-ms-track-upper, .custom-range.is-valid::-ms-track-upper { + background: rgba(40, 167, 69, 0.35); +} + +.was-validated .input-group .custom-range:invalid, .input-group .custom-range.is-invalid { + border-color: #dc3545; +} + +.was-validated .input-group .custom-range:invalid:focus, .input-group .custom-range.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .custom-range:invalid:focus::-webkit-slider-thumb, .custom-range.is-invalid:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #f6cdd1; +} + +.was-validated .custom-range:invalid:focus::-moz-range-thumb, .custom-range.is-invalid:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #f6cdd1; +} + +.was-validated .custom-range:invalid:focus::-ms-thumb, .custom-range.is-invalid:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem #f6cdd1; +} + +.was-validated .custom-range:invalid::-webkit-slider-thumb, .custom-range.is-invalid::-webkit-slider-thumb { + background-color: #dc3545; + background-image: none; +} + +.was-validated .custom-range:invalid::-webkit-slider-thumb:active, .custom-range.is-invalid::-webkit-slider-thumb:active { + background-color: #f6cdd1; + background-image: none; +} + +.was-validated .custom-range:invalid::-webkit-slider-runnable-track, .custom-range.is-invalid::-webkit-slider-runnable-track { + background-color: rgba(220, 53, 69, 0.35); +} + +.was-validated .custom-range:invalid::-moz-range-thumb, .custom-range.is-invalid::-moz-range-thumb { + background-color: #dc3545; + background-image: none; +} + +.was-validated .custom-range:invalid::-moz-range-thumb:active, .custom-range.is-invalid::-moz-range-thumb:active { + background-color: #f6cdd1; + background-image: none; +} + +.was-validated .custom-range:invalid::-moz-range-track, .custom-range.is-invalid::-moz-range-track { + background: rgba(220, 53, 69, 0.35); +} + +.was-validated .custom-range:invalid ~ .invalid-feedback, +.was-validated .custom-range:invalid ~ .invalid-tooltip, .custom-range.is-invalid ~ .invalid-feedback, +.custom-range.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-range:invalid::-ms-thumb, .custom-range.is-invalid::-ms-thumb { + background-color: #dc3545; + background-image: none; +} + +.was-validated .custom-range:invalid::-ms-thumb:active, .custom-range.is-invalid::-ms-thumb:active { + background-color: #f6cdd1; + background-image: none; +} + +.was-validated .custom-range:invalid::-ms-track-lower, .custom-range.is-invalid::-ms-track-lower { + background: rgba(220, 53, 69, 0.35); +} + +.was-validated .custom-range:invalid::-ms-track-upper, .custom-range.is-invalid::-ms-track-upper { + background: rgba(220, 53, 69, 0.35); +} + +/* b-table: general styling */ +.b-table.table.b-table-fixed { + /* fixed width columns */ + table-layout: fixed; +} + +.b-table.table[aria-busy="true"] { + opacity: 0.55; +} + +.b-table.table > tbody > tr.b-table-details > td { + border-top: none !important; +} + +.b-table.table > caption { + caption-side: bottom; +} + +.b-table.table > caption.b-table-caption-top { + caption-side: top !important; +} + +.b-table.table > thead > tr > th, +.b-table.table > thead > tr > td, +.b-table.table > tfoot > tr > th, +.b-table.table > tfoot > tr > td { + position: relative; +} + +/* b-table: header sort styling */ +.b-table.table > thead > tr > th[aria-sort], +.b-table.table > tfoot > tr > th[aria-sort] { + position: relative; + padding-right: 1.125em; + cursor: pointer; +} + +.b-table.table > thead > tr > th[aria-sort]::after, +.b-table.table > tfoot > tr > th[aria-sort]::after { + position: absolute; + display: block; + bottom: 0; + right: 0.35em; + padding-bottom: inherit; + font-size: inherit; + line-height: inherit; + opacity: 0.4; + content: "\2195"; + speak: none; +} + +.b-table.table > thead > tr > th[aria-sort][aria-sort="ascending"]::after, +.b-table.table > tfoot > tr > th[aria-sort][aria-sort="ascending"]::after { + opacity: 1; + content: "\2193"; +} + +.b-table.table > thead > tr > th[aria-sort][aria-sort="descending"]::after, +.b-table.table > tfoot > tr > th[aria-sort][aria-sort="descending"]::after { + opacity: 1; + content: "\2191"; +} + +/* b-table: stackled tables */ +@media (max-width: 575.98px) { + .b-table.table.b-table-stacked-sm { + display: block; + width: 100%; + } + .b-table.table.b-table-stacked-sm > caption, + .b-table.table.b-table-stacked-sm > tbody, + .b-table.table.b-table-stacked-sm > tbody > tr, + .b-table.table.b-table-stacked-sm > tbody > tr > td, + .b-table.table.b-table-stacked-sm > tbody > tr > td { + display: block; + } + .b-table.table.b-table-stacked-sm > thead, + .b-table.table.b-table-stacked-sm > tfoot { + display: none; + } + .b-table.table.b-table-stacked-sm > thead > tr.b-table-top-row, + .b-table.table.b-table-stacked-sm > thead > tr.b-table-bottom-row, + .b-table.table.b-table-stacked-sm > tfoot > tr.b-table-top-row, + .b-table.table.b-table-stacked-sm > tfoot > tr.b-table-bottom-row { + display: none; + } + .b-table.table.b-table-stacked-sm > caption { + caption-side: top !important; + } + .b-table.table.b-table-stacked-sm > tbody > tr > [data-label] { + display: grid; + grid-template-columns: 40% auto; + grid-gap: 0.25rem 1rem; + } + .b-table.table.b-table-stacked-sm > tbody > tr > [data-label]::before { + content: attr(data-label); + display: inline; + text-align: right; + overflow-wrap: break-word; + font-weight: bold; + font-style: normal; + } + .b-table.table.b-table-stacked-sm > tbody > tr.top-row, .b-table.table.b-table-stacked-sm > tbody > tr.bottom-row { + display: none; + } + .b-table.table.b-table-stacked-sm > tbody > tr > :first-child { + border-top-width: 3px; + } +} + +@media (max-width: 767.98px) { + .b-table.table.b-table-stacked-md { + display: block; + width: 100%; + } + .b-table.table.b-table-stacked-md > caption, + .b-table.table.b-table-stacked-md > tbody, + .b-table.table.b-table-stacked-md > tbody > tr, + .b-table.table.b-table-stacked-md > tbody > tr > td, + .b-table.table.b-table-stacked-md > tbody > tr > td { + display: block; + } + .b-table.table.b-table-stacked-md > thead, + .b-table.table.b-table-stacked-md > tfoot { + display: none; + } + .b-table.table.b-table-stacked-md > thead > tr.b-table-top-row, + .b-table.table.b-table-stacked-md > thead > tr.b-table-bottom-row, + .b-table.table.b-table-stacked-md > tfoot > tr.b-table-top-row, + .b-table.table.b-table-stacked-md > tfoot > tr.b-table-bottom-row { + display: none; + } + .b-table.table.b-table-stacked-md > caption { + caption-side: top !important; + } + .b-table.table.b-table-stacked-md > tbody > tr > [data-label] { + display: grid; + grid-template-columns: 40% auto; + grid-gap: 0.25rem 1rem; + } + .b-table.table.b-table-stacked-md > tbody > tr > [data-label]::before { + content: attr(data-label); + display: inline; + text-align: right; + overflow-wrap: break-word; + font-weight: bold; + font-style: normal; + } + .b-table.table.b-table-stacked-md > tbody > tr.top-row, .b-table.table.b-table-stacked-md > tbody > tr.bottom-row { + display: none; + } + .b-table.table.b-table-stacked-md > tbody > tr > :first-child { + border-top-width: 3px; + } +} + +@media (max-width: 991.98px) { + .b-table.table.b-table-stacked-lg { + display: block; + width: 100%; + } + .b-table.table.b-table-stacked-lg > caption, + .b-table.table.b-table-stacked-lg > tbody, + .b-table.table.b-table-stacked-lg > tbody > tr, + .b-table.table.b-table-stacked-lg > tbody > tr > td, + .b-table.table.b-table-stacked-lg > tbody > tr > td { + display: block; + } + .b-table.table.b-table-stacked-lg > thead, + .b-table.table.b-table-stacked-lg > tfoot { + display: none; + } + .b-table.table.b-table-stacked-lg > thead > tr.b-table-top-row, + .b-table.table.b-table-stacked-lg > thead > tr.b-table-bottom-row, + .b-table.table.b-table-stacked-lg > tfoot > tr.b-table-top-row, + .b-table.table.b-table-stacked-lg > tfoot > tr.b-table-bottom-row { + display: none; + } + .b-table.table.b-table-stacked-lg > caption { + caption-side: top !important; + } + .b-table.table.b-table-stacked-lg > tbody > tr > [data-label] { + display: grid; + grid-template-columns: 40% auto; + grid-gap: 0.25rem 1rem; + } + .b-table.table.b-table-stacked-lg > tbody > tr > [data-label]::before { + content: attr(data-label); + display: inline; + text-align: right; + overflow-wrap: break-word; + font-weight: bold; + font-style: normal; + } + .b-table.table.b-table-stacked-lg > tbody > tr.top-row, .b-table.table.b-table-stacked-lg > tbody > tr.bottom-row { + display: none; + } + .b-table.table.b-table-stacked-lg > tbody > tr > :first-child { + border-top-width: 3px; + } +} + +@media (max-width: 1199.98px) { + .b-table.table.b-table-stacked-xl { + display: block; + width: 100%; + } + .b-table.table.b-table-stacked-xl > caption, + .b-table.table.b-table-stacked-xl > tbody, + .b-table.table.b-table-stacked-xl > tbody > tr, + .b-table.table.b-table-stacked-xl > tbody > tr > td, + .b-table.table.b-table-stacked-xl > tbody > tr > td { + display: block; + } + .b-table.table.b-table-stacked-xl > thead, + .b-table.table.b-table-stacked-xl > tfoot { + display: none; + } + .b-table.table.b-table-stacked-xl > thead > tr.b-table-top-row, + .b-table.table.b-table-stacked-xl > thead > tr.b-table-bottom-row, + .b-table.table.b-table-stacked-xl > tfoot > tr.b-table-top-row, + .b-table.table.b-table-stacked-xl > tfoot > tr.b-table-bottom-row { + display: none; + } + .b-table.table.b-table-stacked-xl > caption { + caption-side: top !important; + } + .b-table.table.b-table-stacked-xl > tbody > tr > [data-label] { + display: grid; + grid-template-columns: 40% auto; + grid-gap: 0.25rem 1rem; + } + .b-table.table.b-table-stacked-xl > tbody > tr > [data-label]::before { + content: attr(data-label); + display: inline; + text-align: right; + overflow-wrap: break-word; + font-weight: bold; + font-style: normal; + } + .b-table.table.b-table-stacked-xl > tbody > tr.top-row, .b-table.table.b-table-stacked-xl > tbody > tr.bottom-row { + display: none; + } + .b-table.table.b-table-stacked-xl > tbody > tr > :first-child { + border-top-width: 3px; + } +} + +.b-table.table.b-table-stacked { + display: block; + width: 100%; +} + +.b-table.table.b-table-stacked > caption, +.b-table.table.b-table-stacked > tbody, +.b-table.table.b-table-stacked > tbody > tr, +.b-table.table.b-table-stacked > tbody > tr > td, +.b-table.table.b-table-stacked > tbody > tr > td { + display: block; +} + +.b-table.table.b-table-stacked > thead, +.b-table.table.b-table-stacked > tfoot { + display: none; +} + +.b-table.table.b-table-stacked > thead > tr.b-table-top-row, +.b-table.table.b-table-stacked > thead > tr.b-table-bottom-row, +.b-table.table.b-table-stacked > tfoot > tr.b-table-top-row, +.b-table.table.b-table-stacked > tfoot > tr.b-table-bottom-row { + display: none; +} + +.b-table.table.b-table-stacked > caption { + caption-side: top !important; +} + +.b-table.table.b-table-stacked > tbody > tr > [data-label] { + display: grid; + grid-template-columns: 40% auto; + grid-gap: 0.25rem 1rem; +} + +.b-table.table.b-table-stacked > tbody > tr > [data-label]::before { + content: attr(data-label); + display: inline; + text-align: right; + overflow-wrap: break-word; + font-weight: bold; + font-style: normal; +} + +.b-table.table.b-table-stacked > tbody > tr.top-row, .b-table.table.b-table-stacked > tbody > tr.bottom-row { + display: none; +} + +.b-table.table.b-table-stacked > tbody > tr > :first-child { + border-top-width: 3px; +} + +/* b-table: selectable rows */ +table.b-table.b-table-selectable > tbody > tr { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*# sourceMappingURL=bootstrap-vue.css.map */ \ No newline at end of file diff --git a/examples/hellograph/assets/bootstrap-vue.js b/examples/hellograph/assets/bootstrap-vue.js new file mode 100644 index 0000000..087d14c --- /dev/null +++ b/examples/hellograph/assets/bootstrap-vue.js @@ -0,0 +1,18962 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.bootstrapVue = factory()); +}(this, function () { 'use strict'; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + + ownKeys.forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } + + return target; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); + } + + function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); + } + + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); + } + + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; + } + + function _possibleConstructorReturn(self, call) { + if (call && (typeof call === "object" || typeof call === "function")) { + return call; + } + + return _assertThisInitialized(self); + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + + var __assign=function(){return (__assign=Object.assign||function(e){for(var a,s=1,t=arguments.length;s 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + + var maxSafeInteger = Math.pow(2, 53) - 1; + + var toLength = function toLength(value) { + return Math.min(Math.max(toInteger(value), 0), maxSafeInteger); + }; // The length property of the from method is 1. + + + return function from(arrayLike + /*, mapFn, thisArg */ + ) { + // 1. Let C be the this value. + var C = this; // 2. Let items be ToObject(arrayLike). + + var items = Object(arrayLike); // 3. ReturnIfAbrupt(items). + + if (arrayLike == null) { + throw new TypeError('Array.from requires an array-like object - not null or undefined'); + } // 4. If mapfn is undefined, then let mapping be false. + + + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + + + if (arguments.length > 2) { + T = arguments[2]; + } + } // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + + + var len = toLength(items.length); // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method + // of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + + var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0. + + var k = 0; // 17. Repeat, while k < len… (also steps a - h) + + var kValue; + + while (k < len) { + kValue = items[k]; + + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + + k += 1; + } // 18. Let putStatus be Put(A, "length", len, true). + + + A.length = len; // 20. Return A. + + return A; + }; + }(); + } // https://tc39.github.io/ecma262/#sec-array.prototype.find + // Needed for IE support + + /* istanbul ignore if */ + + + if (!Array.prototype.find) { + // eslint-disable-next-line no-extend-native + Object.defineProperty(Array.prototype, 'find', { + value: function value(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). + + var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. + + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + + + var thisArg = arguments[1]; // 5. Let k be 0. + + var k = 0; // 6. Repeat, while k < len + + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } // e. Increase k by 1. + + + k++; + } // 7. Return undefined. + + + return undefined; + } + }); + } + /* istanbul ignore if */ + + + if (!Array.isArray) { + Array.isArray = function (arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } // Static + + + var from = Array.from; + var isArray = Array.isArray; // Instance + + var arrayIncludes = function arrayIncludes(array, value) { + return array.indexOf(value) !== -1; + }; + function concat() { + return Array.prototype.concat.apply([], arguments); + } + + function identity(x) { + return x; + } + + /** + * Given an array of properties or an object of property keys, + * plucks all the values off the target object. + * @param {{}|string[]} keysToPluck + * @param {{}} objToPluck + * @param {Function} transformFn + * @return {{}} + */ + + function pluckProps(keysToPluck, objToPluck) { + var transformFn = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : identity; + return (isArray(keysToPluck) ? keysToPluck.slice() : keys(keysToPluck)).reduce(function (memo, prop) { + // eslint-disable-next-line no-sequences + return memo[transformFn(prop)] = objToPluck[prop], memo; + }, {}); + } + + /** + * The Link component is used in many other BV components. + * As such, sharing its props makes supporting all its features easier. + * However, some components need to modify the defaults for their own purpose. + * Prefer sharing a fresh copy of the props to ensure mutations + * do not affect other component references to the props. + * + * https://github.com/vuejs/vue-router/blob/dev/src/components/link.js + * @return {{}} + */ + + function propsFactory() { + return { + href: { + type: String, + default: null + }, + rel: { + type: String, + default: null + }, + target: { + type: String, + default: '_self' + }, + active: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + }, + // router-link specific props + to: { + type: [String, Object], + default: null + }, + append: { + type: Boolean, + default: false + }, + replace: { + type: Boolean, + default: false + }, + event: { + type: [String, Array], + default: 'click' + }, + activeClass: { + type: String // default: undefined + + }, + exact: { + type: Boolean, + default: false + }, + exactActiveClass: { + type: String // default: undefined + + }, + routerTag: { + type: String, + default: 'a' + }, + // nuxt-link specific prop(s) + noPrefetch: { + type: Boolean, + default: false + } + }; + } + function pickLinkProps(propsToPick) { + var freshLinkProps = propsFactory(); // Normalize everything to array. + + propsToPick = concat(propsToPick); + return keys(freshLinkProps).reduce(function (memo, prop) { + if (arrayIncludes(propsToPick, prop)) { + memo[prop] = freshLinkProps[prop]; + } + + return memo; + }, {}); + } + + function computeTag(props, parent) { + return parent.$router && props.to && !props.disabled ? parent.$nuxt ? 'nuxt-link' : 'router-link' : 'a'; + } + + function isRouterLink(tag) { + return tag !== 'a'; + } + + function computeHref(_ref, tag) { + var disabled = _ref.disabled, + href = _ref.href, + to = _ref.to; + + // We've already checked the parent.$router in computeTag, + // so isRouterLink(tag) indicates a live router. + // When deferring to Vue Router's router-link, don't use the href attr at all. + // We return null, and then remove href from the attributes passed to router-link + if (isRouterLink(tag)) { + return null; + } // If href explicitly provided + + + if (href) { + return href; + } // Reconstruct `href` when `to` used, but no router + + + if (to) { + // Fallback to `to` prop (if `to` is a string) + if (typeof to === 'string') { + return to; + } // Fallback to `to.path` prop (if `to` is an object) + + + if (_typeof(to) === 'object' && typeof to.path === 'string') { + return to.path; + } + } // If nothing is provided use '#' as a fallback + + + return '#'; + } + + function computeRel(_ref2) { + var target = _ref2.target, + rel = _ref2.rel; + + if (target === '_blank' && rel === null) { + return 'noopener'; + } + + return rel || null; + } + + function clickHandlerFactory(_ref3) { + var disabled = _ref3.disabled, + tag = _ref3.tag, + href = _ref3.href, + suppliedHandler = _ref3.suppliedHandler, + parent = _ref3.parent; + return function onClick(e) { + if (disabled && e instanceof Event) { + // Stop event from bubbling up. + e.stopPropagation(); // Kill the event loop attached to this specific EventTarget. + + e.stopImmediatePropagation(); + } else { + if (isRouterLink(tag) && e.target.__vue__) { + e.target.__vue__.$emit('click', e); + } + + if (typeof suppliedHandler === 'function') { + suppliedHandler.apply(void 0, arguments); + } + + parent.$root.$emit('clicked::link', e); + } + + if (!isRouterLink(tag) && href === '#' || disabled) { + // Stop scroll-to-top behavior or navigation. + e.preventDefault(); + } + }; + } // @vue/component + + + var BLink = { + name: 'BLink', + functional: true, + props: propsFactory(), + render: function render(h, _ref4) { + var props = _ref4.props, + data = _ref4.data, + parent = _ref4.parent, + children = _ref4.children; + var tag = computeTag(props, parent); + var rel = computeRel(props); + var href = computeHref(props, tag); + var eventType = isRouterLink(tag) ? 'nativeOn' : 'on'; + var suppliedHandler = (data[eventType] || {}).click; + var handlers = { + click: clickHandlerFactory({ + tag: tag, + href: href, + disabled: props.disabled, + suppliedHandler: suppliedHandler, + parent: parent + }) + }; + var componentData = mergeData(data, { + class: { + active: props.active, + disabled: props.disabled + }, + attrs: { + rel: rel, + target: props.target, + tabindex: props.disabled ? '-1' : data.attrs ? data.attrs.tabindex : null, + 'aria-disabled': props.disabled ? 'true' : null + }, + props: _objectSpread({}, props, { + tag: props.routerTag + }) + }); // If href attribute exists on router-link (even undefined or null) it fails working on SSR + // So we explicitly add it here if needed (i.e. if computeHref() is truthy) + + if (href) { + componentData.attrs.href = href; + } // We want to overwrite any click handler since our callback + // will invoke the user supplied handler if !props.disabled + + + componentData[eventType] = _objectSpread({}, componentData[eventType] || {}, handlers); + return h(tag, componentData, children); + } + }; + + var linkProps = propsFactory(); + delete linkProps.href.default; + delete linkProps.to.default; + var props$1 = _objectSpread({}, linkProps, { + tag: { + type: String, + default: 'span' + }, + variant: { + type: String, + default: 'secondary' + }, + pill: { + type: Boolean, + default: false + } // @vue/component + + }); + var BBadge = { + name: 'BBadge', + functional: true, + props: props$1, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var tag = !props.href && !props.to ? props.tag : BLink; + var componentData = { + staticClass: 'badge', + class: [!props.variant ? 'badge-secondary' : "badge-".concat(props.variant), { + 'badge-pill': Boolean(props.pill), + active: props.active, + disabled: props.disabled + }], + props: pluckProps(linkProps, props) + }; + return h(tag, mergeData(data, componentData), children); + } + }; + + var components$1 = { + BBadge: BBadge + }; + var index$1 = { + install: function install(Vue) { + registerComponents(Vue, components$1); + } + }; + + var stripTagsRegex = /(<([^>]+)>)/gi; + function stripTags() { + var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + return String(text).replace(stripTagsRegex, ''); + } + function htmlOrText(innerHTML, textContent) { + return innerHTML ? { + innerHTML: innerHTML + } : { + textContent: textContent + }; + } + + var props$2 = _objectSpread({}, propsFactory(), { + text: { + type: String, + default: null + }, + html: { + type: String, + default: null + }, + ariaCurrent: { + type: String, + default: 'location' + } // @vue/component + + }); + var BBreadcrumbLink = { + name: 'BBreadcrumbLink', + functional: true, + props: props$2, + render: function render(h, _ref) { + var suppliedProps = _ref.props, + data = _ref.data, + children = _ref.children; + var tag = suppliedProps.active ? 'span' : BLink; + var componentData = { + props: pluckProps(props$2, suppliedProps) + }; + + if (suppliedProps.active) { + componentData.attrs = { + 'aria-current': suppliedProps.ariaCurrent + }; + } + + if (!children) { + componentData.domProps = htmlOrText(suppliedProps.html, suppliedProps.text); + } + + return h(tag, mergeData(data, componentData), children); + } + }; + + var BBreadcrumbItem = { + name: 'BBreadcrumbItem', + functional: true, + props: props$2, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h('li', mergeData(data, { + staticClass: 'breadcrumb-item', + class: { + active: props.active + }, + attrs: { + role: 'presentation' + } + }), [h(BBreadcrumbLink, { + props: props + }, children)]); + } + }; + + var props$3 = { + items: { + type: Array, + default: null + } // @vue/component + + }; + var BBreadcrumb = { + name: 'BBreadcrumb', + functional: true, + props: props$3, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var childNodes = children; // Build child nodes from items if given. + + if (isArray(props.items)) { + var activeDefined = false; + childNodes = props.items.map(function (item, idx) { + if (_typeof(item) !== 'object') { + item = { + text: item + }; + } // Copy the value here so we can normalize it. + + + var active = item.active; + + if (active) { + activeDefined = true; + } + + if (!active && !activeDefined) { + // Auto-detect active by position in list. + active = idx + 1 === props.items.length; + } + + return h(BBreadcrumbItem, { + props: _objectSpread({}, item, { + active: active + }) + }); + }); + } + + return h('ol', mergeData(data, { + staticClass: 'breadcrumb' + }), childNodes); + } + }; + + var components$2 = { + BBreadcrumb: BBreadcrumb, + BBreadcrumbItem: BBreadcrumbItem, + BBreadcrumbLink: BBreadcrumbLink + }; + var index$2 = { + install: function install(Vue) { + registerComponents(Vue, components$2); + } + }; + + // Info about the current environment + var inBrowser = typeof document !== 'undefined' && typeof window !== 'undefined'; + var isServer = !inBrowser; + var hasTouchSupport = inBrowser && ('ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0); + var hasPointerEvent = inBrowser && Boolean(window.PointerEvent || window.MSPointerEvent); + + var passiveEventSupported = false; + /* istanbul ignore if */ + + if (inBrowser) { + try { + var options = { + get passive() { + // This function will be called when the browser + // attempts to access the passive property. + passiveEventSupported = true; + } + + }; + window.addEventListener('test', options, options); + window.removeEventListener('test', options, options); + } catch (err) { + passiveEventSupported = false; + } + } // Normalize event options based on support of passive option + + + function parseEventOptions(options) { + var useCapture = false; + + if (options) { + if (_typeof(options) === 'object') { + // eslint-disable-next-line no-unneeded-ternary + useCapture = options.useCapture ? true : false; + } else { + useCapture = options; + } + } + + return passiveEventSupported ? options : useCapture; + } // Attach an event listener to an element + + + var eventOn = function eventOn(el, evtName, handler, options) { + if (el && el.addEventListener) { + el.addEventListener(evtName, handler, parseEventOptions(options)); + } + }; // Remove an event listener from an element + + var eventOff = function eventOff(el, evtName, handler, options) { + if (el && el.removeEventListener) { + el.removeEventListener(evtName, handler, parseEventOptions(options)); + } + }; // Determine if an element is an HTML Element + + var isElement = function isElement(el) { + return el && el.nodeType === Node.ELEMENT_NODE; + }; // Determine if an HTML element is visible - Faster than CSS check + + var isVisible = function isVisible(el) + /* istanbul ignore next: getBoundingClientRect() doesn't work in JSDOM */ + { + if (!isElement(el) || !contains(document.body, el)) { + return false; + } + + var bcr = getBCR(el); + return bcr && bcr.height > 0 && bcr.width > 0; + }; // Determine if an element is disabled + + var isDisabled = function isDisabled(el) { + return !isElement(el) || el.disabled || hasClass(el, 'disabled') || Boolean(getAttr(el, 'disabled')); + }; // Cause/wait-for an element to reflow it's content (adjusting it's height/width) + + var reflow = function reflow(el) { + // Requesting an elements offsetHight will trigger a reflow of the element content + + /* istanbul ignore next: reflow doesn't happen in JSDOM */ + return isElement(el) && el.offsetHeight; + }; // Select all elements matching selector. Returns `[]` if none found + + var selectAll = function selectAll(selector, root) { + if (!isElement(root)) { + root = document; + } + + return from(root.querySelectorAll(selector)); + }; // Select a single element, returns `null` if not found + + var select = function select(selector, root) { + if (!isElement(root)) { + root = document; + } + + return root.querySelector(selector) || null; + }; // Determine if an element matches a selector + + var matches = function matches(el, selector) { + if (!isElement(el)) { + return false; + } // https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill + // Prefer native implementations over polyfill function + + + var proto = Element.prototype; + /* istanbul ignore next */ + + var Matches = proto.matches || proto.matchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector || proto.webkitMatchesSelector || function (sel) + /* istanbul ignore next */ + { + var element = this; + var m = selectAll(sel, element.document || element.ownerDocument); + var i = m.length; // eslint-disable-next-line no-empty + + while (--i >= 0 && m.item(i) !== element) {} + + return i > -1; + }; + + return Matches.call(el, selector); + }; // Finds closest element matching selector. Returns `null` if not found + + var closest = function closest(selector, root) { + if (!isElement(root)) { + return null; + } // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest + // Since we dont support IE < 10, we can use the "Matches" version of the polyfill for speed + // Prefer native implementation over polyfill function + + + var Closest = Element.prototype.closest || function (sel) + /* istanbul ignore next */ + { + var element = this; + + if (!contains(document.documentElement, element)) { + return null; + } + + do { + // Use our "patched" matches function + if (matches(element, sel)) { + return element; + } + + element = element.parentElement; + } while (element !== null); + + return null; + }; + + var el = Closest.call(root, selector); // Emulate jQuery closest and return `null` if match is the passed in element (root) + + return el === root ? null : el; + }; // Returns true if the parent element contains the child element + + var contains = function contains(parent, child) { + if (!parent || typeof parent.contains !== 'function') { + return false; + } + + return parent.contains(child); + }; // Get an element given an ID + + var getById = function getById(id) { + return document.getElementById(/^#/.test(id) ? id.slice(1) : id) || null; + }; // Add a class to an element + + var addClass = function addClass(el, className) { + // We are checking for `el.classList` existence here since IE 11 + // returns `undefined` for some elements (e.g. SVG elements) + // See https://github.com/bootstrap-vue/bootstrap-vue/issues/2713 + if (className && isElement(el) && el.classList) { + el.classList.add(className); + } + }; // Remove a class from an element + + var removeClass = function removeClass(el, className) { + // We are checking for `el.classList` existence here since IE 11 + // returns `undefined` for some elements (e.g. SVG elements) + // See https://github.com/bootstrap-vue/bootstrap-vue/issues/2713 + if (className && isElement(el) && el.classList) { + el.classList.remove(className); + } + }; // Test if an element has a class + + var hasClass = function hasClass(el, className) { + // We are checking for `el.classList` existence here since IE 11 + // returns `undefined` for some elements (e.g. SVG elements) + // See https://github.com/bootstrap-vue/bootstrap-vue/issues/2713 + if (className && isElement(el) && el.classList) { + return el.classList.contains(className); + } + + return false; + }; // Set an attribute on an element + + var setAttr = function setAttr(el, attr, value) { + if (attr && isElement(el)) { + el.setAttribute(attr, value); + } + }; // Remove an attribute from an element + + var removeAttr = function removeAttr(el, attr) { + if (attr && isElement(el)) { + el.removeAttribute(attr); + } + }; // Get an attribute value from an element (returns `null` if not found) + + var getAttr = function getAttr(el, attr) { + if (attr && isElement(el)) { + return el.getAttribute(attr); + } + + return null; + }; // Determine if an attribute exists on an element (returns `true` + // or `false`, or `null` if element not found) + + var hasAttr = function hasAttr(el, attr) { + if (attr && isElement(el)) { + return el.hasAttribute(attr); + } + + return null; + }; // Return the Bounding Client Rect of an element. Returns `null` if not an element + + var getBCR = function getBCR(el) { + /* istanbul ignore next: getBoundingClientRect() doesn't work in JSDOM */ + return isElement(el) ? el.getBoundingClientRect() : null; + }; // Get computed style object for an element + + var getCS = function getCS(el) { + /* istanbul ignore next: getComputedStyle() doesn't work in JSDOM */ + return isElement(el) ? window.getComputedStyle(el) : {}; + }; // Return an element's offset with respect to document element + // https://j11y.io/jquery/#v=git&fn=jQuery.fn.offset + + var offset = function offset(el) + /* istanbul ignore next: getBoundingClientRect(), getClientRects() doesn't work in JSDOM */ + { + var _offset = { + top: 0, + left: 0 + }; + + if (!isElement(el) || el.getClientRects().length === 0) { + return _offset; + } + + var bcr = getBCR(el); + + if (bcr) { + var win = el.ownerDocument.defaultView; + _offset.top = bcr.top + win.pageYOffset; + _offset.left = bcr.left + win.pageXOffset; + } + + return _offset; + }; // Return an element's offset with respect to to it's offsetParent + // https://j11y.io/jquery/#v=git&fn=jQuery.fn.position + + var position = function position(el) + /* istanbul ignore next: getBoundingClientRect() doesn't work in JSDOM */ + { + var _offset = { + top: 0, + left: 0 + }; + + if (!isElement(el)) { + return _offset; + } + + var parentOffset = { + top: 0, + left: 0 + }; + var elStyles = getCS(el); + + if (elStyles.position === 'fixed') { + _offset = getBCR(el) || _offset; + } else { + _offset = offset(el); + var doc = el.ownerDocument; + var offsetParent = el.offsetParent || doc.documentElement; + + while (offsetParent && (offsetParent === doc.body || offsetParent === doc.documentElement) && getCS(offsetParent).position === 'static') { + offsetParent = offsetParent.parentNode; + } + + if (offsetParent && offsetParent !== el && offsetParent.nodeType === Node.ELEMENT_NODE) { + parentOffset = offset(offsetParent); + var offsetParentStyles = getCS(offsetParent); + parentOffset.top += parseFloat(offsetParentStyles.borderTopWidth); + parentOffset.left += parseFloat(offsetParentStyles.borderLeftWidth); + } + } + + return { + top: _offset.top - parentOffset.top - parseFloat(elStyles.marginTop), + left: _offset.left - parentOffset.left - parseFloat(elStyles.marginLeft) + }; + }; // requestAnimationFrame convenience method + // We don't have a version for cancelAnimationFrame, but we don't call it anywhere + + var requestAF = function requestAF(cb) { + var w = inBrowser ? window : {}; + + var rAF = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.mozRequestAnimationFrame || w.msRequestAnimationFrame || w.oRequestAnimationFrame || function (cb) { + // Fallback, but not a true polyfill. + // But all browsers we support (other than Opera Mini) support rAF + // without a polyfill. + + /* istanbul ignore next */ + return setTimeout(cb, 16); + }; + + return rAF(cb); + }; + + var btnProps = { + block: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + }, + size: { + type: String, + default: null + }, + variant: { + type: String, + default: null + }, + type: { + type: String, + default: 'button' + }, + tag: { + type: String, + default: 'button' + }, + pressed: { + // tri-state prop: true, false or null + // => on, off, not a toggle + type: Boolean, + default: null + } + }; + var linkProps$1 = propsFactory(); + delete linkProps$1.href.default; + delete linkProps$1.to.default; + var linkPropKeys = keys(linkProps$1); + var props$4 = _objectSpread({}, linkProps$1, btnProps); // Focus handler for toggle buttons. Needs class of 'focus' when focused. + + function handleFocus(evt) { + if (evt.type === 'focusin') { + addClass(evt.target, 'focus'); + } else if (evt.type === 'focusout') { + removeClass(evt.target, 'focus'); + } + } // Helper functons to minimize runtime memory footprint when lots of buttons on page + // Is the requested button a link? + + + function isLink(props) { + // If tag prop is set to `a`, we use a b-link to get proper disabled handling + return Boolean(props.href || props.to || props.tag && String(props.tag).toLowerCase() === 'a'); + } // Is the button to be a toggle button? + + + function isToggle(props) { + return typeof props.pressed === 'boolean'; + } // Is the button "really" a button? + + + function isButton(props) { + if (isLink(props)) { + return false; + } else if (props.tag && String(props.tag).toLowerCase() !== 'button') { + return false; + } + + return true; + } // Is the requested tag not a button or link? + + + function isNonStandardTag(props) { + return !isLink(props) && !isButton(props); + } // Compute required classes (non static classes) + + + function computeClass(props) { + var _ref; + + return [props.variant ? "btn-".concat(props.variant) : "btn-secondary", (_ref = {}, _defineProperty(_ref, "btn-".concat(props.size), Boolean(props.size)), _defineProperty(_ref, 'btn-block', props.block), _defineProperty(_ref, "disabled", props.disabled), _defineProperty(_ref, "active", props.pressed), _ref)]; + } // Compute the link props to pass to b-link (if required) + + + function computeLinkProps(props) { + return isLink(props) ? pluckProps(linkPropKeys, props) : null; + } // Compute the attributes for a button + + + function computeAttrs(props, data) { + var button = isButton(props); + var link = isLink(props); + var toggle = isToggle(props); + var nonStdTag = isNonStandardTag(props); + var role = data.attrs && data.attrs['role'] ? data.attrs['role'] : null; + var tabindex = data.attrs ? data.attrs['tabindex'] : null; + + if (nonStdTag) { + tabindex = '0'; + } + + return { + // Type only used for "real" buttons + type: button && !link ? props.type : null, + // Disabled only set on "real" buttons + disabled: button ? props.disabled : null, + // We add a role of button when the tag is not a link or button for ARIA. + // Don't bork any role provided in data.attrs when isLink or isButton + role: nonStdTag ? 'button' : role, + // We set the aria-disabled state for non-standard tags + 'aria-disabled': nonStdTag ? String(props.disabled) : null, + // For toggles, we need to set the pressed state for ARIA + 'aria-pressed': toggle ? String(props.pressed) : null, + // autocomplete off is needed in toggle mode to prevent some browsers from + // remembering the previous setting when using the back button. + autocomplete: toggle ? 'off' : null, + // Tab index is used when the component is not a button. + // Links are tabbable, but don't allow disabled, while non buttons or links + // are not tabbable, so we mimic that functionality by disabling tabbing + // when disabled, and adding a tabindex of '0' to non buttons or non links. + tabindex: props.disabled && !button ? '-1' : tabindex + }; + } // @vue/component + + + var BButton = { + name: 'BButton', + functional: true, + props: props$4, + render: function render(h, _ref2) { + var props = _ref2.props, + data = _ref2.data, + listeners = _ref2.listeners, + children = _ref2.children; + var toggle = isToggle(props); + var link = isLink(props); + var on = { + click: function click(e) { + if (props.disabled && e instanceof Event) { + e.stopPropagation(); + e.preventDefault(); + } else if (toggle && listeners && listeners['update:pressed']) { + // Send .sync updates to any "pressed" prop (if .sync listeners) + // Concat will normalize the value to an array + // without double wrapping an array value in an array. + concat(listeners['update:pressed']).forEach(function (fn) { + if (typeof fn === 'function') { + fn(!props.pressed); + } + }); + } + } + }; + + if (toggle) { + on.focusin = handleFocus; + on.focusout = handleFocus; + } + + var componentData = { + staticClass: 'btn', + class: computeClass(props), + props: computeLinkProps(props), + attrs: computeAttrs(props, data), + on: on + }; + return h(link ? BLink : props.tag, mergeData(data, componentData), children); + } + }; + + var components$3 = { + BButton: BButton, + BBtn: BButton, + BButtonClose: BButtonClose, + BBtnClose: BButtonClose + }; + var index$3 = { + install: function install(Vue) { + registerComponents(Vue, components$3); + } + }; + + var props$5 = { + vertical: { + type: Boolean, + default: false + }, + size: { + type: String, + default: null + }, + tag: { + type: String, + default: 'div' + }, + ariaRole: { + type: String, + default: 'group' + } // @vue/component + + }; + var BButtonGroup = { + name: 'BButtonGroup', + functional: true, + props: props$5, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + class: _defineProperty({ + 'btn-group': !props.vertical, + 'btn-group-vertical': props.vertical + }, "btn-group-".concat(props.size), Boolean(props.size)), + attrs: { + role: props.ariaRole + } + }), children); + } + }; + + var components$4 = { + BButtonGroup: BButtonGroup, + BBtnGroup: BButtonGroup + }; + var index$4 = { + install: function install(Vue) { + registerComponents(Vue, components$4); + } + }; + + /* + * Key Codes (events) + */ + var KeyCodes = { + SPACE: 32, + ENTER: 13, + ESC: 27, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + PAGEUP: 33, + PAGEDOWN: 34, + HOME: 36, + END: 35, + TAB: 9, + SHIFT: 16, + CTRL: 17, + BACKSPACE: 8, + ALT: 18, + PAUSE: 19, + BREAK: 19, + INSERT: 45, + INS: 45, + DELETE: 46 + }; + + var ITEM_SELECTOR = ['.btn:not(.disabled):not([disabled]):not(.dropdown-item)', '.form-control:not(.disabled):not([disabled])', 'select:not(.disabled):not([disabled])', 'input[type="checkbox"]:not(.disabled)', 'input[type="radio"]:not(.disabled)'].join(','); // @vue/component + + var BButtonToolbar = { + name: 'BButtonToolbar', + props: { + justify: { + type: Boolean, + default: false + }, + keyNav: { + type: Boolean, + default: false + } + }, + computed: { + classObject: function classObject() { + return ['btn-toolbar', this.justify && !this.vertical ? 'justify-content-between' : '']; + } + }, + mounted: function mounted() { + if (this.keyNav) { + // Pre-set the tabindexes if the markup does not include tabindex="-1" on the toolbar items + this.getItems(); + } + }, + methods: { + onFocusin: function onFocusin(evt) { + if (evt.target === this.$el) { + evt.preventDefault(); + evt.stopPropagation(); + this.focusFirst(evt); + } + }, + onKeydown: function onKeydown(evt) { + if (!this.keyNav) { + return; + } + + var key = evt.keyCode; + var shift = evt.shiftKey; + + if (key === KeyCodes.UP || key === KeyCodes.LEFT) { + evt.preventDefault(); + evt.stopPropagation(); + + if (shift) { + this.focusFirst(evt); + } else { + this.focusNext(evt, true); + } + } else if (key === KeyCodes.DOWN || key === KeyCodes.RIGHT) { + evt.preventDefault(); + evt.stopPropagation(); + + if (shift) { + this.focusLast(evt); + } else { + this.focusNext(evt, false); + } + } + }, + setItemFocus: function setItemFocus(item) { + this.$nextTick(function () { + item.focus(); + }); + }, + focusNext: function focusNext(evt, prev) { + var items = this.getItems(); + + if (items.length < 1) { + return; + } + + var index = items.indexOf(evt.target); + + if (prev && index > 0) { + index--; + } else if (!prev && index < items.length - 1) { + index++; + } + + if (index < 0) { + index = 0; + } + + this.setItemFocus(items[index]); + }, + focusFirst: function focusFirst(evt) { + var items = this.getItems(); + + if (items.length > 0) { + this.setItemFocus(items[0]); + } + }, + focusLast: function focusLast(evt) { + var items = this.getItems(); + + if (items.length > 0) { + this.setItemFocus([items.length - 1]); + } + }, + getItems: function getItems() { + var items = selectAll(ITEM_SELECTOR, this.$el); + items.forEach(function (item) { + // Ensure tabfocus is -1 on any new elements + item.tabIndex = -1; + }); + return items.filter(function (el) { + return isVisible(el); + }); + } + }, + render: function render(h) { + return h('div', { + class: this.classObject, + attrs: { + role: 'toolbar', + tabindex: this.keyNav ? '0' : null + }, + on: { + focusin: this.onFocusin, + keydown: this.onKeydown + } + }, [this.$slots.default]); + } + }; + + var components$5 = { + BButtonToolbar: BButtonToolbar, + BBtnToolbar: BButtonToolbar + }; + var index$5 = { + install: function install(Vue) { + registerComponents(Vue, components$5); + } + }; + + var props$6 = { + tag: { + type: String, + default: 'div' + } // @vue/component + + }; + var InputGroupText = { + name: 'BInputGroupText', + functional: true, + props: props$6, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'input-group-text' + }), children); + } + }; + + var commonProps = { + id: { + type: String, + default: null + }, + tag: { + type: String, + default: 'div' + }, + isText: { + type: Boolean, + default: false + } // @vue/component + + }; + var InputGroupAddon = { + name: 'BInputGroupAddon', + functional: true, + props: _objectSpread({}, commonProps, { + append: { + type: Boolean, + default: false + } + }), + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + class: { + 'input-group-append': props.append, + 'input-group-prepend': !props.append + }, + attrs: { + id: props.id + } + }), props.isText ? [h(InputGroupText, children)] : children); + } + }; + + var InputGroupPrepend = { + name: 'BInputGroupPrepend', + functional: true, + props: commonProps, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + // pass all our props/attrs down to child, and set`append` to false + return h(InputGroupAddon, mergeData(data, { + props: _objectSpread({}, props, { + append: false + }) + }), children); + } + }; + + var InputGroupAppend = { + name: 'BInputGroupAppend', + functional: true, + props: commonProps, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + // pass all our props/attrs down to child, and set`append` to true + return h(InputGroupAddon, mergeData(data, { + props: _objectSpread({}, props, { + append: true + }) + }), children); + } + }; + + var props$7 = { + id: { + type: String + }, + size: { + type: String + }, + prepend: { + type: String + }, + prependHTML: { + type: String + }, + append: { + type: String + }, + appendHTML: { + type: String + }, + tag: { + type: String, + default: 'div' + } // @vue/component + + }; + var BInputGroup = { + name: 'BInputGroup', + functional: true, + props: props$7, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + slots = _ref.slots; + var $slots = slots(); + var childNodes = []; // Prepend prop + + if (props.prepend) { + childNodes.push(h(InputGroupPrepend, [h(InputGroupText, { + domProps: htmlOrText(props.prependHTML, props.prepend) + })])); + } else { + childNodes.push(h(false)); + } // Prepend slot + + + if ($slots.prepend) { + childNodes.push(h(InputGroupPrepend, $slots.prepend)); + } else { + childNodes.push(h(false)); + } // Default slot + + + if ($slots.default) { + childNodes.push.apply(childNodes, _toConsumableArray($slots.default)); + } else { + childNodes.push(h(false)); + } // Append prop + + + if (props.append) { + childNodes.push(h(InputGroupAppend, [h(InputGroupText, { + domProps: htmlOrText(props.appendHTML, props.append) + })])); + } else { + childNodes.push(h(false)); + } // Append slot + + + if ($slots.append) { + childNodes.push(h(InputGroupAppend, $slots.append)); + } else { + childNodes.push(h(false)); + } + + return h(props.tag, mergeData(data, { + staticClass: 'input-group', + class: _defineProperty({}, "input-group-".concat(props.size), Boolean(props.size)), + attrs: { + id: props.id || null, + role: 'group' + } + }), childNodes); + } + }; + + var components$6 = { + BInputGroup: BInputGroup, + BInputGroupAddon: InputGroupAddon, + BInputGroupPrepend: InputGroupPrepend, + BInputGroupAppend: InputGroupAppend, + BInputGroupText: InputGroupText + }; + var index$6 = { + install: function install(Vue) { + registerComponents(Vue, components$6); + } + }; + + /** + * @param {string} str + */ + function upperFirst(str) { + if (typeof str !== 'string') { + str = String(str); + } + + return str.charAt(0).toUpperCase() + str.slice(1); + } + + /** + * @param {string} prefix + * @param {string} value + */ + + function prefixPropName(prefix, value) { + return prefix + upperFirst(value); + } + + /** + * @param {string} str + */ + function lowerFirst(str) { + str = String(str); + return str.charAt(0).toLowerCase() + str.slice(1); + } + + /** + * @param {string} prefix + * @param {string} value + */ + + function unPrefixPropName(prefix, value) { + return lowerFirst(value.replace(prefix, '')); + } + + /** + * @param {[]|{}} props + * @param {Function} transformFn + */ + + function copyProps(props) { + var transformFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : identity; + + if (isArray(props)) { + return props.map(transformFn); + } // Props as an object. + + + var copied = {}; + + for (var prop in props) { + if (props.hasOwnProperty(prop)) { + if (_typeof(prop) === 'object') { + copied[transformFn(prop)] = _objectSpread({}, props[prop]); + } else { + copied[transformFn(prop)] = props[prop]; + } + } + } + + return copied; + } + + // @vue/component + var cardMixin = { + props: { + tag: { + type: String, + default: 'div' + }, + bgVariant: { + type: String, + default: null + }, + borderVariant: { + type: String, + default: null + }, + textVariant: { + type: String, + default: null + } + } + }; + + var props$8 = { + title: { + type: String, + default: '' + }, + titleTag: { + type: String, + default: 'h4' + } // @vue/component + + }; + var BCardTitle = { + name: 'BCardTitle', + functional: true, + props: props$8, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.titleTag, mergeData(data, { + staticClass: 'card-title' + }), children || props.title); + } + }; + + var props$9 = { + subTitle: { + type: String, + default: '' + }, + subTitleTag: { + type: String, + default: 'h6' + }, + subTitleTextVariant: { + type: String, + default: 'muted' + } // @vue/component + + }; + var BCardSubTitle = { + name: 'BCardSubTitle', + functional: true, + props: props$9, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.subTitleTag, mergeData(data, { + staticClass: 'card-subtitle', + class: [props.subTitleTextVariant ? "text-".concat(props.subTitleTextVariant) : null] + }), children || props.subTitle); + } + }; + + var props$a = _objectSpread({}, copyProps(cardMixin.props, prefixPropName.bind(null, 'body')), { + bodyClass: { + type: [String, Object, Array], + default: null + } + }, props$8, props$9, { + overlay: { + type: Boolean, + default: false + } // @vue/component + + }); + var BCardBody = { + name: 'BCardBody', + functional: true, + props: props$a, + render: function render(h, _ref) { + var _ref2; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var cardTitle = h(false); + var cardSubTitle = h(false); + var cardContent = children || [h(false)]; + + if (props.title) { + cardTitle = h(BCardTitle, { + props: pluckProps(props$8, props) + }); + } + + if (props.subTitle) { + cardSubTitle = h(BCardSubTitle, { + props: pluckProps(props$9, props), + class: ['mb-2'] + }); + } + + return h(props.bodyTag, mergeData(data, { + staticClass: 'card-body', + class: [(_ref2 = { + 'card-img-overlay': props.overlay + }, _defineProperty(_ref2, "bg-".concat(props.bodyBgVariant), Boolean(props.bodyBgVariant)), _defineProperty(_ref2, "border-".concat(props.bodyBorderVariant), Boolean(props.bodyBorderVariant)), _defineProperty(_ref2, "text-".concat(props.bodyTextVariant), Boolean(props.bodyTextVariant)), _ref2), props.bodyClass || {}] + }), [cardTitle, cardSubTitle].concat(_toConsumableArray(cardContent))); + } + }; + + var props$b = _objectSpread({}, copyProps(cardMixin.props, prefixPropName.bind(null, 'header')), { + header: { + type: String, + default: null + }, + headerHtml: { + type: String, + default: null + }, + headerClass: { + type: [String, Object, Array], + default: null + } // @vue/component + + }); + var BCardHeader = { + name: 'BCardHeader', + functional: true, + props: props$b, + render: function render(h, _ref) { + var _ref2; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.headerTag, mergeData(data, { + staticClass: 'card-header', + class: [props.headerClass, (_ref2 = {}, _defineProperty(_ref2, "bg-".concat(props.headerBgVariant), Boolean(props.headerBgVariant)), _defineProperty(_ref2, "border-".concat(props.headerBorderVariant), Boolean(props.headerBorderVariant)), _defineProperty(_ref2, "text-".concat(props.headerTextVariant), Boolean(props.headerTextVariant)), _ref2)] + }), children || [h('div', { + domProps: htmlOrText(props.headerHtml, props.header) + })]); + } + }; + + var props$c = _objectSpread({}, copyProps(cardMixin.props, prefixPropName.bind(null, 'footer')), { + footer: { + type: String, + default: null + }, + footerHtml: { + type: String, + default: null + }, + footerClass: { + type: [String, Object, Array], + default: null + } // @vue/component + + }); + var BCardFooter = { + name: 'BCardFooter', + functional: true, + props: props$c, + render: function render(h, _ref) { + var _ref2; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.footerTag, mergeData(data, { + staticClass: 'card-footer', + class: [props.footerClass, (_ref2 = {}, _defineProperty(_ref2, "bg-".concat(props.footerBgVariant), Boolean(props.footerBgVariant)), _defineProperty(_ref2, "border-".concat(props.footerBorderVariant), Boolean(props.footerBorderVariant)), _defineProperty(_ref2, "text-".concat(props.footerTextVariant), Boolean(props.footerTextVariant)), _ref2)] + }), children || [h('div', { + domProps: htmlOrText(props.footerHtml, props.footer) + })]); + } + }; + + var props$d = { + src: { + type: String, + default: null, + required: true + }, + alt: { + type: String, + default: null + }, + top: { + type: Boolean, + default: false + }, + bottom: { + type: Boolean, + default: false + }, + left: { + type: Boolean, + default: false + }, + start: { + type: Boolean, + default: false // alias of 'left' + + }, + right: { + type: Boolean, + default: false + }, + end: { + type: Boolean, + default: false // alias of 'right' + + }, + height: { + type: String, + default: null + }, + width: { + type: String, + default: null + } // @vue/component + + }; + var BCardImg = { + name: 'BCardImg', + functional: true, + props: props$d, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data; + var baseClass = 'card-img'; + + if (props.top) { + baseClass += '-top'; + } else if (props.right || props.end) { + baseClass += '-right'; + } else if (props.bottom) { + baseClass += '-bottom'; + } else if (props.left || props.start) { + baseClass += '-left'; + } + + return h('img', mergeData(data, { + class: [baseClass], + attrs: { + src: props.src, + alt: props.alt, + height: props.height, + width: props.width + } + })); + } + }; + + var cardImgProps = copyProps(props$d, prefixPropName.bind(null, 'img')); + cardImgProps.imgSrc.required = false; + var props$e = _objectSpread({}, props$a, props$b, props$c, cardImgProps, copyProps(cardMixin.props), { + align: { + type: String, + default: null + }, + noBody: { + type: Boolean, + default: false + } // @vue/component + + }); + var BCard = { + name: 'BCard', + functional: true, + props: props$e, + render: function render(h, _ref) { + var _class; + + var props = _ref.props, + data = _ref.data, + slots = _ref.slots; + var $slots = slots(); // Create placeholder elements for each section + + var imgFirst = h(false); + var header = h(false); + var content = h(false); + var footer = h(false); + var imgLast = h(false); + + if (props.imgSrc) { + var img = h(BCardImg, { + props: pluckProps(cardImgProps, props, unPrefixPropName.bind(null, 'img')) + }); + + if (props.imgBottom) { + imgLast = img; + } else { + imgFirst = img; + } + } + + if (props.header || $slots.header) { + header = h(BCardHeader, { + props: pluckProps(props$b, props) + }, $slots.header); + } + + if (props.noBody) { + content = $slots.default || []; + } else { + // Wrap content in card-body + content = [h(BCardBody, { + props: pluckProps(props$a, props) + }, $slots.default)]; + } + + if (props.footer || $slots.footer) { + footer = h(BCardFooter, { + props: pluckProps(props$c, props) + }, $slots.footer); + } + + return h(props.tag, mergeData(data, { + staticClass: 'card', + class: (_class = { + 'flex-row': props.imgLeft || props.imgStart, + 'flex-row-reverse': (props.imgRight || props.imgEnd) && !(props.imgLeft || props.imgStart) + }, _defineProperty(_class, "text-".concat(props.align), Boolean(props.align)), _defineProperty(_class, "bg-".concat(props.bgVariant), Boolean(props.bgVariant)), _defineProperty(_class, "border-".concat(props.borderVariant), Boolean(props.borderVariant)), _defineProperty(_class, "text-".concat(props.textVariant), Boolean(props.textVariant)), _class) + }), [imgFirst, header].concat(_toConsumableArray(content), [footer, imgLast])); + } + }; + + var BLANK_TEMPLATE = '' + '' + ''; + + function makeBlankImgSrc(width, height, color) { + var src = encodeURIComponent(BLANK_TEMPLATE.replace('%{w}', String(width)).replace('%{h}', String(height)).replace('%{f}', color)); + return "data:image/svg+xml;charset=UTF-8,".concat(src); + } + + var props$f = { + src: { + type: String, + default: null + }, + alt: { + type: String, + default: null + }, + width: { + type: [Number, String], + default: null + }, + height: { + type: [Number, String], + default: null + }, + block: { + type: Boolean, + default: false + }, + fluid: { + type: Boolean, + default: false + }, + fluidGrow: { + // Gives fluid images class `w-100` to make them grow to fit container + type: Boolean, + default: false + }, + rounded: { + // rounded can be: + // false: no rounding of corners + // true: slightly rounded corners + // 'top': top corners rounded + // 'right': right corners rounded + // 'bottom': bottom corners rounded + // 'left': left corners rounded + // 'circle': circle/oval + // '0': force rounding off + type: [Boolean, String], + default: false + }, + thumbnail: { + type: Boolean, + default: false + }, + left: { + type: Boolean, + default: false + }, + right: { + type: Boolean, + default: false + }, + center: { + type: Boolean, + default: false + }, + blank: { + type: Boolean, + default: false + }, + blankColor: { + type: String, + default: 'transparent' + } // @vue/component + + }; + var BImg = { + name: 'BImg', + functional: true, + props: props$f, + render: function render(h, _ref) { + var _class; + + var props = _ref.props, + data = _ref.data; + var src = props.src; + var width = parseInt(props.width, 10) ? parseInt(props.width, 10) : null; + var height = parseInt(props.height, 10) ? parseInt(props.height, 10) : null; + var align = null; + var block = props.block; + + if (props.blank) { + if (!height && Boolean(width)) { + height = width; + } else if (!width && Boolean(height)) { + width = height; + } + + if (!width && !height) { + width = 1; + height = 1; + } // Make a blank SVG image + + + src = makeBlankImgSrc(width, height, props.blankColor || 'transparent'); + } + + if (props.left) { + align = 'float-left'; + } else if (props.right) { + align = 'float-right'; + } else if (props.center) { + align = 'mx-auto'; + block = true; + } + + return h('img', mergeData(data, { + attrs: { + src: src, + alt: props.alt, + width: width ? String(width) : null, + height: height ? String(height) : null + }, + class: (_class = { + 'img-thumbnail': props.thumbnail, + 'img-fluid': props.fluid || props.fluidGrow, + 'w-100': props.fluidGrow, + rounded: props.rounded === '' || props.rounded === true + }, _defineProperty(_class, "rounded-".concat(props.rounded), typeof props.rounded === 'string' && props.rounded !== ''), _defineProperty(_class, align, Boolean(align)), _defineProperty(_class, 'd-block', block), _class) + })); + } + }; + + var THROTTLE = 100; + var EventOptions = { + passive: true, + capture: false // @vue/component + + }; + var BImgLazy = { + name: 'BImgLazy', + components: { + BImg: BImg + }, + props: { + src: { + type: String, + default: null, + required: true + }, + alt: { + type: String, + default: null + }, + width: { + type: [Number, String], + default: null + }, + height: { + type: [Number, String], + default: null + }, + blankSrc: { + // If null, a blank image is generated + type: String, + default: null + }, + blankColor: { + type: String, + default: 'transparent' + }, + blankWidth: { + type: [Number, String], + default: null + }, + blankHeight: { + type: [Number, String], + default: null + }, + show: { + type: Boolean, + default: false + }, + fluid: { + type: Boolean, + default: false + }, + fluidGrow: { + type: Boolean, + default: false + }, + block: { + type: Boolean, + default: false + }, + thumbnail: { + type: Boolean, + default: false + }, + rounded: { + type: [Boolean, String], + default: false + }, + left: { + type: Boolean, + default: false + }, + right: { + type: Boolean, + default: false + }, + center: { + type: Boolean, + default: false + }, + offset: { + type: [Number, String], + default: 360 + }, + throttle: { + type: [Number, String], + default: THROTTLE + } + }, + data: function data() { + return { + isShown: false, + scrollTimeout: null + }; + }, + computed: { + computedSrc: function computedSrc() { + return !this.blankSrc || this.isShown ? this.src : this.blankSrc; + }, + computedBlank: function computedBlank() { + return !(this.isShown || this.blankSrc); + }, + computedWidth: function computedWidth() { + return this.isShown ? this.width : this.blankWidth || this.width; + }, + computedHeight: function computedHeight() { + return this.isShown ? this.height : this.blankHeight || this.height; + } + }, + watch: { + show: function show(newVal, oldVal) { + if (newVal !== oldVal) { + this.isShown = newVal; + + if (!newVal) { + // Make sure listeners are re-enabled if img is force set to blank + this.setListeners(true); + } + } + }, + isShown: function isShown(newVal, oldVal) { + if (newVal !== oldVal) { + // Update synched show prop + this.$emit('update:show', newVal); + } + } + }, + created: function created() { + this.isShown = this.show; + }, + mounted: function mounted() { + if (this.isShown) { + this.setListeners(false); + } else { + this.setListeners(true); + this.$nextTick(this.checkView); + } + }, + activated: function activated() { + /* istanbul ignore if */ + if (!this.isShown) { + this.setListeners(true); + this.$nextTick(this.checkView); + } + }, + deactivated: function deactivated() { + /* istanbul ignore next */ + this.setListeners(false); + }, + beforeDestroy: function beforeDestroy() { + /* istanbul ignore next */ + this.setListeners(false); + }, + methods: { + setListeners: function setListeners(on) { + clearTimeout(this.scrollTimer); + this.scrollTimeout = null; + var root = window; + + if (on) { + eventOn(this.$el, 'load', this.checkView); + eventOn(root, 'scroll', this.onScroll, EventOptions); + eventOn(root, 'resize', this.onScroll, EventOptions); + eventOn(root, 'orientationchange', this.onScroll, EventOptions); + eventOn(document, 'transitionend', this.onScroll, EventOptions); + } else { + eventOff(this.$el, 'load', this.checkView); + eventOff(root, 'scroll', this.onScroll, EventOptions); + eventOff(root, 'resize', this.onScroll, EventOptions); + eventOff(root, 'orientationchange', this.onScroll, EventOptions); + eventOff(document, 'transitionend', this.onScroll, EventOptions); + } + }, + checkView: function checkView() + /* istanbul ignore next: can't test getBoundingClientRect in JSDOM */ + { + // check bounding box + offset to see if we should show + if (this.isShown) { + this.setListeners(false); + return; + } + + var offset = parseInt(this.offset, 10) || 0; + var docElement = document.documentElement; + var view = { + l: 0 - offset, + t: 0 - offset, + b: docElement.clientHeight + offset, + r: docElement.clientWidth + offset + /* istanbul ignore next */ + + }; + var box = getBCR(this.$el); + /* istanbul ignore if */ + + if (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b) { + // image is in view (or about to be in view) + this.isShown = true; + this.setListeners(false); + } + }, + onScroll: function onScroll() { + if (this.isShown) { + this.setListeners(false); + } else { + clearTimeout(this.scrollTimeout); + this.scrollTimeout = setTimeout(this.checkView, parseInt(this.throttle, 10) || THROTTLE); + } + } + }, + render: function render(h) { + return h('b-img', { + props: { + src: this.computedSrc, + alt: this.alt, + blank: this.computedBlank, + blankColor: this.blankColor, + width: this.computedWidth, + height: this.computedHeight, + fluid: this.fluid, + fluidGrow: this.fluidGrow, + block: this.block, + thumbnail: this.thumbnail, + rounded: this.rounded, + left: this.left, + right: this.right, + center: this.center + } + }); + } + }; + + // The `omit()` util creates a new object, so we can just pass the original props + + var lazyProps = omit(BImgLazy.props, ['left', 'right', 'center', 'block', 'rounded', 'thumbnail', 'fluid', 'fluidGrow']); + var props$g = _objectSpread({}, lazyProps, { + top: { + type: Boolean, + default: false + }, + bottom: { + type: Boolean, + default: false + }, + left: { + type: Boolean, + default: false + }, + start: { + type: Boolean, + default: false // alias of 'left' + + }, + right: { + type: Boolean, + default: false + }, + end: { + type: Boolean, + default: false // alias of 'right' + + } // @vue/component + + }); + var BCardImgLazy = { + name: 'BCardImgLazy', + functional: true, + props: props$g, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data; + var baseClass = 'card-img'; + + if (props.top) { + baseClass += '-top'; + } else if (props.right || props.end) { + baseClass += '-right'; + } else if (props.bottom) { + baseClass += '-bottom'; + } else if (props.left || props.start) { + baseClass += '-left'; + } // False out the left/center/right props before passing to b-img-lazy + + + var lazyProps = _objectSpread({}, props, { + left: false, + right: false, + center: false + }); + + return h(BImgLazy, mergeData(data, { + class: [baseClass], + props: lazyProps + })); + } + }; + + var props$h = { + textTag: { + type: String, + default: 'p' + } // @vue/component + + }; + var BCardText = { + name: 'BCardText', + functional: true, + props: props$h, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.textTag, mergeData(data, { + staticClass: 'card-text' + }), children); + } + }; + + var props$i = { + tag: { + type: String, + default: 'div' + }, + deck: { + type: Boolean, + default: false + }, + columns: { + type: Boolean, + default: false + } // @vue/component + + }; + var BCardGroup = { + name: 'BCardGroup', + functional: true, + props: props$i, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var baseClass = 'card-group'; + + if (props.deck) { + baseClass = 'card-deck'; + } else if (props.columns) { + baseClass = 'card-columns'; + } + + return h(props.tag, mergeData(data, { + class: baseClass + }), children); + } + }; + + var components$7 = { + BCard: BCard, + BCardHeader: BCardHeader, + BCardBody: BCardBody, + BCardTitle: BCardTitle, + BCardSubTitle: BCardSubTitle, + BCardFooter: BCardFooter, + BCardImg: BCardImg, + BCardImgLazy: BCardImgLazy, + BCardText: BCardText, + BCardGroup: BCardGroup + }; + var index$7 = { + install: function install(Vue) { + registerComponents(Vue, components$7); + } + }; + + // Emulate observer disconnect() method so that we can detach the events later + + function fakeObserverFactory(el, callback) + /* istanbul ignore next: hard to test in JSDOM */ + { + eventOn(el, 'DOMNodeInserted', callback, false); + eventOn(el, 'DOMNodeRemoved', callback, false); + return { + disconnect: function disconnect() { + eventOff(el, 'DOMNodeInserted', callback, false); + eventOff(el, 'DOMNodeRemoved', callback, false); + } + }; + } + /** + * Observe a DOM element changes, falls back to eventListener mode + * @param {Element} el The DOM element to observe + * @param {Function} callback callback to be called on change + * @param {object} [opts={childList: true, subtree: true}] observe options + * @see http://stackoverflow.com/questions/3219758 + */ + + + function observeDOM(el, callback, opts) + /* istanbul ignore next: difficult to test in JSDOM */ + { + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var eventListenerSupported = window.addEventListener; // Handle case where we might be passed a vue instance + + el = el ? el.$el || el : null; + /* istanbul ignore next: dificult to test in JSDOM */ + + if (!isElement(el)) { + // We can't observe somthing that isn't an element + return null; + } + + var obs = null; + + if (MutationObserver) { + // Define a new observer + obs = new MutationObserver(function (mutations) { + var changed = false; // A Mutation can contain several change records, so we loop through them to see what has changed. + // We break out of the loop early if any "significant" change has been detected + + for (var i = 0; i < mutations.length && !changed; i++) { + // The muttion record + var mutation = mutations[i]; // Mutation Type + + var type = mutation.type; // DOM Node (could be any DOM Node type - HTMLElement, Text, comment, etc) + + var target = mutation.target; + + if (type === 'characterData' && target.nodeType === Node.TEXT_NODE) { + // We ignore nodes that are not TEXT (i.e. comments, etc) as they don't change layout + changed = true; + } else if (type === 'attributes') { + changed = true; + } else if (type === 'childList' && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) { + // This includes HTMLElement and Text Nodes being added/removed/re-arranged + changed = true; + } + } + + if (changed) { + // We only call the callback if a change that could affect layout/size truely happened. + callback(); + } + }); // Have the observer observe foo for changes in children, etc + + obs.observe(el, _objectSpread({ + childList: true, + subtree: true + }, opts)); + } else if (eventListenerSupported) { + // Legacy interface. most likely not used in modern browsers + obs = fakeObserverFactory(el, callback); + } // We return a reference to the observer so that obs.disconnect() can be called if necessary + // To reduce overhead when the root element is hiiden + + + return obs; + } + + /* + * SSR Safe Client Side ID attribute generation + * id's can only be generated client side, after mount. + * this._uid is not synched between server and client. + */ + // @vue/component + var idMixin = { + props: { + id: { + type: String, + default: null + } + }, + data: function data() { + return { + localId_: null + }; + }, + computed: { + safeId: function safeId() { + // Computed property that returns a dynamic function for creating the ID. + // Reacts to changes in both .id and .localId_ And regens a new function + var id = this.id || this.localId_; // We return a function that accepts an optional suffix string + // So this computed prop looks and works like a method!!! + + var fn = function fn(suffix) { + if (!id) { + return null; + } + + suffix = String(suffix || '').replace(/\s+/g, '_'); + return suffix ? id + '_' + suffix : id; + }; + + return fn; + } + }, + mounted: function mounted() { + var _this = this; + + // mounted only occurs client side + this.$nextTick(function () { + // Update dom with auto ID after dom loaded to prevent + // SSR hydration errors. + _this.localId_ = "__BVID__".concat(_this._uid); + }); + } + }; + + var DIRECTION = { + next: { + dirClass: 'carousel-item-left', + overlayClass: 'carousel-item-next' + }, + prev: { + dirClass: 'carousel-item-right', + overlayClass: 'carousel-item-prev' + } // Fallback Transition duration (with a little buffer) in ms + + }; + var TRANS_DURATION = 600 + 50; // Time for mouse compat events to fire after touch + + var TOUCHEVENT_COMPAT_WAIT = 500; // Number of pixels to consider touch move a swipe + + var SWIPE_THRESHOLD = 40; // PointerEvent pointer types + + var PointerType = { + TOUCH: 'touch', + PEN: 'pen' // Transition Event names + + }; + var TransitionEndEvents = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + OTransition: 'otransitionend oTransitionEnd', + transition: 'transitionend' + }; + var EventOptions$1 = { + passive: true, + capture: false // Return the browser specific transitionEnd event name + + }; + + function getTransisionEndEvent(el) { + for (var name in TransitionEndEvents) { + if (el.style[name] !== undefined) { + /* istanbul ignore next: JSDOM doesn't support transition events */ + return TransitionEndEvents[name]; + } + } // fallback + + + return null; + } // @vue/component + + + var BCarousel = { + name: 'BCarousel', + mixins: [idMixin], + provide: function provide() { + return { + bvCarousel: this + }; + }, + props: { + labelPrev: { + type: String, + default: 'Previous Slide' + }, + labelNext: { + type: String, + default: 'Next Slide' + }, + labelGotoSlide: { + type: String, + default: 'Goto Slide' + }, + labelIndicators: { + type: String, + default: 'Select a slide to display' + }, + interval: { + type: Number, + default: 5000 + }, + indicators: { + type: Boolean, + default: false + }, + controls: { + type: Boolean, + default: false + }, + noAnimation: { + // Disable slide/fade animation + type: Boolean, + default: false + }, + fade: { + // Enable cross-fade animation instead of slide animation + type: Boolean, + default: false + }, + noTouch: { + // Sniffed by carousel-slide + type: Boolean, + default: false + }, + imgWidth: { + // Sniffed by carousel-slide + type: [Number, String] // default: undefined + + }, + imgHeight: { + // Sniffed by carousel-slide + type: [Number, String] // default: undefined + + }, + background: { + type: String // default: undefined + + }, + value: { + type: Number, + default: 0 + } + }, + data: function data() { + return { + index: this.value || 0, + isSliding: false, + transitionEndEvent: null, + slides: [], + direction: null, + isPaused: false, + // Touch event handling values + touchStartX: 0, + touchDeltaX: 0 + }; + }, + watch: { + value: function value(newVal, oldVal) { + if (newVal !== oldVal) { + this.setSlide(newVal); + } + }, + interval: function interval(newVal, oldVal) { + if (newVal === oldVal) { + return; + } + + if (!newVal) { + // Pausing slide show + this.pause(false); + } else { + // Restarting or Changing interval + this.pause(true); + this.start(false); + } + }, + isPaused: function isPaused(newVal, oldVal) { + if (newVal !== oldVal) { + this.$emit(newVal ? 'paused' : 'unpaused'); + } + }, + index: function index(to, from) { + if (to === from || this.isSliding) { + return; + } + + this.doSlide(to, from); + } + }, + created: function created() { + // Create private non-reactive props + this._intervalId = null; + this._animationTimeout = null; + this._touchTimeout = null; + }, + mounted: function mounted() { + // Cache current browser transitionend event name + this.transitionEndEvent = getTransisionEndEvent(this.$el) || null; // Get all slides + + this.updateSlides(); // Observe child changes so we can update slide list + + observeDOM(this.$refs.inner, this.updateSlides.bind(this), { + subtree: false, + childList: true, + attributes: true, + attributeFilter: ['id'] + }); + }, + beforeDestroy: function beforeDestroy() + /* istanbul ignore next: dificult to test */ + { + clearTimeout(this._animationTimeout); + clearTimeout(this._touchTimeout); + clearInterval(this._intervalId); + this._intervalId = null; + this._animationTimeout = null; + this._touchTimeout = null; + }, + methods: { + // Set slide + setSlide: function setSlide(slide) { + var _this = this; + + var direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + // Don't animate when page is not visible + + /* istanbul ignore if: dificult to test */ + if (inBrowser && document.visibilityState && document.hidden) { + return; + } + + var len = this.slides.length; // Don't do anything if nothing to slide to + + if (len === 0) { + return; + } // Don't change slide while transitioning, wait until transition is done + + + if (this.isSliding) { + // Schedule slide after sliding complete + this.$once('sliding-end', function () { + return _this.setSlide(slide, direction); + }); + return; + } + + this.direction = direction; // Make sure we have an integer (you never know!) + + slide = Math.floor(slide); // Set new slide index. Wrap around if necessary + + this.index = slide >= len ? 0 : slide >= 0 ? slide : len - 1; + }, + // Previous slide + prev: function prev() { + this.setSlide(this.index - 1, 'prev'); + }, + // Next slide + next: function next() { + this.setSlide(this.index + 1, 'next'); + }, + // Pause auto rotation + pause: function pause(evt) { + if (!evt) { + this.isPaused = true; + } + + if (this._intervalId) { + clearInterval(this._intervalId); + this._intervalId = null; + } + }, + // Start auto rotate slides + start: function start(evt) { + if (!evt) { + this.isPaused = false; + } + + if (this._intervalId) { + clearInterval(this._intervalId); + this._intervalId = null; + } // Don't start if no interval, or less than 2 slides + + + if (this.interval && this.slides.length > 1) { + this._intervalId = setInterval(this.next, Math.max(1000, this.interval)); + } + }, + // Re-Start auto rotate slides when focus/hover leaves the carousel + restart: function restart(evt) { + /* istanbul ignore if: dificult to test */ + if (!this.$el.contains(document.activeElement)) { + this.start(); + } + }, + doSlide: function doSlide(to, from) { + var _this2 = this; + + var isCycling = Boolean(this.interval); // Determine sliding direction + + var direction = this.calcDirection(this.direction, from, to); + var overlayClass = direction.overlayClass; + var dirClass = direction.dirClass; // Determine current and next slides + + var currentSlide = this.slides[from]; + var nextSlide = this.slides[to]; // Don't do anything if there aren't any slides to slide to + + if (!currentSlide || !nextSlide) { + return; + } // Start animating + + + this.isSliding = true; + + if (isCycling) { + this.pause(false); + } + + this.$emit('sliding-start', to); // Update v-model + + this.$emit('input', this.index); + + if (this.noAnimation) { + addClass(nextSlide, 'active'); + removeClass(currentSlide, 'active'); + this.isSliding = false; // Notify ourselves that we're done sliding (slid) + + this.$nextTick(function () { + return _this2.$emit('sliding-end', to); + }); + } else { + addClass(nextSlide, overlayClass); // Trigger a reflow of next slide + + reflow(nextSlide); + addClass(currentSlide, dirClass); + addClass(nextSlide, dirClass); // Transition End handler + + var called = false; + /* istanbul ignore next: dificult to test */ + + var onceTransEnd = function onceTransEnd(evt) { + if (called) { + return; + } + + called = true; + /* istanbul ignore if: transition events cant be tested in JSDOM */ + + if (_this2.transitionEndEvent) { + var events = _this2.transitionEndEvent.split(/\s+/); + + events.forEach(function (evt) { + return eventOff(currentSlide, evt, onceTransEnd, EventOptions$1); + }); + } + + _this2._animationTimeout = null; + removeClass(nextSlide, dirClass); + removeClass(nextSlide, overlayClass); + addClass(nextSlide, 'active'); + removeClass(currentSlide, 'active'); + removeClass(currentSlide, dirClass); + removeClass(currentSlide, overlayClass); + setAttr(currentSlide, 'aria-current', 'false'); + setAttr(nextSlide, 'aria-current', 'true'); + setAttr(currentSlide, 'aria-hidden', 'true'); + setAttr(nextSlide, 'aria-hidden', 'false'); + _this2.isSliding = false; + _this2.direction = null; // Notify ourselves that we're done sliding (slid) + + _this2.$nextTick(function () { + return _this2.$emit('sliding-end', to); + }); + }; // Set up transitionend handler + + /* istanbul ignore if: transition events cant be tested in JSDOM */ + + + if (this.transitionEndEvent) { + var events = this.transitionEndEvent.split(/\s+/); + events.forEach(function (event) { + return eventOn(currentSlide, event, onceTransEnd, EventOptions$1); + }); + } // Fallback to setTimeout + + + this._animationTimeout = setTimeout(onceTransEnd, TRANS_DURATION); + } + + if (isCycling) { + this.start(false); + } + }, + // Update slide list + updateSlides: function updateSlides() { + this.pause(true); // Get all slides as DOM elements + + this.slides = selectAll('.carousel-item', this.$refs.inner); + var numSlides = this.slides.length; // Keep slide number in range + + var index = Math.max(0, Math.min(Math.floor(this.index), numSlides - 1)); + this.slides.forEach(function (slide, idx) { + var n = idx + 1; + + if (idx === index) { + addClass(slide, 'active'); + setAttr(slide, 'aria-current', 'true'); + } else { + removeClass(slide, 'active'); + setAttr(slide, 'aria-current', 'false'); + } + + setAttr(slide, 'aria-posinset', String(n)); + setAttr(slide, 'aria-setsize', String(numSlides)); + }); // Set slide as active + + this.setSlide(index); + this.start(this.isPaused); + }, + calcDirection: function calcDirection() { + var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + var curIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var nextIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + + if (!direction) { + return nextIndex > curIndex ? DIRECTION.next : DIRECTION.prev; + } + + return DIRECTION[direction]; + }, + handleClick: function handleClick(evt, fn) { + var keyCode = evt.keyCode; + + if (evt.type === 'click' || keyCode === KeyCodes.SPACE || keyCode === KeyCodes.ENTER) { + evt.preventDefault(); + evt.stopPropagation(); + fn(); + } + }, + handleSwipe: function handleSwipe() + /* istanbul ignore next: JSDOM doesn't support touch events */ + { + var absDeltax = Math.abs(this.touchDeltaX); + + if (absDeltax <= SWIPE_THRESHOLD) { + return; + } + + var direction = absDeltax / this.touchDeltaX; + + if (direction > 0) { + // swipe left + this.prev(); + } else if (direction < 0) { + // swipe right + this.next(); + } + }, + touchStart: function touchStart(evt) + /* istanbul ignore next: JSDOM doesn't support touch events */ + { + if (hasPointerEvent && PointerType[evt.pointerType.toUpperCase()]) { + this.touchStartX = evt.clientX; + } else if (!hasPointerEvent) { + this.touchStartX = evt.touches[0].clientX; + } + }, + touchMove: function touchMove(evt) + /* istanbul ignore next: JSDOM doesn't support touch events */ + { + // ensure swiping with one touch and not pinching + if (evt.touches && evt.originalEvent.touches.length > 1) { + this.touchDeltaX = 0; + } else { + this.touchDeltaX = evt.touches[0].clientX - this.touchStartX; + } + }, + touchEnd: function touchEnd(evt) + /* istanbul ignore next: JSDOM doesn't support touch events */ + { + if (hasPointerEvent && PointerType[evt.pointerType.toUpperCase()]) { + this.touchDeltaX = evt.clientX - this.touchStartX; + } + + this.handleSwipe(); // 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.pause(false); + + if (this._touchTimeout) { + clearTimeout(this._touchTimeout); + } + + this._touchTimeout = setTimeout(this.start, TOUCHEVENT_COMPAT_WAIT + Math.max(1000, this.interval)); + } + }, + render: function render(h) { + var _this3 = this; + + // Wrapper for slides + var inner = h('div', { + ref: 'inner', + class: ['carousel-inner'], + attrs: { + id: this.safeId('__BV_inner_'), + role: 'list' + } + }, [this.$slots.default]); // Prev and Next Controls + + var controls = h(false); + + if (this.controls) { + controls = [h('a', { + class: ['carousel-control-prev'], + attrs: { + href: '#', + role: 'button', + 'aria-controls': this.safeId('__BV_inner_') + }, + on: { + click: function click(evt) { + _this3.handleClick(evt, _this3.prev); + }, + keydown: function keydown(evt) { + _this3.handleClick(evt, _this3.prev); + } + } + }, [h('span', { + class: ['carousel-control-prev-icon'], + attrs: { + 'aria-hidden': 'true' + } + }), h('span', { + class: ['sr-only'] + }, [this.labelPrev])]), h('a', { + class: ['carousel-control-next'], + attrs: { + href: '#', + role: 'button', + 'aria-controls': this.safeId('__BV_inner_') + }, + on: { + click: function click(evt) { + _this3.handleClick(evt, _this3.next); + }, + keydown: function keydown(evt) { + _this3.handleClick(evt, _this3.next); + } + } + }, [h('span', { + class: ['carousel-control-next-icon'], + attrs: { + 'aria-hidden': 'true' + } + }), h('span', { + class: ['sr-only'] + }, [this.labelNext])])]; + } // Indicators + + + var indicators = h('ol', { + class: ['carousel-indicators'], + directives: [{ + name: 'show', + rawName: 'v-show', + value: this.indicators, + expression: 'indicators' + }], + attrs: { + id: this.safeId('__BV_indicators_'), + 'aria-hidden': this.indicators ? 'false' : 'true', + 'aria-label': this.labelIndicators, + 'aria-owns': this.safeId('__BV_inner_') + } + }, this.slides.map(function (slide, n) { + return h('li', { + key: "slide_".concat(n), + class: { + active: n === _this3.index + }, + attrs: { + role: 'button', + id: _this3.safeId("__BV_indicator_".concat(n + 1, "_")), + tabindex: _this3.indicators ? '0' : '-1', + 'aria-current': n === _this3.index ? 'true' : 'false', + 'aria-label': "".concat(_this3.labelGotoSlide, " ").concat(n + 1), + 'aria-describedby': _this3.slides[n].id || null, + 'aria-controls': _this3.safeId('__BV_inner_') + }, + on: { + click: function click(evt) { + _this3.handleClick(evt, function () { + _this3.setSlide(n); + }); + }, + keydown: function keydown(evt) { + _this3.handleClick(evt, function () { + _this3.setSlide(n); + }); + } + } + }); + })); + var on = { + mouseenter: this.pause, + mouseleave: this.restart, + focusin: this.pause, + focusout: this.restart, + keydown: function keydown(evt) { + if (/input|textarea/i.test(evt.target.tagName)) { + return; + } + + var keyCode = evt.keyCode; + + if (keyCode === KeyCodes.LEFT || keyCode === KeyCodes.RIGHT) { + evt.preventDefault(); + evt.stopPropagation(); + + _this3[keyCode === KeyCodes.LEFT ? 'prev' : 'next'](); + } + } // Touch support event handlers for environment + + }; + + if (!this.noTouch && hasTouchSupport) { + /* istanbul ignore next: JSDOM doesn't support touch events */ + // Attach appropriate listeners (passsive mode) + if (hasPointerEvent) { + on['&pointerdown'] = this.touchStart; + on['&pointerup'] = this.touchEnd; + } else { + on['&touchstart'] = this.touchStart; + on['&touchmove'] = this.touchMove; + on['&touchend'] = this.touchEnd; + } + } // Return the carousel + + + return h('div', { + staticClass: 'carousel', + class: { + slide: !this.noAnimation, + 'carousel-fade': !this.noAnimation && this.fade, + 'pointer-event': !this.noTouch && hasTouchSupport && hasPointerEvent + }, + style: { + background: this.background + }, + attrs: { + role: 'region', + id: this.safeId(), + 'aria-busy': this.isSliding ? 'true' : 'false' + }, + on: on + }, [inner, controls, indicators]); + } + }; + + var BCarouselSlide = { + name: 'BCarouselSlide', + components: { + BImg: BImg + }, + mixins: [idMixin], + inject: { + bvCarousel: { + default: function _default() { + return { + // Explicitly disable touch if not a child of carousel + noTouch: true + }; + } + } + }, + props: { + imgSrc: { + type: String // default: undefined + + }, + imgAlt: { + type: String // default: undefined + + }, + imgWidth: { + type: [Number, String] // default: undefined + + }, + imgHeight: { + type: [Number, String] // default: undefined + + }, + imgBlank: { + type: Boolean, + default: false + }, + imgBlankColor: { + type: String, + default: 'transparent' + }, + contentVisibleUp: { + type: String + }, + contentTag: { + type: String, + default: 'div' + }, + caption: { + type: String + }, + captionHtml: { + type: String + }, + captionTag: { + type: String, + default: 'h3' + }, + text: { + type: String + }, + textHtml: { + type: String + }, + textTag: { + type: String, + default: 'p' + }, + background: { + type: String + } + }, + data: function data() { + return {}; + }, + computed: { + contentClasses: function contentClasses() { + return [this.contentVisibleUp ? 'd-none' : '', this.contentVisibleUp ? "d-".concat(this.contentVisibleUp, "-block") : '']; + }, + computedWidth: function computedWidth() { + // Use local width, or try parent width + return this.imgWidth || this.bvCarousel.imgWidth || null; + }, + computedHeight: function computedHeight() { + // Use local height, or try parent height + return this.imgHeight || this.bvCarousel.imgHeight || null; + } + }, + render: function render(h) { + var $slots = this.$slots; + var noDrag = !this.bvCarousel.noTouch && hasTouchSupport; + var img = $slots.img; + + if (!img && (this.imgSrc || this.imgBlank)) { + img = h('b-img', { + props: { + fluidGrow: true, + block: true, + src: this.imgSrc, + blank: this.imgBlank, + blankColor: this.imgBlankColor, + width: this.computedWidth, + height: this.computedHeight, + alt: this.imgAlt + }, + // Touch support event handler + on: noDrag ? { + dragstart: function dragstart(e) { + e.preventDefault(); + } + } : {} + }); + } + + if (!img) { + img = h(false); + } + + var content = h(this.contentTag, { + staticClass: 'carousel-caption', + class: this.contentClasses + }, [this.caption || this.captionHtml ? h(this.captionTag, { + domProps: htmlOrText(this.captionHtml, this.caption) + }) : h(false), this.text || this.textHtml ? h(this.textTag, { + domProps: htmlOrText(this.textHtml, this.text) + }) : h(false), $slots.default]); + return h('div', { + staticClass: 'carousel-item', + style: { + background: this.background || this.bvCarousel.background || null + }, + attrs: { + id: this.safeId(), + role: 'listitem' + } + }, [img, content]); + } + }; + + var components$8 = { + BCarousel: BCarousel, + BCarouselSlide: BCarouselSlide + }; + var index$8 = { + install: function install(Vue) { + registerComponents(Vue, components$8); + } + }; + + var props$j = { + tag: { + type: String, + default: 'div' + }, + fluid: { + type: Boolean, + default: false + } // @vue/component + + }; + var Container = { + name: 'BContainer', + functional: true, + props: props$j, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + class: { + container: !props.fluid, + 'container-fluid': props.fluid + } + }), children); + } + }; + + var COMMON_ALIGNMENT = ['start', 'end', 'center']; + var props$k = { + tag: { + type: String, + default: 'div' + }, + noGutters: { + type: Boolean, + default: false + }, + alignV: { + type: String, + default: null, + validator: function validator(str) { + return arrayIncludes(COMMON_ALIGNMENT.concat(['baseline', 'stretch']), str); + } + }, + alignH: { + type: String, + default: null, + validator: function validator(str) { + return arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around']), str); + } + }, + alignContent: { + type: String, + default: null, + validator: function validator(str) { + return arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around', 'stretch']), str); + } + } // @vue/component + + }; + var BRow = { + name: 'BRow', + functional: true, + props: props$k, + render: function render(h, _ref) { + var _class; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'row', + class: (_class = { + 'no-gutters': props.noGutters + }, _defineProperty(_class, "align-items-".concat(props.alignV), props.alignV), _defineProperty(_class, "justify-content-".concat(props.alignH), props.alignH), _defineProperty(_class, "align-content-".concat(props.alignContent), props.alignContent), _class) + }), children); + } + }; + + function memoize(fn) { + var cache = create(null); + return function memoizedFn() { + var args = JSON.stringify(arguments); + return cache[args] = cache[args] || fn.apply(null, arguments); + }; + } + + /** + * Suffix can be a falsey value so nothing is appended to string. + * (helps when looping over props & some shouldn't change) + * Use data last parameters to allow for currying. + * @param {string} suffix + * @param {string} str + */ + + function suffixPropName(suffix, str) { + return str + (suffix ? upperFirst(suffix) : ''); + } + + /** + * Generates a prop object with a type of + * [Boolean, String, Number] + */ + + function boolStrNum() { + return { + type: [Boolean, String, Number], + default: false + }; + } + /** + * Generates a prop object with a type of + * [String, Number] + */ + + + function strNum() { + return { + type: [String, Number], + default: null + }; + } + + var computeBkPtClass = memoize(function computeBkPt(type, breakpoint, val) { + var className = type; + + if (val === false || val === null || val === undefined) { + return undefined; + } + + if (breakpoint) { + className += "-".concat(breakpoint); + } // Handling the boolean style prop when accepting [Boolean, String, Number] + // means Vue will not convert to sm: true for us. + // Since the default is false, an empty string indicates the prop's presence. + + + if (type === 'col' && (val === '' || val === true)) { + // .col-md + return className.toLowerCase(); + } // .order-md-6 + + + className += "-".concat(val); + return className.toLowerCase(); + }); + var BREAKPOINTS = ['sm', 'md', 'lg', 'xl']; // Supports classes like: .col-sm, .col-md-6, .col-lg-auto + + var breakpointCol = BREAKPOINTS.reduce( // eslint-disable-next-line no-sequences + function (propMap, breakpoint) { + return propMap[breakpoint] = boolStrNum(), propMap; + }, create(null)); // Supports classes like: .offset-md-1, .offset-lg-12 + + var breakpointOffset = BREAKPOINTS.reduce( // eslint-disable-next-line no-sequences + function (propMap, breakpoint) { + return propMap[suffixPropName(breakpoint, 'offset')] = strNum(), propMap; + }, create(null)); // Supports classes like: .order-md-1, .order-lg-12 + + var breakpointOrder = BREAKPOINTS.reduce( // eslint-disable-next-line no-sequences + function (propMap, breakpoint) { + return propMap[suffixPropName(breakpoint, 'order')] = strNum(), propMap; + }, create(null)); // For loop doesn't need to check hasOwnProperty + // when using an object created from null + + var breakpointPropMap = assign(create(null), { + col: keys(breakpointCol), + offset: keys(breakpointOffset), + order: keys(breakpointOrder) + }); + var props$l = _objectSpread({}, breakpointCol, breakpointOffset, breakpointOrder, { + tag: { + type: String, + default: 'div' + }, + // Generic flexbox .col + col: { + type: Boolean, + default: false + }, + // .col-[1-12]|auto + cols: strNum(), + // .offset-[1-12] + offset: strNum(), + // Flex ordering utility .order-[1-12] + order: strNum(), + alignSelf: { + type: String, + default: null, + validator: function validator(str) { + return arrayIncludes(['auto', 'start', 'end', 'center', 'baseline', 'stretch'], str); + } + } + /** + * We need ".col" to default in when no other props are passed, + * but always render when col=true. + */ + // @vue/component + + }); + var BCol = { + name: 'BCol', + functional: true, + props: props$l, + render: function render(h, _ref) { + var _classList$push; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var classList = []; // Loop through `col`, `offset`, `order` breakpoint props + + for (var type in breakpointPropMap) { + // Returns colSm, offset, offsetSm, orderMd, etc. + var _keys = breakpointPropMap[type]; + + for (var i = 0; i < _keys.length; i++) { + // computeBkPt(col, colSm => Sm, value=[String, Number, Boolean]) + var c = computeBkPtClass(type, _keys[i].replace(type, ''), props[_keys[i]]); // If a class is returned, push it onto the array. + + if (c) { + classList.push(c); + } + } + } + + classList.push((_classList$push = { + // Default to .col if no other classes generated nor `cols` specified. + col: props.col || classList.length === 0 && !props.cols + }, _defineProperty(_classList$push, "col-".concat(props.cols), props.cols), _defineProperty(_classList$push, "offset-".concat(props.offset), props.offset), _defineProperty(_classList$push, "order-".concat(props.order), props.order), _defineProperty(_classList$push, "align-self-".concat(props.alignSelf), props.alignSelf), _classList$push)); + return h(props.tag, mergeData(data, { + class: classList + }), children); + } + }; + + var props$m = { + tag: { + type: String, + default: 'div' + } // @vue/component + + }; + var BFormRow = { + name: 'BFormRow', + functional: true, + props: props$m, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'form-row' + }), children); + } + }; + + var components$9 = { + BContainer: Container, + BRow: BRow, + BCol: BCol, + BFormRow: BFormRow + }; + var index$9 = { + install: function install(Vue) { + registerComponents(Vue, components$9); + } + }; + + /** + * Issue #569: collapse::toggle::state triggered too many times + * @link https://github.com/bootstrap-vue/bootstrap-vue/issues/569 + */ + + var BVRL = '__BV_root_listeners__'; // @vue/component + + var listenOnRootMixin = { + beforeDestroy: function beforeDestroy() { + if (this[BVRL] && isArray(this[BVRL])) { + while (this[BVRL].length > 0) { + // shift to process in order + var _this$BVRL$shift = this[BVRL].shift(), + event = _this$BVRL$shift.event, + callback = _this$BVRL$shift.callback; + + this.$root.$off(event, callback); + } + } + }, + methods: { + /** + * Safely register event listeners on the root Vue node. + * While Vue automatically removes listeners for individual components, + * when a component registers a listener on root and is destroyed, + * this orphans a callback because the node is gone, + * but the root does not clear the callback. + * + * This adds a non-reactive prop to a vm on the fly + * in order to avoid object observation and its performance costs + * to something that needs no reactivity. + * It should be highly unlikely there are any naming collisions. + * @param {string} event + * @param {function} callback + * @chainable + */ + listenOnRoot: function listenOnRoot(event, callback) { + if (!this[BVRL] || !isArray(this[BVRL])) { + this[BVRL] = []; + } + + this[BVRL].push({ + event: event, + callback: callback + }); + this.$root.$on(event, callback); + return this; + }, + + /** + * Convenience method for calling vm.$emit on vm.$root. + * @param {string} event + * @param {*} args + * @chainable + */ + emitOnRoot: function emitOnRoot(event) { + var _this$$root; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + (_this$$root = this.$root).$emit.apply(_this$$root, [event].concat(args)); + + return this; + } + } + }; + + var EVENT_STATE = 'bv::collapse::state'; + var EVENT_ACCORDION = 'bv::collapse::accordion'; // Events we listen to on $root + + var EVENT_TOGGLE = 'bv::toggle::collapse'; // Event Listener options + + var EventOptions$2 = { + passive: true, + capture: false // @vue/component + + }; + var BCollapse = { + name: 'BCollapse', + mixins: [listenOnRootMixin], + model: { + prop: 'visible', + event: 'input' + }, + props: { + id: { + type: String, + required: true + }, + isNav: { + type: Boolean, + default: false + }, + accordion: { + type: String, + default: null + }, + visible: { + type: Boolean, + default: false + }, + tag: { + type: String, + default: 'div' + } + }, + data: function data() { + return { + show: this.visible, + transitioning: false + }; + }, + computed: { + classObject: function classObject() { + return { + 'navbar-collapse': this.isNav, + collapse: !this.transitioning, + show: this.show && !this.transitioning + }; + } + }, + watch: { + visible: function visible(newVal) { + if (newVal !== this.show) { + this.show = newVal; + } + }, + show: function show(newVal, oldVal) { + if (newVal !== oldVal) { + this.emitState(); + } + } + }, + created: function created() { + // Listen for toggle events to open/close us + this.listenOnRoot(EVENT_TOGGLE, this.handleToggleEvt); // Listen to other collapses for accordion events + + this.listenOnRoot(EVENT_ACCORDION, this.handleAccordionEvt); + }, + mounted: function mounted() { + if (this.isNav && typeof document !== 'undefined') { + // Set up handlers + eventOn(window, 'resize', this.handleResize, EventOptions$2); + eventOn(window, 'orientationchange', this.handleResize, EventOptions$2); + this.handleResize(); + } + + this.emitState(); + }, + updated: function updated() { + this.$root.$emit(EVENT_STATE, this.id, this.show); + }, + beforeDestroy: function beforeDestroy() + /* istanbul ignore next */ + { + if (this.isNav && typeof document !== 'undefined') { + eventOff(window, 'resize', this.handleResize, EventOptions$2); + eventOff(window, 'orientationchange', this.handleResize, EventOptions$2); + } + }, + methods: { + toggle: function toggle() { + this.show = !this.show; + }, + onEnter: function onEnter(el) { + el.style.height = 0; + reflow(el); + el.style.height = el.scrollHeight + 'px'; + this.transitioning = true; // This should be moved out so we can add cancellable events + + this.$emit('show'); + }, + onAfterEnter: function onAfterEnter(el) { + el.style.height = null; + this.transitioning = false; + this.$emit('shown'); + }, + onLeave: function onLeave(el) { + el.style.height = 'auto'; + el.style.display = 'block'; + el.style.height = getBCR(el).height + 'px'; + reflow(el); + this.transitioning = true; + el.style.height = 0; // This should be moved out so we can add cancellable events + + this.$emit('hide'); + }, + onAfterLeave: function onAfterLeave(el) { + el.style.height = null; + this.transitioning = false; + this.$emit('hidden'); + }, + emitState: function emitState() { + this.$emit('input', this.show); // Let v-b-toggle know the state of this collapse + + this.$root.$emit(EVENT_STATE, this.id, this.show); + + if (this.accordion && this.show) { + // Tell the other collapses in this accordion to close + this.$root.$emit(EVENT_ACCORDION, this.id, this.accordion); + } + }, + clickHandler: function clickHandler(evt) { + // If we are in a nav/navbar, close the collapse when non-disabled link clicked + var el = evt.target; + + if (!this.isNav || !el || getCS(this.$el).display !== 'block') { + return; + } + + if (matches(el, '.nav-link,.dropdown-item') || closest('.nav-link,.dropdown-item', el)) { + this.show = false; + } + }, + handleToggleEvt: function handleToggleEvt(target) { + if (target !== this.id) { + return; + } + + this.toggle(); + }, + handleAccordionEvt: function handleAccordionEvt(openedId, accordion) { + if (!this.accordion || accordion !== this.accordion) { + return; + } + + if (openedId === this.id) { + // Open this collapse if not shown + if (!this.show) { + this.toggle(); + } + } else { + // Close this collapse if shown + if (this.show) { + this.toggle(); + } + } + }, + handleResize: function handleResize() { + // Handler for orientation/resize to set collapsed state in nav/navbar + this.show = getCS(this.$el).display === 'block'; + } + }, + render: function render(h) { + var content = h(this.tag, { + class: this.classObject, + directives: [{ + name: 'show', + value: this.show + }], + attrs: { + id: this.id || null + }, + on: { + click: this.clickHandler + } + }, [this.$slots.default]); + return h('transition', { + props: { + enterClass: '', + enterActiveClass: 'collapsing', + enterToClass: '', + leaveClass: '', + leaveActiveClass: 'collapsing', + leaveToClass: '' + }, + on: { + enter: this.onEnter, + afterEnter: this.onAfterEnter, + leave: this.onLeave, + afterLeave: this.onAfterLeave + } + }, [content]); + } + }; + + var allListenTypes = { + hover: true, + click: true, + focus: true + }; + var BVBoundListeners = '__BV_boundEventListeners__'; + + var bindTargets = function bindTargets(vnode, binding, listenTypes, fn) { + var targets = keys(binding.modifiers || {}).filter(function (t) { + return !allListenTypes[t]; + }); + + if (binding.value) { + targets.push(binding.value); + } + + var listener = function listener() { + fn({ + targets: targets, + vnode: vnode + }); + }; + + keys(allListenTypes).forEach(function (type) { + if (listenTypes[type] || binding.modifiers[type]) { + eventOn(vnode.elm, type, listener); + var boundListeners = vnode.elm[BVBoundListeners] || {}; + boundListeners[type] = boundListeners[type] || []; + boundListeners[type].push(listener); + vnode.elm[BVBoundListeners] = boundListeners; + } + }); // Return the list of targets + + return targets; + }; + + var unbindTargets = function unbindTargets(vnode, binding, listenTypes) { + keys(allListenTypes).forEach(function (type) { + if (listenTypes[type] || binding.modifiers[type]) { + var boundListeners = vnode.elm[BVBoundListeners] && vnode.elm[BVBoundListeners][type]; + + if (boundListeners) { + boundListeners.forEach(function (listener) { + return eventOff(vnode.elm, type, listener); + }); + delete vnode.elm[BVBoundListeners][type]; + } + } + }); + }; + + var inBrowser$1 = typeof window !== 'undefined'; // target listen types + + var listenTypes = { + click: true // Property key for handler storage + + }; + var BVT = '__BV_toggle__'; // Emitted Control Event for collapse (emitted to collapse) + + var EVENT_TOGGLE$1 = 'bv::toggle::collapse'; // Listen to Event for toggle state update (Emited by collapse) + + var EVENT_STATE$1 = 'bv::collapse::state'; + var bToggle = { + bind: function bind(el, binding, vnode) { + var targets = bindTargets(vnode, binding, listenTypes, function (_ref) { + var targets = _ref.targets, + vnode = _ref.vnode; + targets.forEach(function (target) { + vnode.context.$root.$emit(EVENT_TOGGLE$1, target); + }); + }); + + if (inBrowser$1 && vnode.context && targets.length > 0) { + // Add aria attributes to element + setAttr(el, 'aria-controls', targets.join(' ')); + setAttr(el, 'aria-expanded', 'false'); + + if (el.tagName !== 'BUTTON') { + // If element is not a button, we add `role="button"` for accessibility + setAttr(el, 'role', 'button'); + } // Toggle state hadnler, stored on element + + + el[BVT] = function toggleDirectiveHandler(id, state) { + if (targets.indexOf(id) !== -1) { + // Set aria-expanded state + setAttr(el, 'aria-expanded', state ? 'true' : 'false'); // Set/Clear 'collapsed' class state + + if (state) { + removeClass(el, 'collapsed'); + } else { + addClass(el, 'collapsed'); + } + } + }; // Listen for toggle state changes + + + vnode.context.$root.$on(EVENT_STATE$1, el[BVT]); + } + }, + unbind: function unbind(el, binding, vnode) { + if (el[BVT]) { + // Remove our $root listener + vnode.context.$root.$off(EVENT_STATE$1, el[BVT]); + el[BVT] = null; + } + } + }; + + var directives = { + bToggle: bToggle + }; + var toggleDirectivePlugin = { + install: function install(Vue) { + registerDirectives(Vue, directives); + } + }; + + var components$a = { + BCollapse: BCollapse + }; + var collapsePlugin = { + install: function install(Vue) { + registerComponents(Vue, components$a); + Vue.use(toggleDirectivePlugin); + } + }; + + /**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.14.7 + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * 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. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'; + + var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; + var timeoutDuration = 0; + for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { + if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { + timeoutDuration = 1; + break; + } + } + + function microtaskDebounce(fn) { + var called = false; + return function () { + if (called) { + return; + } + called = true; + window.Promise.resolve().then(function () { + called = false; + fn(); + }); + }; + } + + function taskDebounce(fn) { + var scheduled = false; + return function () { + if (!scheduled) { + scheduled = true; + setTimeout(function () { + scheduled = false; + fn(); + }, timeoutDuration); + } + }; + } + + var supportsMicroTasks = isBrowser && window.Promise; + + /** + * Create a debounced version of a method, that's asynchronously deferred + * but called in the minimum time possible. + * + * @method + * @memberof Popper.Utils + * @argument {Function} fn + * @returns {Function} + */ + var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce; + + /** + * Check if the given variable is a function + * @method + * @memberof Popper.Utils + * @argument {Any} functionToCheck - variable to check + * @returns {Boolean} answer to: is a function? + */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + /** + * Get CSS computed property of the given element + * @method + * @memberof Popper.Utils + * @argument {Eement} element + * @argument {String} property + */ + function getStyleComputedProperty(element, property) { + if (element.nodeType !== 1) { + return []; + } + // NOTE: 1 DOM access here + var window = element.ownerDocument.defaultView; + var css = window.getComputedStyle(element, null); + return property ? css[property] : css; + } + + /** + * Returns the parentNode or the host of the element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} parent + */ + function getParentNode(element) { + if (element.nodeName === 'HTML') { + return element; + } + return element.parentNode || element.host; + } + + /** + * Returns the scrolling parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} scroll parent + */ + function getScrollParent(element) { + // Return body, `getScroll` will take care to get the correct `scrollTop` from it + if (!element) { + return document.body; + } + + switch (element.nodeName) { + case 'HTML': + case 'BODY': + return element.ownerDocument.body; + case '#document': + return element.body; + } + + // Firefox want us to check `-x` and `-y` variations as well + + var _getStyleComputedProp = getStyleComputedProperty(element), + overflow = _getStyleComputedProp.overflow, + overflowX = _getStyleComputedProp.overflowX, + overflowY = _getStyleComputedProp.overflowY; + + if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { + return element; + } + + return getScrollParent(getParentNode(element)); + } + + var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode); + var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent); + + /** + * Determines if the browser is Internet Explorer + * @method + * @memberof Popper.Utils + * @param {Number} version to check + * @returns {Boolean} isIE + */ + function isIE(version) { + if (version === 11) { + return isIE11; + } + if (version === 10) { + return isIE10; + } + return isIE11 || isIE10; + } + + /** + * Returns the offset parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} offset parent + */ + function getOffsetParent(element) { + if (!element) { + return document.documentElement; + } + + var noOffsetParent = isIE(10) ? document.body : null; + + // NOTE: 1 DOM access here + var offsetParent = element.offsetParent || null; + // Skip hidden elements which don't have an offsetParent + while (offsetParent === noOffsetParent && element.nextElementSibling) { + offsetParent = (element = element.nextElementSibling).offsetParent; + } + + var nodeName = offsetParent && offsetParent.nodeName; + + if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { + return element ? element.ownerDocument.documentElement : document.documentElement; + } + + // .offsetParent will return the closest TH, TD or TABLE in case + // no offsetParent is present, I hate this job... + if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') { + return getOffsetParent(offsetParent); + } + + return offsetParent; + } + + function isOffsetContainer(element) { + var nodeName = element.nodeName; + + if (nodeName === 'BODY') { + return false; + } + return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element; + } + + /** + * Finds the root node (document, shadowDOM root) of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} node + * @returns {Element} root node + */ + function getRoot(node) { + if (node.parentNode !== null) { + return getRoot(node.parentNode); + } + + return node; + } + + /** + * Finds the offset parent common to the two provided nodes + * @method + * @memberof Popper.Utils + * @argument {Element} element1 + * @argument {Element} element2 + * @returns {Element} common offset parent + */ + function findCommonOffsetParent(element1, element2) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) { + return document.documentElement; + } + + // Here we make sure to give as "start" the element that comes first in the DOM + var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING; + var start = order ? element1 : element2; + var end = order ? element2 : element1; + + // Get common ancestor container + var range = document.createRange(); + range.setStart(start, 0); + range.setEnd(end, 0); + var commonAncestorContainer = range.commonAncestorContainer; + + // Both nodes are inside #document + + if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) { + if (isOffsetContainer(commonAncestorContainer)) { + return commonAncestorContainer; + } + + return getOffsetParent(commonAncestorContainer); + } + + // one of the nodes is inside shadowDOM, find which one + var element1root = getRoot(element1); + if (element1root.host) { + return findCommonOffsetParent(element1root.host, element2); + } else { + return findCommonOffsetParent(element1, getRoot(element2).host); + } + } + + /** + * Gets the scroll value of the given element in the given side (top and left) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {String} side `top` or `left` + * @returns {number} amount of scrolled pixels + */ + function getScroll(element) { + var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top'; + + var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft'; + var nodeName = element.nodeName; + + if (nodeName === 'BODY' || nodeName === 'HTML') { + var html = element.ownerDocument.documentElement; + var scrollingElement = element.ownerDocument.scrollingElement || html; + return scrollingElement[upperSide]; + } + + return element[upperSide]; + } + + /* + * Sum or subtract the element scroll values (left and top) from a given rect object + * @method + * @memberof Popper.Utils + * @param {Object} rect - Rect object you want to change + * @param {HTMLElement} element - The element from the function reads the scroll values + * @param {Boolean} subtract - set to true if you want to subtract the scroll values + * @return {Object} rect - The modifier rect object + */ + function includeScroll(rect, element) { + var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + var modifier = subtract ? -1 : 1; + rect.top += scrollTop * modifier; + rect.bottom += scrollTop * modifier; + rect.left += scrollLeft * modifier; + rect.right += scrollLeft * modifier; + return rect; + } + + /* + * Helper to detect borders of a given element + * @method + * @memberof Popper.Utils + * @param {CSSStyleDeclaration} styles + * Result of `getStyleComputedProperty` on the given element + * @param {String} axis - `x` or `y` + * @return {number} borders - The borders size of the given axis + */ + + function getBordersSize(styles, axis) { + var sideA = axis === 'x' ? 'Left' : 'Top'; + var sideB = sideA === 'Left' ? 'Right' : 'Bottom'; + + return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10); + } + + function getSize(axis, body, html, computedStyle) { + return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0); + } + + function getWindowSizes(document) { + var body = document.body; + var html = document.documentElement; + var computedStyle = isIE(10) && getComputedStyle(html); + + return { + height: getSize('Height', body, html, computedStyle), + width: getSize('Width', body, html, computedStyle) + }; + } + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + + + + + var defineProperty$1 = function (obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + }; + + var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + /** + * Given element offsets, generate an output similar to getBoundingClientRect + * @method + * @memberof Popper.Utils + * @argument {Object} offsets + * @returns {Object} ClientRect like output + */ + function getClientRect(offsets) { + return _extends({}, offsets, { + right: offsets.left + offsets.width, + bottom: offsets.top + offsets.height + }); + } + + /** + * Get bounding client rect of given element + * @method + * @memberof Popper.Utils + * @param {HTMLElement} element + * @return {Object} client rect + */ + function getBoundingClientRect(element) { + var rect = {}; + + // IE10 10 FIX: Please, don't ask, the element isn't + // considered in DOM in some circumstances... + // This isn't reproducible in IE10 compatibility mode of IE11 + try { + if (isIE(10)) { + rect = element.getBoundingClientRect(); + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + rect.top += scrollTop; + rect.left += scrollLeft; + rect.bottom += scrollTop; + rect.right += scrollLeft; + } else { + rect = element.getBoundingClientRect(); + } + } catch (e) {} + + var result = { + left: rect.left, + top: rect.top, + width: rect.right - rect.left, + height: rect.bottom - rect.top + }; + + // subtract scrollbar size from sizes + var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {}; + var width = sizes.width || element.clientWidth || result.right - result.left; + var height = sizes.height || element.clientHeight || result.bottom - result.top; + + var horizScrollbar = element.offsetWidth - width; + var vertScrollbar = element.offsetHeight - height; + + // if an hypothetical scrollbar is detected, we must be sure it's not a `border` + // we make this check conditional for performance reasons + if (horizScrollbar || vertScrollbar) { + var styles = getStyleComputedProperty(element); + horizScrollbar -= getBordersSize(styles, 'x'); + vertScrollbar -= getBordersSize(styles, 'y'); + + result.width -= horizScrollbar; + result.height -= vertScrollbar; + } + + return getClientRect(result); + } + + function getOffsetRectRelativeToArbitraryNode(children, parent) { + var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var isIE10 = isIE(10); + var isHTML = parent.nodeName === 'HTML'; + var childrenRect = getBoundingClientRect(children); + var parentRect = getBoundingClientRect(parent); + var scrollParent = getScrollParent(children); + + var styles = getStyleComputedProperty(parent); + var borderTopWidth = parseFloat(styles.borderTopWidth, 10); + var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10); + + // In cases where the parent is fixed, we must ignore negative scroll in offset calc + if (fixedPosition && isHTML) { + parentRect.top = Math.max(parentRect.top, 0); + parentRect.left = Math.max(parentRect.left, 0); + } + var offsets = getClientRect({ + top: childrenRect.top - parentRect.top - borderTopWidth, + left: childrenRect.left - parentRect.left - borderLeftWidth, + width: childrenRect.width, + height: childrenRect.height + }); + offsets.marginTop = 0; + offsets.marginLeft = 0; + + // Subtract margins of documentElement in case it's being used as parent + // we do this only on HTML because it's the only element that behaves + // differently when margins are applied to it. The margins are included in + // the box of the documentElement, in the other cases not. + if (!isIE10 && isHTML) { + var marginTop = parseFloat(styles.marginTop, 10); + var marginLeft = parseFloat(styles.marginLeft, 10); + + offsets.top -= borderTopWidth - marginTop; + offsets.bottom -= borderTopWidth - marginTop; + offsets.left -= borderLeftWidth - marginLeft; + offsets.right -= borderLeftWidth - marginLeft; + + // Attach marginTop and marginLeft because in some circumstances we may need them + offsets.marginTop = marginTop; + offsets.marginLeft = marginLeft; + } + + if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') { + offsets = includeScroll(offsets, parent); + } + + return offsets; + } + + function getViewportOffsetRectRelativeToArtbitraryNode(element) { + var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var html = element.ownerDocument.documentElement; + var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html); + var width = Math.max(html.clientWidth, window.innerWidth || 0); + var height = Math.max(html.clientHeight, window.innerHeight || 0); + + var scrollTop = !excludeScroll ? getScroll(html) : 0; + var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0; + + var offset = { + top: scrollTop - relativeOffset.top + relativeOffset.marginTop, + left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft, + width: width, + height: height + }; + + return getClientRect(offset); + } + + /** + * Check if the given element is fixed or is inside a fixed parent + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {Element} customContainer + * @returns {Boolean} answer to "isFixed?" + */ + function isFixed(element) { + var nodeName = element.nodeName; + if (nodeName === 'BODY' || nodeName === 'HTML') { + return false; + } + if (getStyleComputedProperty(element, 'position') === 'fixed') { + return true; + } + var parentNode = getParentNode(element); + if (!parentNode) { + return false; + } + return isFixed(parentNode); + } + + /** + * Finds the first parent of an element that has a transformed property defined + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} first transformed parent or documentElement + */ + + function getFixedPositionOffsetParent(element) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element || !element.parentElement || isIE()) { + return document.documentElement; + } + var el = element.parentElement; + while (el && getStyleComputedProperty(el, 'transform') === 'none') { + el = el.parentElement; + } + return el || document.documentElement; + } + + /** + * Computed the boundaries limits and return them + * @method + * @memberof Popper.Utils + * @param {HTMLElement} popper + * @param {HTMLElement} reference + * @param {number} padding + * @param {HTMLElement} boundariesElement - Element used to define the boundaries + * @param {Boolean} fixedPosition - Is in fixed position mode + * @returns {Object} Coordinates of the boundaries + */ + function getBoundaries(popper, reference, padding, boundariesElement) { + var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + // NOTE: 1 DOM access here + + var boundaries = { top: 0, left: 0 }; + var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); + + // Handle viewport case + if (boundariesElement === 'viewport') { + boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition); + } else { + // Handle other cases based on DOM element used as boundaries + var boundariesNode = void 0; + if (boundariesElement === 'scrollParent') { + boundariesNode = getScrollParent(getParentNode(reference)); + if (boundariesNode.nodeName === 'BODY') { + boundariesNode = popper.ownerDocument.documentElement; + } + } else if (boundariesElement === 'window') { + boundariesNode = popper.ownerDocument.documentElement; + } else { + boundariesNode = boundariesElement; + } + + var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); + + // In case of HTML, we need a different computation + if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) { + var _getWindowSizes = getWindowSizes(popper.ownerDocument), + height = _getWindowSizes.height, + width = _getWindowSizes.width; + + boundaries.top += offsets.top - offsets.marginTop; + boundaries.bottom = height + offsets.top; + boundaries.left += offsets.left - offsets.marginLeft; + boundaries.right = width + offsets.left; + } else { + // for all the other DOM elements, this one is good + boundaries = offsets; + } + } + + // Add paddings + padding = padding || 0; + var isPaddingNumber = typeof padding === 'number'; + boundaries.left += isPaddingNumber ? padding : padding.left || 0; + boundaries.top += isPaddingNumber ? padding : padding.top || 0; + boundaries.right -= isPaddingNumber ? padding : padding.right || 0; + boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0; + + return boundaries; + } + + function getArea(_ref) { + var width = _ref.width, + height = _ref.height; + + return width * height; + } + + /** + * Utility used to transform the `auto` placement to the placement with more + * available space. + * @method + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) { + var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; + + if (placement.indexOf('auto') === -1) { + return placement; + } + + var boundaries = getBoundaries(popper, reference, padding, boundariesElement); + + var rects = { + top: { + width: boundaries.width, + height: refRect.top - boundaries.top + }, + right: { + width: boundaries.right - refRect.right, + height: boundaries.height + }, + bottom: { + width: boundaries.width, + height: boundaries.bottom - refRect.bottom + }, + left: { + width: refRect.left - boundaries.left, + height: boundaries.height + } + }; + + var sortedAreas = Object.keys(rects).map(function (key) { + return _extends({ + key: key + }, rects[key], { + area: getArea(rects[key]) + }); + }).sort(function (a, b) { + return b.area - a.area; + }); + + var filteredAreas = sortedAreas.filter(function (_ref2) { + var width = _ref2.width, + height = _ref2.height; + return width >= popper.clientWidth && height >= popper.clientHeight; + }); + + var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key; + + var variation = placement.split('-')[1]; + + return computedPlacement + (variation ? '-' + variation : ''); + } + + /** + * Get offsets to the reference element + * @method + * @memberof Popper.Utils + * @param {Object} state + * @param {Element} popper - the popper element + * @param {Element} reference - the reference element (the popper will be relative to this) + * @param {Element} fixedPosition - is in fixed position mode + * @returns {Object} An object containing the offsets which will be applied to the popper + */ + function getReferenceOffsets(state, popper, reference) { + var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); + return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition); + } + + /** + * Get the outer sizes of the given element (offset size + margins) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Object} object containing width and height properties + */ + function getOuterSizes(element) { + var window = element.ownerDocument.defaultView; + var styles = window.getComputedStyle(element); + var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0); + var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0); + var result = { + width: element.offsetWidth + y, + height: element.offsetHeight + x + }; + return result; + } + + /** + * Get the opposite placement of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement + * @returns {String} flipped placement + */ + function getOppositePlacement(placement) { + var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash[matched]; + }); + } + + /** + * Get offsets to the popper + * @method + * @memberof Popper.Utils + * @param {Object} position - CSS position the Popper will get applied + * @param {HTMLElement} popper - the popper element + * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this) + * @param {String} placement - one of the valid placement options + * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper + */ + function getPopperOffsets(popper, referenceOffsets, placement) { + placement = placement.split('-')[0]; + + // Get popper node sizes + var popperRect = getOuterSizes(popper); + + // Add position, width and height to our offsets object + var popperOffsets = { + width: popperRect.width, + height: popperRect.height + }; + + // depending by the popper placement we have to compute its offsets slightly differently + var isHoriz = ['right', 'left'].indexOf(placement) !== -1; + var mainSide = isHoriz ? 'top' : 'left'; + var secondarySide = isHoriz ? 'left' : 'top'; + var measurement = isHoriz ? 'height' : 'width'; + var secondaryMeasurement = !isHoriz ? 'height' : 'width'; + + popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2; + if (placement === secondarySide) { + popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement]; + } else { + popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)]; + } + + return popperOffsets; + } + + /** + * Mimics the `find` method of Array + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function find(arr, check) { + // use native find if supported + if (Array.prototype.find) { + return arr.find(check); + } + + // use `filter` to obtain the same behavior of `find` + return arr.filter(check)[0]; + } + + /** + * Return the index of the matching object + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function findIndex(arr, prop, value) { + // use native findIndex if supported + if (Array.prototype.findIndex) { + return arr.findIndex(function (cur) { + return cur[prop] === value; + }); + } + + // use `find` + `indexOf` if `findIndex` isn't supported + var match = find(arr, function (obj) { + return obj[prop] === value; + }); + return arr.indexOf(match); + } + + /** + * Loop trough the list of modifiers and run them in order, + * each of them will then edit the data object. + * @method + * @memberof Popper.Utils + * @param {dataObject} data + * @param {Array} modifiers + * @param {String} ends - Optional modifier name used as stopper + * @returns {dataObject} + */ + function runModifiers(modifiers, data, ends) { + var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends)); + + modifiersToRun.forEach(function (modifier) { + if (modifier['function']) { + // eslint-disable-line dot-notation + console.warn('`modifier.function` is deprecated, use `modifier.fn`!'); + } + var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation + if (modifier.enabled && isFunction(fn)) { + // Add properties to offsets to make them a complete clientRect object + // we do this before each modifier to make sure the previous one doesn't + // mess with these values + data.offsets.popper = getClientRect(data.offsets.popper); + data.offsets.reference = getClientRect(data.offsets.reference); + + data = fn(data, modifier); + } + }); + + return data; + } + + /** + * Updates the position of the popper, computing the new offsets and applying + * the new style.
+ * Prefer `scheduleUpdate` over `update` because of performance reasons. + * @method + * @memberof Popper + */ + function update() { + // if popper is destroyed, don't perform any further update + if (this.state.isDestroyed) { + return; + } + + var data = { + instance: this, + styles: {}, + arrowStyles: {}, + attributes: {}, + flipped: false, + offsets: {} + }; + + // compute reference element offsets + data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding); + + // store the computed placement inside `originalPlacement` + data.originalPlacement = data.placement; + + data.positionFixed = this.options.positionFixed; + + // compute the popper offsets + data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement); + + data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute'; + + // run the modifiers + data = runModifiers(this.modifiers, data); + + // the first `update` will call `onCreate` callback + // the other ones will call `onUpdate` callback + if (!this.state.isCreated) { + this.state.isCreated = true; + this.options.onCreate(data); + } else { + this.options.onUpdate(data); + } + } + + /** + * Helper used to know if the given modifier is enabled. + * @method + * @memberof Popper.Utils + * @returns {Boolean} + */ + function isModifierEnabled(modifiers, modifierName) { + return modifiers.some(function (_ref) { + var name = _ref.name, + enabled = _ref.enabled; + return enabled && name === modifierName; + }); + } + + /** + * Get the prefixed supported property name + * @method + * @memberof Popper.Utils + * @argument {String} property (camelCase) + * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix) + */ + function getSupportedPropertyName(property) { + var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O']; + var upperProp = property.charAt(0).toUpperCase() + property.slice(1); + + for (var i = 0; i < prefixes.length; i++) { + var prefix = prefixes[i]; + var toCheck = prefix ? '' + prefix + upperProp : property; + if (typeof document.body.style[toCheck] !== 'undefined') { + return toCheck; + } + } + return null; + } + + /** + * Destroys the popper. + * @method + * @memberof Popper + */ + function destroy() { + this.state.isDestroyed = true; + + // touch DOM only if `applyStyle` modifier is enabled + if (isModifierEnabled(this.modifiers, 'applyStyle')) { + this.popper.removeAttribute('x-placement'); + this.popper.style.position = ''; + this.popper.style.top = ''; + this.popper.style.left = ''; + this.popper.style.right = ''; + this.popper.style.bottom = ''; + this.popper.style.willChange = ''; + this.popper.style[getSupportedPropertyName('transform')] = ''; + } + + this.disableEventListeners(); + + // remove the popper if user explicity asked for the deletion on destroy + // do not use `remove` because IE11 doesn't support it + if (this.options.removeOnDestroy) { + this.popper.parentNode.removeChild(this.popper); + } + return this; + } + + /** + * Get the window associated with the element + * @argument {Element} element + * @returns {Window} + */ + function getWindow(element) { + var ownerDocument = element.ownerDocument; + return ownerDocument ? ownerDocument.defaultView : window; + } + + function attachToScrollParents(scrollParent, event, callback, scrollParents) { + var isBody = scrollParent.nodeName === 'BODY'; + var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; + target.addEventListener(event, callback, { passive: true }); + + if (!isBody) { + attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents); + } + scrollParents.push(target); + } + + /** + * Setup needed event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function setupEventListeners(reference, options, state, updateBound) { + // Resize event listener on window + state.updateBound = updateBound; + getWindow(reference).addEventListener('resize', state.updateBound, { passive: true }); + + // Scroll event listener on scroll parents + var scrollElement = getScrollParent(reference); + attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents); + state.scrollElement = scrollElement; + state.eventsEnabled = true; + + return state; + } + + /** + * It will add resize/scroll events and start recalculating + * position of the popper element when they are triggered. + * @method + * @memberof Popper + */ + function enableEventListeners() { + if (!this.state.eventsEnabled) { + this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate); + } + } + + /** + * Remove event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function removeEventListeners(reference, state) { + // Remove resize event listener on window + getWindow(reference).removeEventListener('resize', state.updateBound); + + // Remove scroll event listener on scroll parents + state.scrollParents.forEach(function (target) { + target.removeEventListener('scroll', state.updateBound); + }); + + // Reset state + state.updateBound = null; + state.scrollParents = []; + state.scrollElement = null; + state.eventsEnabled = false; + return state; + } + + /** + * It will remove resize/scroll events and won't recalculate popper position + * when they are triggered. It also won't trigger `onUpdate` callback anymore, + * unless you call `update` method manually. + * @method + * @memberof Popper + */ + function disableEventListeners() { + if (this.state.eventsEnabled) { + cancelAnimationFrame(this.scheduleUpdate); + this.state = removeEventListeners(this.reference, this.state); + } + } + + /** + * Tells if a given input is a number + * @method + * @memberof Popper.Utils + * @param {*} input to check + * @return {Boolean} + */ + function isNumeric(n) { + return n !== '' && !isNaN(parseFloat(n)) && isFinite(n); + } + + /** + * Set the style to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the style to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setStyles(element, styles) { + Object.keys(styles).forEach(function (prop) { + var unit = ''; + // add unit if the value is numeric and is one of the following + if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) { + unit = 'px'; + } + element.style[prop] = styles[prop] + unit; + }); + } + + /** + * Set the attributes to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the attributes to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setAttributes(element, attributes) { + Object.keys(attributes).forEach(function (prop) { + var value = attributes[prop]; + if (value !== false) { + element.setAttribute(prop, attributes[prop]); + } else { + element.removeAttribute(prop); + } + }); + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} data.styles - List of style properties - values to apply to popper element + * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The same data object + */ + function applyStyle(data) { + // any property present in `data.styles` will be applied to the popper, + // in this way we can make the 3rd party modifiers add custom styles to it + // Be aware, modifiers could override the properties defined in the previous + // lines of this modifier! + setStyles(data.instance.popper, data.styles); + + // any property present in `data.attributes` will be applied to the popper, + // they will be set as HTML attributes of the element + setAttributes(data.instance.popper, data.attributes); + + // if arrowElement is defined and arrowStyles has some properties + if (data.arrowElement && Object.keys(data.arrowStyles).length) { + setStyles(data.arrowElement, data.arrowStyles); + } + + return data; + } + + /** + * Set the x-placement attribute before everything else because it could be used + * to add margins to the popper margins needs to be calculated to get the + * correct popper offsets. + * @method + * @memberof Popper.modifiers + * @param {HTMLElement} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as popper + * @param {Object} options - Popper.js options + */ + function applyStyleOnLoad(reference, popper, options, modifierOptions, state) { + // compute reference element offsets + var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding); + + popper.setAttribute('x-placement', placement); + + // Apply `position` to popper before anything else because + // without the position applied we can't guarantee correct computations + setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' }); + + return options; + } + + /** + * @function + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by `update` method + * @argument {Boolean} shouldRound - If the offsets should be rounded at all + * @returns {Object} The popper's position offsets rounded + * + * The tale of pixel-perfect positioning. It's still not 100% perfect, but as + * good as it can be within reason. + * Discussion here: https://github.com/FezVrasta/popper.js/pull/715 + * + * Low DPI screens cause a popper to be blurry if not using full pixels (Safari + * as well on High DPI screens). + * + * Firefox prefers no rounding for positioning and does not have blurriness on + * high DPI screens. + * + * Only horizontal placement and left/right values need to be considered. + */ + function getRoundedOffsets(data, shouldRound) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + var round = Math.round, + floor = Math.floor; + + var noRound = function noRound(v) { + return v; + }; + + var referenceWidth = round(reference.width); + var popperWidth = round(popper.width); + + var isVertical = ['left', 'right'].indexOf(data.placement) !== -1; + var isVariation = data.placement.indexOf('-') !== -1; + var sameWidthParity = referenceWidth % 2 === popperWidth % 2; + var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1; + + var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor; + var verticalToInteger = !shouldRound ? noRound : round; + + return { + left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left), + top: verticalToInteger(popper.top), + bottom: verticalToInteger(popper.bottom), + right: horizontalToInteger(popper.right) + }; + } + + var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent); + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeStyle(data, options) { + var x = options.x, + y = options.y; + var popper = data.offsets.popper; + + // Remove this legacy support in Popper.js v2 + + var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'applyStyle'; + }).gpuAcceleration; + if (legacyGpuAccelerationOption !== undefined) { + console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!'); + } + var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration; + + var offsetParent = getOffsetParent(data.instance.popper); + var offsetParentRect = getBoundingClientRect(offsetParent); + + // Styles + var styles = { + position: popper.position + }; + + var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox); + + var sideA = x === 'bottom' ? 'top' : 'bottom'; + var sideB = y === 'right' ? 'left' : 'right'; + + // if gpuAcceleration is set to `true` and transform is supported, + // we use `translate3d` to apply the position to the popper we + // automatically use the supported prefixed version if needed + var prefixedProperty = getSupportedPropertyName('transform'); + + // now, let's make a step back and look at this code closely (wtf?) + // If the content of the popper grows once it's been positioned, it + // may happen that the popper gets misplaced because of the new content + // overflowing its reference element + // To avoid this problem, we provide two options (x and y), which allow + // the consumer to define the offset origin. + // If we position a popper on top of a reference element, we can set + // `x` to `top` to make the popper grow towards its top instead of + // its bottom. + var left = void 0, + top = void 0; + if (sideA === 'bottom') { + // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar) + // and not the bottom of the html element + if (offsetParent.nodeName === 'HTML') { + top = -offsetParent.clientHeight + offsets.bottom; + } else { + top = -offsetParentRect.height + offsets.bottom; + } + } else { + top = offsets.top; + } + if (sideB === 'right') { + if (offsetParent.nodeName === 'HTML') { + left = -offsetParent.clientWidth + offsets.right; + } else { + left = -offsetParentRect.width + offsets.right; + } + } else { + left = offsets.left; + } + if (gpuAcceleration && prefixedProperty) { + styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; + styles[sideA] = 0; + styles[sideB] = 0; + styles.willChange = 'transform'; + } else { + // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties + var invertTop = sideA === 'bottom' ? -1 : 1; + var invertLeft = sideB === 'right' ? -1 : 1; + styles[sideA] = top * invertTop; + styles[sideB] = left * invertLeft; + styles.willChange = sideA + ', ' + sideB; + } + + // Attributes + var attributes = { + 'x-placement': data.placement + }; + + // Update `data` attributes, styles and arrowStyles + data.attributes = _extends({}, attributes, data.attributes); + data.styles = _extends({}, styles, data.styles); + data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles); + + return data; + } + + /** + * Helper used to know if the given modifier depends from another one.
+ * It checks if the needed modifier is listed and enabled. + * @method + * @memberof Popper.Utils + * @param {Array} modifiers - list of modifiers + * @param {String} requestingName - name of requesting modifier + * @param {String} requestedName - name of requested modifier + * @returns {Boolean} + */ + function isModifierRequired(modifiers, requestingName, requestedName) { + var requesting = find(modifiers, function (_ref) { + var name = _ref.name; + return name === requestingName; + }); + + var isRequired = !!requesting && modifiers.some(function (modifier) { + return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order; + }); + + if (!isRequired) { + var _requesting = '`' + requestingName + '`'; + var requested = '`' + requestedName + '`'; + console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!'); + } + return isRequired; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function arrow(data, options) { + var _data$offsets$arrow; + + // arrow depends on keepTogether in order to work + if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) { + return data; + } + + var arrowElement = options.element; + + // if arrowElement is a string, suppose it's a CSS selector + if (typeof arrowElement === 'string') { + arrowElement = data.instance.popper.querySelector(arrowElement); + + // if arrowElement is not found, don't run the modifier + if (!arrowElement) { + return data; + } + } else { + // if the arrowElement isn't a query selector we must check that the + // provided DOM node is child of its popper node + if (!data.instance.popper.contains(arrowElement)) { + console.warn('WARNING: `arrow.element` must be child of its popper element!'); + return data; + } + } + + var placement = data.placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isVertical = ['left', 'right'].indexOf(placement) !== -1; + + var len = isVertical ? 'height' : 'width'; + var sideCapitalized = isVertical ? 'Top' : 'Left'; + var side = sideCapitalized.toLowerCase(); + var altSide = isVertical ? 'left' : 'top'; + var opSide = isVertical ? 'bottom' : 'right'; + var arrowElementSize = getOuterSizes(arrowElement)[len]; + + // + // extends keepTogether behavior making sure the popper and its + // reference have enough pixels in conjunction + // + + // top/left side + if (reference[opSide] - arrowElementSize < popper[side]) { + data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize); + } + // bottom/right side + if (reference[side] + arrowElementSize > popper[opSide]) { + data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide]; + } + data.offsets.popper = getClientRect(data.offsets.popper); + + // compute center of the popper + var center = reference[side] + reference[len] / 2 - arrowElementSize / 2; + + // Compute the sideValue using the updated popper offsets + // take popper margin in account because we don't have this info available + var css = getStyleComputedProperty(data.instance.popper); + var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10); + var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10); + var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide; + + // prevent arrowElement from being placed not contiguously to its popper + sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0); + + data.arrowElement = arrowElement; + data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty$1(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty$1(_data$offsets$arrow, altSide, ''), _data$offsets$arrow); + + return data; + } + + /** + * Get the opposite placement variation of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement variation + * @returns {String} flipped placement variation + */ + function getOppositeVariation(variation) { + if (variation === 'end') { + return 'start'; + } else if (variation === 'start') { + return 'end'; + } + return variation; + } + + /** + * List of accepted placements to use as values of the `placement` option.
+ * Valid placements are: + * - `auto` + * - `top` + * - `right` + * - `bottom` + * - `left` + * + * Each placement can have a variation from this list: + * - `-start` + * - `-end` + * + * Variations are interpreted easily if you think of them as the left to right + * written languages. Horizontally (`top` and `bottom`), `start` is left and `end` + * is right.
+ * Vertically (`left` and `right`), `start` is top and `end` is bottom. + * + * Some valid examples are: + * - `top-end` (on top of reference, right aligned) + * - `right-start` (on right of reference, top aligned) + * - `bottom` (on bottom, centered) + * - `auto-end` (on the side with more space available, alignment depends by placement) + * + * @static + * @type {Array} + * @enum {String} + * @readonly + * @method placements + * @memberof Popper + */ + var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']; + + // Get rid of `auto` `auto-start` and `auto-end` + var validPlacements = placements.slice(3); + + /** + * Given an initial placement, returns all the subsequent placements + * clockwise (or counter-clockwise). + * + * @method + * @memberof Popper.Utils + * @argument {String} placement - A valid placement (it accepts variations) + * @argument {Boolean} counter - Set to true to walk the placements counterclockwise + * @returns {Array} placements including their variations + */ + function clockwise(placement) { + var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var index = validPlacements.indexOf(placement); + var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index)); + return counter ? arr.reverse() : arr; + } + + var BEHAVIORS = { + FLIP: 'flip', + CLOCKWISE: 'clockwise', + COUNTERCLOCKWISE: 'counterclockwise' + }; + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function flip(data, options) { + // if `inner` modifier is enabled, we can't use the `flip` modifier + if (isModifierEnabled(data.instance.modifiers, 'inner')) { + return data; + } + + if (data.flipped && data.placement === data.originalPlacement) { + // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides + return data; + } + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed); + + var placement = data.placement.split('-')[0]; + var placementOpposite = getOppositePlacement(placement); + var variation = data.placement.split('-')[1] || ''; + + var flipOrder = []; + + switch (options.behavior) { + case BEHAVIORS.FLIP: + flipOrder = [placement, placementOpposite]; + break; + case BEHAVIORS.CLOCKWISE: + flipOrder = clockwise(placement); + break; + case BEHAVIORS.COUNTERCLOCKWISE: + flipOrder = clockwise(placement, true); + break; + default: + flipOrder = options.behavior; + } + + flipOrder.forEach(function (step, index) { + if (placement !== step || flipOrder.length === index + 1) { + return data; + } + + placement = data.placement.split('-')[0]; + placementOpposite = getOppositePlacement(placement); + + var popperOffsets = data.offsets.popper; + var refOffsets = data.offsets.reference; + + // using floor because the reference offsets may contain decimals we are not going to consider here + var floor = Math.floor; + var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom); + + var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); + var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); + var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); + var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); + + var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom; + + // flip the variation if required + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); + + if (overlapsRef || overflowsBoundaries || flippedVariation) { + // this boolean to detect any flip loop + data.flipped = true; + + if (overlapsRef || overflowsBoundaries) { + placement = flipOrder[index + 1]; + } + + if (flippedVariation) { + variation = getOppositeVariation(variation); + } + + data.placement = placement + (variation ? '-' + variation : ''); + + // this object contains `position`, we want to preserve it along with + // any additional property we may add in the future + data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement)); + + data = runModifiers(data.instance.modifiers, data, 'flip'); + } + }); + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function keepTogether(data) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var placement = data.placement.split('-')[0]; + var floor = Math.floor; + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var side = isVertical ? 'right' : 'bottom'; + var opSide = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + if (popper[side] < floor(reference[opSide])) { + data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement]; + } + if (popper[opSide] > floor(reference[side])) { + data.offsets.popper[opSide] = floor(reference[side]); + } + + return data; + } + + /** + * Converts a string containing value + unit into a px value number + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} str - Value + unit string + * @argument {String} measurement - `height` or `width` + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @returns {Number|String} + * Value in pixels, or original string if no values were extracted + */ + function toValue(str, measurement, popperOffsets, referenceOffsets) { + // separate value from unit + var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/); + var value = +split[1]; + var unit = split[2]; + + // If it's not a number it's an operator, I guess + if (!value) { + return str; + } + + if (unit.indexOf('%') === 0) { + var element = void 0; + switch (unit) { + case '%p': + element = popperOffsets; + break; + case '%': + case '%r': + default: + element = referenceOffsets; + } + + var rect = getClientRect(element); + return rect[measurement] / 100 * value; + } else if (unit === 'vh' || unit === 'vw') { + // if is a vh or vw, we calculate the size based on the viewport + var size = void 0; + if (unit === 'vh') { + size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + } else { + size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + } + return size / 100 * value; + } else { + // if is an explicit pixel unit, we get rid of the unit and keep the value + // if is an implicit unit, it's px, and we return just the value + return value; + } + } + + /** + * Parse an `offset` string to extrapolate `x` and `y` numeric offsets. + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} offset + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @argument {String} basePlacement + * @returns {Array} a two cells array with x and y offsets in numbers + */ + function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) { + var offsets = [0, 0]; + + // Use height if placement is left or right and index is 0 otherwise use width + // in this way the first offset will use an axis and the second one + // will use the other one + var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1; + + // Split the offset string to obtain a list of values and operands + // The regex addresses values with the plus or minus sign in front (+10, -20, etc) + var fragments = offset.split(/(\+|\-)/).map(function (frag) { + return frag.trim(); + }); + + // Detect if the offset string contains a pair of values or a single one + // they could be separated by comma or space + var divider = fragments.indexOf(find(fragments, function (frag) { + return frag.search(/,|\s/) !== -1; + })); + + if (fragments[divider] && fragments[divider].indexOf(',') === -1) { + console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.'); + } + + // If divider is found, we divide the list of values and operands to divide + // them by ofset X and Y. + var splitRegex = /\s*,\s*|\s+/; + var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments]; + + // Convert the values with units to absolute pixels to allow our computations + ops = ops.map(function (op, index) { + // Most of the units rely on the orientation of the popper + var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width'; + var mergeWithPrevious = false; + return op + // This aggregates any `+` or `-` sign that aren't considered operators + // e.g.: 10 + +5 => [10, +, +5] + .reduce(function (a, b) { + if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) { + a[a.length - 1] = b; + mergeWithPrevious = true; + return a; + } else if (mergeWithPrevious) { + a[a.length - 1] += b; + mergeWithPrevious = false; + return a; + } else { + return a.concat(b); + } + }, []) + // Here we convert the string values into number values (in px) + .map(function (str) { + return toValue(str, measurement, popperOffsets, referenceOffsets); + }); + }); + + // Loop trough the offsets arrays and execute the operations + ops.forEach(function (op, index) { + op.forEach(function (frag, index2) { + if (isNumeric(frag)) { + offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1); + } + }); + }); + return offsets; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @argument {Number|String} options.offset=0 + * The offset value as described in the modifier description + * @returns {Object} The data object, properly modified + */ + function offset$1(data, _ref) { + var offset = _ref.offset; + var placement = data.placement, + _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var basePlacement = placement.split('-')[0]; + + var offsets = void 0; + if (isNumeric(+offset)) { + offsets = [+offset, 0]; + } else { + offsets = parseOffset(offset, popper, reference, basePlacement); + } + + if (basePlacement === 'left') { + popper.top += offsets[0]; + popper.left -= offsets[1]; + } else if (basePlacement === 'right') { + popper.top += offsets[0]; + popper.left += offsets[1]; + } else if (basePlacement === 'top') { + popper.left += offsets[0]; + popper.top -= offsets[1]; + } else if (basePlacement === 'bottom') { + popper.left += offsets[0]; + popper.top += offsets[1]; + } + + data.popper = popper; + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function preventOverflow(data, options) { + var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper); + + // If offsetParent is the reference element, we really want to + // go one step up and use the next offsetParent as reference to + // avoid to make this modifier completely useless and look like broken + if (data.instance.reference === boundariesElement) { + boundariesElement = getOffsetParent(boundariesElement); + } + + // NOTE: DOM access here + // resets the popper's position so that the document size can be calculated excluding + // the size of the popper element itself + var transformProp = getSupportedPropertyName('transform'); + var popperStyles = data.instance.popper.style; // assignment to help minification + var top = popperStyles.top, + left = popperStyles.left, + transform = popperStyles[transformProp]; + + popperStyles.top = ''; + popperStyles.left = ''; + popperStyles[transformProp] = ''; + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed); + + // NOTE: DOM access here + // restores the original style properties after the offsets have been computed + popperStyles.top = top; + popperStyles.left = left; + popperStyles[transformProp] = transform; + + options.boundaries = boundaries; + + var order = options.priority; + var popper = data.offsets.popper; + + var check = { + primary: function primary(placement) { + var value = popper[placement]; + if (popper[placement] < boundaries[placement] && !options.escapeWithReference) { + value = Math.max(popper[placement], boundaries[placement]); + } + return defineProperty$1({}, placement, value); + }, + secondary: function secondary(placement) { + var mainSide = placement === 'right' ? 'left' : 'top'; + var value = popper[mainSide]; + if (popper[placement] > boundaries[placement] && !options.escapeWithReference) { + value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height)); + } + return defineProperty$1({}, mainSide, value); + } + }; + + order.forEach(function (placement) { + var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary'; + popper = _extends({}, popper, check[side](placement)); + }); + + data.offsets.popper = popper; + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function shift(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var shiftvariation = placement.split('-')[1]; + + // if shift shiftvariation is specified, run the modifier + if (shiftvariation) { + var _data$offsets = data.offsets, + reference = _data$offsets.reference, + popper = _data$offsets.popper; + + var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1; + var side = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + var shiftOffsets = { + start: defineProperty$1({}, side, reference[side]), + end: defineProperty$1({}, side, reference[side] + reference[measurement] - popper[measurement]) + }; + + data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]); + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function hide(data) { + if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) { + return data; + } + + var refRect = data.offsets.reference; + var bound = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'preventOverflow'; + }).boundaries; + + if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === true) { + return data; + } + + data.hide = true; + data.attributes['x-out-of-boundaries'] = ''; + } else { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === false) { + return data; + } + + data.hide = false; + data.attributes['x-out-of-boundaries'] = false; + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function inner(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1; + + var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1; + + popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0); + + data.placement = getOppositePlacement(placement); + data.offsets.popper = getClientRect(popper); + + return data; + } + + /** + * Modifier function, each modifier can have a function of this type assigned + * to its `fn` property.
+ * These functions will be called on each update, this means that you must + * make sure they are performant enough to avoid performance bottlenecks. + * + * @function ModifierFn + * @argument {dataObject} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {dataObject} The data object, properly modified + */ + + /** + * Modifiers are plugins used to alter the behavior of your poppers.
+ * Popper.js uses a set of 9 modifiers to provide all the basic functionalities + * needed by the library. + * + * Usually you don't want to override the `order`, `fn` and `onLoad` props. + * All the other properties are configurations that could be tweaked. + * @namespace modifiers + */ + var modifiers = { + /** + * Modifier used to shift the popper on the start or end of its reference + * element.
+ * It will read the variation of the `placement` property.
+ * It can be one either `-end` or `-start`. + * @memberof modifiers + * @inner + */ + shift: { + /** @prop {number} order=100 - Index used to define the order of execution */ + order: 100, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: shift + }, + + /** + * The `offset` modifier can shift your popper on both its axis. + * + * It accepts the following units: + * - `px` or unit-less, interpreted as pixels + * - `%` or `%r`, percentage relative to the length of the reference element + * - `%p`, percentage relative to the length of the popper element + * - `vw`, CSS viewport width unit + * - `vh`, CSS viewport height unit + * + * For length is intended the main axis relative to the placement of the popper.
+ * This means that if the placement is `top` or `bottom`, the length will be the + * `width`. In case of `left` or `right`, it will be the `height`. + * + * You can provide a single value (as `Number` or `String`), or a pair of values + * as `String` divided by a comma or one (or more) white spaces.
+ * The latter is a deprecated method because it leads to confusion and will be + * removed in v2.
+ * Additionally, it accepts additions and subtractions between different units. + * Note that multiplications and divisions aren't supported. + * + * Valid examples are: + * ``` + * 10 + * '10%' + * '10, 10' + * '10%, 10' + * '10 + 10%' + * '10 - 5vh + 3%' + * '-10px + 5vh, 5px - 6%' + * ``` + * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap + * > with their reference element, unfortunately, you will have to disable the `flip` modifier. + * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373). + * + * @memberof modifiers + * @inner + */ + offset: { + /** @prop {number} order=200 - Index used to define the order of execution */ + order: 200, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: offset$1, + /** @prop {Number|String} offset=0 + * The offset value as described in the modifier description + */ + offset: 0 + }, + + /** + * Modifier used to prevent the popper from being positioned outside the boundary. + * + * A scenario exists where the reference itself is not within the boundaries.
+ * We can say it has "escaped the boundaries" — or just "escaped".
+ * In this case we need to decide whether the popper should either: + * + * - detach from the reference and remain "trapped" in the boundaries, or + * - if it should ignore the boundary and "escape with its reference" + * + * When `escapeWithReference` is set to`true` and reference is completely + * outside its boundaries, the popper will overflow (or completely leave) + * the boundaries in order to remain attached to the edge of the reference. + * + * @memberof modifiers + * @inner + */ + preventOverflow: { + /** @prop {number} order=300 - Index used to define the order of execution */ + order: 300, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: preventOverflow, + /** + * @prop {Array} [priority=['left','right','top','bottom']] + * Popper will try to prevent overflow following these priorities by default, + * then, it could overflow on the left and on top of the `boundariesElement` + */ + priority: ['left', 'right', 'top', 'bottom'], + /** + * @prop {number} padding=5 + * Amount of pixel used to define a minimum distance between the boundaries + * and the popper. This makes sure the popper always has a little padding + * between the edges of its container + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='scrollParent' + * Boundaries used by the modifier. Can be `scrollParent`, `window`, + * `viewport` or any DOM element. + */ + boundariesElement: 'scrollParent' + }, + + /** + * Modifier used to make sure the reference and its popper stay near each other + * without leaving any gap between the two. Especially useful when the arrow is + * enabled and you want to ensure that it points to its reference element. + * It cares only about the first axis. You can still have poppers with margin + * between the popper and its reference element. + * @memberof modifiers + * @inner + */ + keepTogether: { + /** @prop {number} order=400 - Index used to define the order of execution */ + order: 400, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: keepTogether + }, + + /** + * This modifier is used to move the `arrowElement` of the popper to make + * sure it is positioned between the reference element and its popper element. + * It will read the outer size of the `arrowElement` node to detect how many + * pixels of conjunction are needed. + * + * It has no effect if no `arrowElement` is provided. + * @memberof modifiers + * @inner + */ + arrow: { + /** @prop {number} order=500 - Index used to define the order of execution */ + order: 500, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: arrow, + /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */ + element: '[x-arrow]' + }, + + /** + * Modifier used to flip the popper's placement when it starts to overlap its + * reference element. + * + * Requires the `preventOverflow` modifier before it in order to work. + * + * **NOTE:** this modifier will interrupt the current update cycle and will + * restart it if it detects the need to flip the placement. + * @memberof modifiers + * @inner + */ + flip: { + /** @prop {number} order=600 - Index used to define the order of execution */ + order: 600, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: flip, + /** + * @prop {String|Array} behavior='flip' + * The behavior used to change the popper's placement. It can be one of + * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid + * placements (with optional variations) + */ + behavior: 'flip', + /** + * @prop {number} padding=5 + * The popper will flip if it hits the edges of the `boundariesElement` + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='viewport' + * The element which will define the boundaries of the popper position. + * The popper will never be placed outside of the defined boundaries + * (except if `keepTogether` is enabled) + */ + boundariesElement: 'viewport' + }, + + /** + * Modifier used to make the popper flow toward the inner of the reference element. + * By default, when this modifier is disabled, the popper will be placed outside + * the reference element. + * @memberof modifiers + * @inner + */ + inner: { + /** @prop {number} order=700 - Index used to define the order of execution */ + order: 700, + /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */ + enabled: false, + /** @prop {ModifierFn} */ + fn: inner + }, + + /** + * Modifier used to hide the popper when its reference element is outside of the + * popper boundaries. It will set a `x-out-of-boundaries` attribute which can + * be used to hide with a CSS selector the popper when its reference is + * out of boundaries. + * + * Requires the `preventOverflow` modifier before it in order to work. + * @memberof modifiers + * @inner + */ + hide: { + /** @prop {number} order=800 - Index used to define the order of execution */ + order: 800, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: hide + }, + + /** + * Computes the style that will be applied to the popper element to gets + * properly positioned. + * + * Note that this modifier will not touch the DOM, it just prepares the styles + * so that `applyStyle` modifier can apply it. This separation is useful + * in case you need to replace `applyStyle` with a custom implementation. + * + * This modifier has `850` as `order` value to maintain backward compatibility + * with previous versions of Popper.js. Expect the modifiers ordering method + * to change in future major versions of the library. + * + * @memberof modifiers + * @inner + */ + computeStyle: { + /** @prop {number} order=850 - Index used to define the order of execution */ + order: 850, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: computeStyle, + /** + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: true, + /** + * @prop {string} [x='bottom'] + * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin. + * Change this if your popper should grow in a direction different from `bottom` + */ + x: 'bottom', + /** + * @prop {string} [x='left'] + * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin. + * Change this if your popper should grow in a direction different from `right` + */ + y: 'right' + }, + + /** + * Applies the computed styles to the popper element. + * + * All the DOM manipulations are limited to this modifier. This is useful in case + * you want to integrate Popper.js inside a framework or view library and you + * want to delegate all the DOM manipulations to it. + * + * Note that if you disable this modifier, you must make sure the popper element + * has its position set to `absolute` before Popper.js can do its work! + * + * Just disable this modifier and define your own to achieve the desired effect. + * + * @memberof modifiers + * @inner + */ + applyStyle: { + /** @prop {number} order=900 - Index used to define the order of execution */ + order: 900, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: applyStyle, + /** @prop {Function} */ + onLoad: applyStyleOnLoad, + /** + * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: undefined + } + }; + + /** + * The `dataObject` is an object containing all the information used by Popper.js. + * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks. + * @name dataObject + * @property {Object} data.instance The Popper.js instance + * @property {String} data.placement Placement applied to popper + * @property {String} data.originalPlacement Placement originally defined on init + * @property {Boolean} data.flipped True if popper has been flipped by flip modifier + * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper + * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier + * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.boundaries Offsets of the popper boundaries + * @property {Object} data.offsets The measurements of popper, reference and arrow elements + * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0 + */ + + /** + * Default options provided to Popper.js constructor.
+ * These can be overridden using the `options` argument of Popper.js.
+ * To override an option, simply pass an object with the same + * structure of the `options` object, as the 3rd argument. For example: + * ``` + * new Popper(ref, pop, { + * modifiers: { + * preventOverflow: { enabled: false } + * } + * }) + * ``` + * @type {Object} + * @static + * @memberof Popper + */ + var Defaults = { + /** + * Popper's placement. + * @prop {Popper.placements} placement='bottom' + */ + placement: 'bottom', + + /** + * Set this to true if you want popper to position it self in 'fixed' mode + * @prop {Boolean} positionFixed=false + */ + positionFixed: false, + + /** + * Whether events (resize, scroll) are initially enabled. + * @prop {Boolean} eventsEnabled=true + */ + eventsEnabled: true, + + /** + * Set to true if you want to automatically remove the popper when + * you call the `destroy` method. + * @prop {Boolean} removeOnDestroy=false + */ + removeOnDestroy: false, + + /** + * Callback called when the popper is created.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onCreate} + */ + onCreate: function onCreate() {}, + + /** + * Callback called when the popper is updated. This callback is not called + * on the initialization/creation of the popper, but only on subsequent + * updates.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onUpdate} + */ + onUpdate: function onUpdate() {}, + + /** + * List of modifiers used to modify the offsets before they are applied to the popper. + * They provide most of the functionalities of Popper.js. + * @prop {modifiers} + */ + modifiers: modifiers + }; + + /** + * @callback onCreate + * @param {dataObject} data + */ + + /** + * @callback onUpdate + * @param {dataObject} data + */ + + // Utils + // Methods + var Popper = function () { + /** + * Creates a new Popper.js instance. + * @class Popper + * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as the popper + * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults) + * @return {Object} instance - The generated Popper.js instance + */ + function Popper(reference, popper) { + var _this = this; + + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + classCallCheck(this, Popper); + + this.scheduleUpdate = function () { + return requestAnimationFrame(_this.update); + }; + + // make update() debounced, so that it only runs at most once-per-tick + this.update = debounce(this.update.bind(this)); + + // with {} we create a new object with the options inside it + this.options = _extends({}, Popper.Defaults, options); + + // init state + this.state = { + isDestroyed: false, + isCreated: false, + scrollParents: [] + }; + + // get reference and popper elements (allow jQuery wrappers) + this.reference = reference && reference.jquery ? reference[0] : reference; + this.popper = popper && popper.jquery ? popper[0] : popper; + + // Deep merge modifiers options + this.options.modifiers = {}; + Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) { + _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {}); + }); + + // Refactoring modifiers' list (Object => Array) + this.modifiers = Object.keys(this.options.modifiers).map(function (name) { + return _extends({ + name: name + }, _this.options.modifiers[name]); + }) + // sort the modifiers by order + .sort(function (a, b) { + return a.order - b.order; + }); + + // modifiers have the ability to execute arbitrary code when Popper.js get inited + // such code is executed in the same order of its modifier + // they could add new properties to their options configuration + // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`! + this.modifiers.forEach(function (modifierOptions) { + if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) { + modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state); + } + }); + + // fire the first update to position the popper in the right place + this.update(); + + var eventsEnabled = this.options.eventsEnabled; + if (eventsEnabled) { + // setup event listeners, they will take care of update the position in specific situations + this.enableEventListeners(); + } + + this.state.eventsEnabled = eventsEnabled; + } + + // We can't use class properties because they don't get listed in the + // class prototype and break stuff like Sinon stubs + + + createClass(Popper, [{ + key: 'update', + value: function update$$1() { + return update.call(this); + } + }, { + key: 'destroy', + value: function destroy$$1() { + return destroy.call(this); + } + }, { + key: 'enableEventListeners', + value: function enableEventListeners$$1() { + return enableEventListeners.call(this); + } + }, { + key: 'disableEventListeners', + value: function disableEventListeners$$1() { + return disableEventListeners.call(this); + } + + /** + * Schedules an update. It will run on the next UI update available. + * @method scheduleUpdate + * @memberof Popper + */ + + + /** + * Collection of utilities useful when writing custom modifiers. + * Starting from version 1.7, this method is available only if you + * include `popper-utils.js` before `popper.js`. + * + * **DEPRECATION**: This way to access PopperUtils is deprecated + * and will be removed in v2! Use the PopperUtils module directly instead. + * Due to the high instability of the methods contained in Utils, we can't + * guarantee them to follow semver. Use them at your own risk! + * @static + * @private + * @type {Object} + * @deprecated since version 1.8 + * @member Utils + * @memberof Popper + */ + + }]); + return Popper; + }(); + + /** + * The `referenceObject` is an object that provides an interface compatible with Popper.js + * and lets you use it as replacement of a real DOM node.
+ * You can use this method to position a popper relatively to a set of coordinates + * in case you don't have a DOM node to use as reference. + * + * ``` + * new Popper(referenceObject, popperNode); + * ``` + * + * NB: This feature isn't supported in Internet Explorer 10. + * @name referenceObject + * @property {Function} data.getBoundingClientRect + * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method. + * @property {number} data.clientWidth + * An ES6 getter that will return the width of the virtual reference element. + * @property {number} data.clientHeight + * An ES6 getter that will return the height of the virtual reference element. + */ + + + Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils; + Popper.placements = placements; + Popper.Defaults = Defaults; + + var clickOutMixin = { + data: function data() { + return { + listenForClickOut: false + }; + }, + watch: { + listenForClickOut: function listenForClickOut(newValue, oldValue) { + if (newValue !== oldValue) { + eventOff(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, false); + + if (newValue) { + eventOn(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, false); + } + } + } + }, + beforeCreate: function beforeCreate() { + // Declare non-reactive properties + this.clickOutElement = null; + this.clickOutEventName = null; + }, + mounted: function mounted() { + if (!this.clickOutElement) { + this.clickOutElement = document; + } + + if (!this.clickOutEventName) { + this.clickOutEventName = 'ontouchstart' in document.documentElement ? 'touchstart' : 'click'; + } + + if (this.listenForClickOut) { + eventOn(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, false); + } + }, + beforeDestroy: function beforeDestroy() { + eventOff(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, false); + }, + methods: { + isClickOut: function isClickOut(evt) { + return !contains(this.$el, evt.target); + }, + _clickOutHandler: function _clickOutHandler(evt) { + if (this.clickOutHandler && this.isClickOut(evt)) { + this.clickOutHandler(evt); + } + } + } + }; + + var focusInMixin = { + data: function data() { + return { + listenForFocusIn: false + }; + }, + watch: { + listenForFocusIn: function listenForFocusIn(newValue, oldValue) { + if (newValue !== oldValue) { + eventOff(this.focusInElement, 'focusin', this._focusInHandler, false); + + if (newValue) { + eventOn(this.focusInElement, 'focusin', this._focusInHandler, false); + } + } + } + }, + beforeCreate: function beforeCreate() { + // Declare non-reactive properties + this.focusInElement = null; + }, + mounted: function mounted() { + if (!this.focusInElement) { + this.focusInElement = document; + } + + if (this.listenForFocusIn) { + eventOn(this.focusInElement, 'focusin', this._focusInHandler, false); + } + }, + beforeDestroy: function beforeDestroy() { + eventOff(this.focusInElement, 'focusin', this._focusInHandler, false); + }, + methods: { + _focusInHandler: function _focusInHandler(evt) { + if (this.focusInHandler) { + this.focusInHandler(evt); + } + } + } + }; + + var BvEvent = + /*#__PURE__*/ + function () { + function BvEvent(type) { + var eventInit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, BvEvent); + + // Start by emulating native Event constructor. + if (!type) { + /* istanbul ignore next */ + throw new TypeError("Failed to construct '".concat(this.constructor.name, "'. 1 argument required, ").concat(arguments.length, " given.")); + } // Assign defaults first, the eventInit, + // and the type last so it can't be overwritten. + + + assign(this, BvEvent.defaults(), eventInit, { + type: type + }); // Freeze some props as readonly, but leave them enumerable. + + defineProperties(this, { + type: readonlyDescriptor(), + cancelable: readonlyDescriptor(), + nativeEvent: readonlyDescriptor(), + target: readonlyDescriptor(), + relatedTarget: readonlyDescriptor(), + vueTarget: readonlyDescriptor() + }); // Create a private variable using closure scoping. + + var defaultPrevented = false; // Recreate preventDefault method. One way setter. + + this.preventDefault = function preventDefault() { + if (this.cancelable) { + defaultPrevented = true; + } + }; // Create 'defaultPrevented' publicly accessible prop + // that can only be altered by the preventDefault method. + + + defineProperty(this, 'defaultPrevented', { + enumerable: true, + get: function get() { + return defaultPrevented; + } + }); + } + + _createClass(BvEvent, null, [{ + key: "defaults", + value: function defaults() { + return { + type: '', + cancelable: true, + nativeEvent: null, + target: null, + relatedTarget: null, + vueTarget: null + }; + } + }]); + + return BvEvent; + }(); + + /** + * Log a warning message to the console with bootstrap-vue formatting sugar. + * @param {string} message + */ + + /* istanbul ignore next */ + function warn(message) { + console.warn("[BootstrapVue warn]: ".concat(message)); + } + + function filterVisible(els) { + return (els || []).filter(isVisible); + } // Dropdown item CSS selectors + // TODO: .dropdown-form handling + + + var Selector = { + FORM_CHILD: '.dropdown form', + NAVBAR_NAV: '.navbar-nav', + ITEM_SELECTOR: '.dropdown-item:not(.disabled):not([disabled])' // Popper attachment positions + + }; + var AttachmentMap = { + // Dropup left align + TOP: 'top-start', + // Dropup right align + TOPEND: 'top-end', + // Dropdown left align + BOTTOM: 'bottom-start', + // Dropdown right align + BOTTOMEND: 'bottom-end', + // Dropright left align + RIGHT: 'right-start', + // Dropright right align + RIGHTEND: 'right-end', + // Dropleft left align + LEFT: 'left-start', + // Dropleft right align + LEFTEND: 'left-end' // @vue/component + + }; + var dropdownMixin = { + mixins: [clickOutMixin, focusInMixin], + provide: function provide() { + return { + bvDropdown: this + }; + }, + props: { + disabled: { + type: Boolean, + default: false + }, + text: { + // Button label + type: String, + default: '' + }, + html: { + // Button label + type: String + }, + dropup: { + // place on top if possible + type: Boolean, + default: false + }, + dropright: { + // place right if possible + type: Boolean, + default: false + }, + dropleft: { + // place left if possible + type: Boolean, + default: false + }, + right: { + // Right align menu (default is left align) + type: Boolean, + default: false + }, + offset: { + // Number of pixels to offset menu, or a CSS unit value (i.e. 1px, 1rem, etc) + type: [Number, String], + default: 0 + }, + noFlip: { + // Disable auto-flipping of menu from bottom<=>top + type: Boolean, + default: false + }, + popperOpts: { + // type: Object, + default: function _default() {} + } + }, + data: function data() { + return { + visible: false, + inNavbar: null, + visibleChangePrevented: false + }; + }, + computed: { + toggler: function toggler() { + var toggle = this.$refs.toggle; + return toggle ? toggle.$el || toggle : null; + } + }, + watch: { + visible: function visible(newValue, oldValue) { + if (this.visibleChangePrevented) { + this.visibleChangePrevented = false; + return; + } + + if (newValue !== oldValue) { + var evtName = newValue ? 'show' : 'hide'; + var bvEvt = new BvEvent(evtName, { + cancelable: true, + vueTarget: this, + target: this.$refs.menu, + relatedTarget: null + }); + this.emitEvent(bvEvt); + + if (bvEvt.defaultPrevented) { + // Reset value and exit if canceled + this.visibleChangePrevented = true; + this.visible = oldValue; // Just in case a child element triggereded this.hide(true) + + this.$off('hidden', this.focusToggler); + return; + } + + if (evtName === 'show') { + this.showMenu(); + } else { + this.hideMenu(); + } + } + }, + disabled: function disabled(newValue, oldValue) { + if (newValue !== oldValue && newValue && this.visible) { + // Hide dropdown if disabled changes to true + this.visible = false; + } + } + }, + created: function created() { + // Create non-reactive property + this._popper = null; + }, + deactivated: function deactivated() + /* istanbul ignore next: not easy to test */ + { + // In case we are inside a `` + this.visible = false; + this.whileOpenListen(false); + this.removePopper(); + }, + beforeDestroy: function beforeDestroy() + /* istanbul ignore next: not easy to test */ + { + this.visible = false; + this.whileOpenListen(false); + this.removePopper(); + }, + methods: { + // Event emitter + emitEvent: function emitEvent(bvEvt) { + var type = bvEvt.type; + this.$emit(type, bvEvt); + this.$root.$emit("bv::dropdown::".concat(type), bvEvt); + }, + showMenu: function showMenu() { + var _this = this; + + if (this.disabled) { + return; + } // Ensure other menus are closed + + + this.$root.$emit('bv::dropdown::shown', this); // Are we in a navbar ? + + if (this.inNavbar === null && this.isNav) { + this.inNavbar = Boolean(closest('.navbar', this.$el)); + } // Disable totally Popper.js for Dropdown in Navbar + + /* istanbul ignore next: cant test popper in JSDOM */ + + + if (!this.inNavbar) { + if (typeof Popper === 'undefined') { + warn('b-dropdown: Popper.js not found. Falling back to CSS positioning.'); + } else { + // for dropup with alignment we use the parent element as popper container + var element = this.dropup && this.right || this.split ? this.$el : this.$refs.toggle; // Make sure we have a reference to an element, not a component! + + element = element.$el || element; // Instantiate popper.js + + this.createPopper(element); + } + } + + this.whileOpenListen(true); // Wrap in nextTick to ensure menu is fully rendered/shown + + this.$nextTick(function () { + // Focus on the menu container on show + _this.focusMenu(); // Emit the shown event + + + _this.$emit('shown'); + }); + }, + hideMenu: function hideMenu() { + this.whileOpenListen(false); + this.$root.$emit('bv::dropdown::hidden', this); + this.$emit('hidden'); + this.removePopper(); + }, + createPopper: function createPopper(element) + /* istanbul ignore next: cant test popper in JSDOM */ + { + this.removePopper(); + this._popper = new Popper(element, this.$refs.menu, this.getPopperConfig()); + }, + removePopper: function removePopper() + /* istanbul ignore next: cant test popper in JSDOM */ + { + if (this._popper) { + // Ensure popper event listeners are removed cleanly + this._popper.destroy(); + } + + this._popper = null; + }, + getPopperConfig: function getPopperConfig() + /* istanbul ignore next: can't test popper in JSDOM */ + { + var placement = AttachmentMap.BOTTOM; + + if (this.dropup) { + placement = this.right ? AttachmentMap.TOPEND : AttachmentMap.TOP; + } else if (this.dropright) { + placement = AttachmentMap.RIGHT; + } else if (this.dropleft) { + placement = AttachmentMap.LEFT; + } else if (this.right) { + placement = AttachmentMap.BOTTOMEND; + } + + var popperConfig = { + placement: placement, + modifiers: { + offset: { + offset: this.offset || 0 + }, + flip: { + enabled: !this.noFlip + } + } + }; + + if (this.boundary) { + popperConfig.modifiers.preventOverflow = { + boundariesElement: this.boundary + }; + } + + return _objectSpread({}, popperConfig, this.popperOpts || {}); + }, + whileOpenListen: function whileOpenListen(open) { + // turn listeners on/off while open + if (open) { + // If another dropdown is opened + this.$root.$on('bv::dropdown::shown', this.rootCloseListener); // Hide the dropdown when clicked outside + + this.listenForClickOut = true; // Hide the dropdown when it loses focus + + this.listenForFocusIn = true; + } else { + this.$root.$off('bv::dropdown::shown', this.rootCloseListener); + this.listenForClickOut = false; + this.listenForFocusIn = false; + } + }, + rootCloseListener: function rootCloseListener(vm) { + if (vm !== this) { + this.visible = false; + } + }, + show: function show() { + // Public method to show dropdown + if (this.disabled) { + return; + } + + this.visible = true; + }, + hide: function hide() { + var refocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + // Public method to hide dropdown + if (this.disabled) { + return; + } + + this.visible = false; + + if (refocus) { + // Child element is closing the dropdown on click + this.$once('hidden', this.focusToggler); + } + }, + toggle: function toggle(evt) { + // Called only by a button that toggles the menu + evt = evt || {}; + var type = evt.type; + var key = evt.keyCode; + + if (type !== 'click' && !(type === 'keydown' && (key === KeyCodes.ENTER || key === KeyCodes.SPACE || key === KeyCodes.DOWN))) { + // We only toggle on Click, Enter, Space, and Arrow Down + return; + } + + if (this.disabled) { + this.visible = false; + return; + } + + this.$emit('toggle', evt); + + if (evt.defaultPrevented) { + // Exit if canceled + return; + } + + evt.preventDefault(); + evt.stopPropagation(); // Toggle visibility + + this.visible = !this.visible; + }, + click: function click(evt) { + // Called only in split button mode, for the split button + if (this.disabled) { + this.visible = false; + return; + } + + this.$emit('click', evt); + }, + onKeydown: function onKeydown(evt) + /* istanbul ignore next: not easy to test */ + { + // Called from dropdown menu context + var key = evt.keyCode; + + if (key === KeyCodes.ESC) { + // Close on ESC + this.onEsc(evt); + } else if (key === KeyCodes.TAB) { + // Close on tab out + this.onTab(evt); + } else if (key === KeyCodes.DOWN) { + // Down Arrow + this.focusNext(evt, false); + } else if (key === KeyCodes.UP) { + // Up Arrow + this.focusNext(evt, true); + } + }, + onEsc: function onEsc(evt) + /* istanbul ignore next: not easy to test */ + { + if (this.visible) { + this.visible = false; + evt.preventDefault(); + evt.stopPropagation(); // Return focus to original trigger button + + this.$once('hidden', this.focusToggler); + } + }, + onTab: function onTab(evt) + /* istanbul ignore next: not easy to test */ + {// TODO: Need special handler for dealing with form inputs + // Tab, if in a text-like input, we should just focus next item in the dropdown + // Note: Inputs are in a special .dropdown-form container + }, + onMouseOver: function onMouseOver(evt) + /* istanbul ignore next: not easy to test */ + {// Removed mouseover focus handler + }, + // Document click out listener + clickOutHandler: function clickOutHandler() { + if (this.visible) { + this.visible = false; + } + }, + // Document focusin listener + focusInHandler: function focusInHandler(evt) { + // If focus leaves dropdown, hide it + if (this.visible && !contains(this.$refs.menu, evt.target) && !contains(this.$refs.toggle, evt.target)) { + this.visible = false; + } + }, + // Keyboard nav + focusNext: function focusNext(evt, up) { + var _this2 = this; + + if (!this.visible) { + return; + } + + evt.preventDefault(); + evt.stopPropagation(); + this.$nextTick(function () { + var items = _this2.getItems(); + + if (items.length < 1) { + return; + } + + var index = items.indexOf(evt.target); + + if (up && index > 0) { + index--; + } else if (!up && index < items.length - 1) { + index++; + } + + if (index < 0) { + index = 0; + } + + _this2.focusItem(index, items); + }); + }, + focusItem: function focusItem(idx, items) { + var el = items.find(function (el, i) { + return i === idx; + }); + + if (el && getAttr(el, 'tabindex') !== '-1') { + el.focus(); + } + }, + getItems: function getItems() { + // Get all items + return filterVisible(selectAll(Selector.ITEM_SELECTOR, this.$refs.menu)); + }, + focusMenu: function focusMenu() { + this.$refs.menu.focus && this.$refs.menu.focus(); + }, + focusToggler: function focusToggler() { + var toggler = this.toggler; + + if (toggler && toggler.focus) { + toggler.focus(); + } + } + } + }; + + var BDropdown = { + name: 'BDropdown', + components: { + BButton: BButton + }, + mixins: [idMixin, dropdownMixin], + props: { + toggleText: { + type: String, + default: 'Toggle Dropdown' + }, + size: { + type: String, + default: null + }, + variant: { + type: String, + default: null + }, + menuClass: { + type: [String, Array], + default: null + }, + toggleTag: { + type: String, + default: 'button' + }, + toggleClass: { + type: [String, Array], + default: null + }, + noCaret: { + type: Boolean, + default: false + }, + split: { + type: Boolean, + default: false + }, + splitHref: { + type: String // default: undefined + + }, + splitTo: { + type: [String, Object] // default: undefined + + }, + splitVariant: { + type: String, + default: null + }, + role: { + type: String, + default: 'menu' + }, + boundary: { + // String: `scrollParent`, `window` or `viewport` + // Object: HTML Element reference + type: [String, Object], + default: 'scrollParent' + } + }, + computed: { + dropdownClasses: function dropdownClasses() { + // Position `static` is needed to allow menu to "breakout" of the scrollParent boundaries + // when boundary is anything other than `scrollParent` + // See https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786 + var positionStatic = this.boundary !== 'scrollParent' || !this.boundary; + var direction = ''; + + if (this.dropup) { + direction = 'dropup'; + } else if (this.dropright) { + direction = 'dropright'; + } else if (this.dropleft) { + direction = 'dropleft'; + } + + return ['btn-group', 'b-dropdown', 'dropdown', direction, { + show: this.visible, + 'position-static': positionStatic + }]; + }, + menuClasses: function menuClasses() { + return ['dropdown-menu', { + 'dropdown-menu-right': this.right, + show: this.visible + }, this.menuClass]; + }, + toggleClasses: function toggleClasses() { + return ['dropdown-toggle', { + 'dropdown-toggle-split': this.split, + 'dropdown-toggle-no-caret': this.noCaret && !this.split + }, this.toggleClass]; + } + }, + render: function render(h) { + var split = h(false); + + if (this.split) { + var btnProps = { + disabled: this.disabled, + variant: this.splitVariant || this.variant, + size: this.size // We add these as needed due to router-link issues with defined property with undefined/null values + + }; + + if (this.splitTo) { + btnProps.to = this.splitTo; + } + + if (this.splitHref) { + btnProps.href = this.splitHref; + } + + split = h('b-button', { + ref: 'button', + props: btnProps, + attrs: { + id: this.safeId('_BV_button_') + }, + on: { + click: this.click + } + }, [this.$slots['button-content'] || this.$slots.text || this.html || stripTags(this.text)]); + } + + var toggle = h('b-button', { + ref: 'toggle', + class: this.toggleClasses, + props: { + variant: this.variant, + size: this.size, + disabled: this.disabled, + tag: this.toggleTag + }, + attrs: { + id: this.safeId('_BV_toggle_'), + 'aria-haspopup': 'true', + 'aria-expanded': this.visible ? 'true' : 'false' + }, + on: { + click: this.toggle, + // click + keydown: this.toggle // enter, space, down + + } + }, [this.split ? h('span', { + class: ['sr-only'] + }, [this.toggleText]) : this.$slots['button-content'] || this.$slots.text || this.html || stripTags(this.text)]); + var menu = h('div', { + ref: 'menu', + class: this.menuClasses, + attrs: { + role: this.role, + tabindex: '-1', + 'aria-labelledby': this.safeId(this.split ? '_BV_button_' : '_BV_toggle_') + }, + on: { + mouseover: this.onMouseOver, + keydown: this.onKeydown // tab, up, down, esc + + } + }, [this.$slots.default]); + return h('div', { + attrs: { + id: this.safeId() + }, + class: this.dropdownClasses + }, [split, toggle, menu]); + } + }; + + var props$n = propsFactory(); // @vue/component + + var BDropdownItem = { + name: 'BDropdownItem', + inject: { + bvDropdown: { + default: null + } + }, + props: props$n, + methods: { + closeDropdown: function closeDropdown() { + if (this.bvDropdown) { + this.bvDropdown.hide(true); + } + }, + onClick: function onClick(evt) { + this.$emit('click', evt); + this.closeDropdown(); + } + }, + render: function render(h) { + return h(BLink, { + props: this.$props, + staticClass: 'dropdown-item', + attrs: { + role: 'menuitem' + }, + on: { + click: this.onClick + } + }, this.$slots.default); + } + }; + + var props$o = { + active: { + type: Boolean, + default: false + }, + activeClass: { + type: String, + default: 'active' + }, + disabled: { + type: Boolean, + default: false + } // @vue/component + + }; + var BDropdownItemButton = { + name: 'BDropdownItemButton', + inject: { + bvDropdown: { + default: null + } + }, + props: props$o, + methods: { + closeDropdown: function closeDropdown() { + if (this.bvDropdown) { + this.bvDropdown.hide(true); + } + }, + onClick: function onClick(evt) { + this.$emit('click', evt); + this.closeDropdown(); + } + }, + render: function render(h) { + return h('button', { + staticClass: 'dropdown-item', + class: _defineProperty({}, this.activeClass, this.active), + attrs: { + role: 'menuitem', + type: 'button', + disabled: this.disabled + }, + on: { + click: this.onClick + } + }, this.$slots.default); + } + }; + + var props$p = { + id: { + type: String, + default: null + }, + tag: { + type: String, + default: 'h6' + } // @vue/component + + }; + var BDropdownHeader = { + name: 'BDropdownHeader', + functional: true, + props: props$p, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'dropdown-header', + attrs: { + id: props.id || null + } + }), children); + } + }; + + var props$q = { + tag: { + type: String, + default: 'div' + } // @vue/component + + }; + var BDropdownDivider = { + name: 'BDropdownDivider', + functional: true, + props: props$q, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data; + return h(props.tag, mergeData(data, { + staticClass: 'dropdown-divider', + attrs: { + role: 'separator' + } + })); + } + }; + + var props$r = { + id: { + type: String, + default: null + }, + inline: { + type: Boolean, + default: false + }, + novalidate: { + type: Boolean, + default: false + }, + validated: { + type: Boolean, + default: false + } // @vue/component + + }; + var BForm = { + name: 'BForm', + functional: true, + props: props$r, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h('form', mergeData(data, { + class: { + 'form-inline': props.inline, + 'was-validated': props.validated + }, + attrs: { + id: props.id, + novalidate: props.novalidate + } + }), children); + } + }; + + var BDropdownForm = { + name: 'BDropdownForm', + functional: true, + props: _objectSpread({}, props$r), + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(BForm, mergeData(data, { + props: props, + staticClass: 'b-dropdown-form' + }), children); + } + }; + + var BDropdownText = { + name: 'BDropdownText', + functional: true, + props: { + tag: { + type: String, + default: 'p' + } + }, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + props: props, + staticClass: 'b-dropdown-text' + }), children); + } + }; + + var components$b = { + BDropdown: BDropdown, + BDd: BDropdown, + BDropdownItem: BDropdownItem, + BDdItem: BDropdownItem, + BDropdownItemButton: BDropdownItemButton, + BDropdownItemBtn: BDropdownItemButton, + BDdItemButton: BDropdownItemButton, + BDdItemBtn: BDropdownItemButton, + BDropdownHeader: BDropdownHeader, + BDdHeader: BDropdownHeader, + BDropdownDivider: BDropdownDivider, + BDdDivider: BDropdownDivider, + BDropdownForm: BDropdownForm, + BDdForm: BDropdownForm, + BDropdownText: BDropdownText, + BDdText: BDropdownText + }; + var dropdownPlugin = { + install: function install(Vue) { + registerComponents(Vue, components$b); + } + }; + + var props$s = { + type: { + type: String, + default: 'iframe', + validator: function validator(str) { + return arrayIncludes(['iframe', 'embed', 'video', 'object', 'img', 'b-img', 'b-img-lazy'], str); + } + }, + tag: { + type: String, + default: 'div' + }, + aspect: { + type: String, + default: '16by9' + } // @vue/component + + }; + var BEmbed = { + name: 'BEmbed', + functional: true, + props: props$s, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, { + ref: data.ref, + staticClass: 'embed-responsive', + class: _defineProperty({}, "embed-responsive-".concat(props.aspect), Boolean(props.aspect)) + }, [h(props.type, mergeData(data, { + ref: '', + staticClass: 'embed-responsive-item' + }), children)]); + } + }; + + var components$c = { + BEmbed: BEmbed + }; + var index$a = { + install: function install(Vue) { + registerComponents(Vue, components$c); + } + }; + + var props$t = { + id: { + type: String, + default: null + }, + tag: { + type: String, + default: 'small' + }, + textVariant: { + type: String, + default: 'muted' + }, + inline: { + type: Boolean, + default: false + } // @vue/component + + }; + var BFormText = { + name: 'BFormText', + functional: true, + props: props$t, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + class: _defineProperty({ + 'form-text': !props.inline + }, "text-".concat(props.textVariant), Boolean(props.textVariant)), + attrs: { + id: props.id + } + }), children); + } + }; + + var props$u = { + id: { + type: String, + default: null + }, + tag: { + type: String, + default: 'div' + }, + tooltip: { + type: Boolean, + default: false + }, + forceShow: { + type: Boolean, + default: false + }, + state: { + type: [Boolean, String], + default: null + } // @vue/component + + }; + var BFormInvalidFeedback = { + name: 'BFormInvalidFeedback', + functional: true, + props: props$u, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var show = props.forceShow === true || props.state === false || props.state === 'invalid'; + return h(props.tag, mergeData(data, { + class: { + 'invalid-feedback': !props.tooltip, + 'invalid-tooltip': props.tooltip, + 'd-block': show + }, + attrs: { + id: props.id + } + }), children); + } + }; + + var props$v = { + id: { + type: String, + default: null + }, + tag: { + type: String, + default: 'div' + }, + tooltip: { + type: Boolean, + default: false + }, + forceShow: { + type: Boolean, + default: false + }, + state: { + type: [Boolean, String], + default: null + } // @vue/component + + }; + var BFormValidFeedback = { + name: 'BFormValidFeedback', + functional: true, + props: props$v, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var show = props.forceShow === true || props.state === true || props.state === 'valid'; + return h(props.tag, mergeData(data, { + class: { + 'valid-feedback': !props.tooltip, + 'valid-tooltip': props.tooltip, + 'd-block': show + }, + attrs: { + id: props.id + } + }), children); + } + }; + + var components$d = { + BForm: BForm, + BFormRow: BFormRow, + BFormText: BFormText, + BFormInvalidFeedback: BFormInvalidFeedback, + BFormFeedback: BFormInvalidFeedback, + BFormValidFeedback: BFormValidFeedback + }; + var index$b = { + install: function install(Vue) { + registerComponents(Vue, components$d); + } + }; + + /* Form control contextual state class computation + * + * Returned class is either 'is-valid' or 'is-invalid' based on the 'state' prop + * state can be one of five values: + * - true or 'valid' for is-valid + * - false or 'invalid' for is-invalid + * - null (or empty string) for no contextual state + */ + // @vue/component + var formStateMixin = { + props: { + state: { + // true/'valid', false/'invalid', '',null + // The order must be String first, then Boolean! + type: [String, Boolean], + default: null + } + }, + computed: { + computedState: function computedState() { + var state = this.state; + + if (state === '') { + return null; + } else if (state === true || state === 'valid') { + return true; + } else if (state === false || state === 'invalid') { + return false; + } + + return null; + }, + stateClass: function stateClass() { + var state = this.computedState; + + if (state === true) { + return 'is-valid'; + } else if (state === false) { + return 'is-invalid'; + } + + return null; + } + } + }; + + var SELECTOR = 'input:not(:disabled),textarea:not(:disabled),select:not(:disabled)'; // Breakpoint names for label-cols and label-align props + + var BREAKPOINTS$1 = ['', 'sm', 'md', 'lg', 'xl']; // Memoize this function to return cached values to save time in computed functions + + var makePropName = memoize(function () { + var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + var prefix = arguments.length > 1 ? arguments[1] : undefined; + return "".concat(prefix).concat(upperFirst(breakpoint)); + }); // Generate the labelCol breakpoint props + + var bpLabelColProps = BREAKPOINTS$1.reduce(function (props, breakpoint) { + // label-cols, label-cols-sm, label-cols-md, ... + props[makePropName(breakpoint, 'labelCols')] = { + type: [Number, String, Boolean], + default: breakpoint ? false : null + }; + return props; + }, create(null)); // Generate the labelAlign breakpoint props + + var bpLabelAlignProps = BREAKPOINTS$1.reduce(function (props, breakpoint) { + // label-align, label-align-sm, label-align-md, ... + props[makePropName(breakpoint, 'labelAlign')] = { + type: String, + // left, right, center + default: null + }; + return props; + }, create(null)); // render helper functions (here rather than polluting the instance with more methods) + + function renderInvalidFeedback(h, ctx) { + var content = ctx.$slots['invalid-feedback'] || ctx.invalidFeedback; + var invalidFeedback = h(false); + + if (content) { + invalidFeedback = h('b-form-invalid-feedback', { + props: { + id: ctx.invalidFeedbackId, + // If state is explicitly false, always show the feedback + state: ctx.computedState, + tooltip: ctx.tooltip + }, + attrs: { + tabindex: content ? '-1' : null, + role: 'alert', + 'aria-live': 'assertive', + 'aria-atomic': 'true' + } + }, [content]); + } + + return invalidFeedback; + } + + function renderValidFeedback(h, ctx) { + var content = ctx.$slots['valid-feedback'] || ctx.validFeedback; + var validFeedback = h(false); + + if (content) { + validFeedback = h('b-form-valid-feedback', { + props: { + id: ctx.validFeedbackId, + // If state is explicitly true, always show the feedback + state: ctx.computedState, + tooltip: ctx.tooltip + }, + attrs: { + tabindex: '-1', + role: 'alert', + 'aria-live': 'assertive', + 'aria-atomic': 'true' + } + }, [content]); + } + + return validFeedback; + } + + function renderHelpText(h, ctx) { + // Form help text (description) + var content = ctx.$slots['description'] || ctx.description; + var description = h(false); + + if (content) { + description = h('b-form-text', { + attrs: { + id: ctx.descriptionId, + tabindex: '-1' + } + }, [content]); + } + + return description; + } + + function renderLabel(h, ctx) { + // render label/legend inside b-col if necessary + var content = ctx.$slots['label'] || ctx.label; + var labelFor = ctx.labelFor; + var isLegend = !labelFor; + var isHorizontal = ctx.isHorizontal; + var labelTag = isLegend ? 'legend' : 'label'; + + if (!content && !isHorizontal) { + return h(false); + } else if (ctx.labelSrOnly) { + var label = h(false); + + if (content) { + label = h(labelTag, { + class: 'sr-only', + attrs: { + id: ctx.labelId, + for: labelFor || null + } + }, [content]); + } + + return h(isHorizontal ? 'b-col' : 'div', { + props: isHorizontal ? ctx.labelColProps : {} + }, [label]); + } else { + return h(isHorizontal ? 'b-col' : labelTag, { + on: isLegend ? { + click: ctx.legendClick + } : {}, + props: isHorizontal ? _objectSpread({ + tag: labelTag + }, ctx.labelColProps) : {}, + attrs: { + id: ctx.labelId, + for: labelFor || null, + // We add a tab index to legend so that screen readers will properly read the aria-labelledby in IE. + tabindex: isLegend ? '-1' : null + }, + class: [// When horizontal or if a legend is rendered, add col-form-label for correct sizing + // as Bootstrap has inconsitent font styling for legend in non-horiontal form-groups. + // See: https://github.com/twbs/bootstrap/issues/27805 + isHorizontal || isLegend ? 'col-form-label' : '', // Emulate label padding top of 0 on legend when not horizontal + !isHorizontal && isLegend ? 'pt-0' : '', // If not horizontal and not a legend, we add d-block to label so that label-align works + !isHorizontal && !isLegend ? 'd-block' : '', ctx.labelSize ? "col-form-label-".concat(ctx.labelSize) : '', ctx.labelAlignClasses, ctx.labelClass] + }, [content]); + } + } // bFormGroup + // @vue/component + + + var BFormGroup = { + name: 'BFormGroup', + components: { + BFormRow: BFormRow, + BCol: BCol, + BFormInvalidFeedback: BFormInvalidFeedback, + BFormValidFeedback: BFormValidFeedback, + BFormText: BFormText + }, + mixins: [idMixin, formStateMixin], + props: _objectSpread({ + label: { + type: String, + default: null + }, + labelFor: { + type: String, + default: null + }, + labelSize: { + type: String, + default: null + }, + labelSrOnly: { + type: Boolean, + default: false + }, + labelClass: { + type: [String, Array, Object], + default: null + }, + description: { + type: String, + default: null + }, + invalidFeedback: { + type: String, + default: null + }, + validFeedback: { + type: String, + default: null + }, + tooltip: { + // Enable tooltip style feedback + type: Boolean, + default: false + }, + validated: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + } + }, bpLabelColProps, bpLabelAlignProps, { + horizontal: { + // Deprecated + type: Boolean, + default: false + }, + breakpoint: { + // Deprecated (ignored if horizontal is not true) + type: String, + default: null // legacy value 'sm' + + } + }), + computed: { + labelColProps: function labelColProps() { + var _this = this; + + var props = {}; + + if (this.horizontal) { + // Deprecated setting of horizontal/breakpoint props + warn("b-form-group: Props 'horizontal' and 'breakpoint' are deprecated. Use 'label-cols(-{breakpoint})' props instead."); // Legacy default is breakpoint sm and cols 3 + + var bp = this.breakpoint || 'sm'; + var cols = parseInt(this.labelCols, 10) || 3; + props[bp] = cols > 0 ? cols : 3; // We then return the single breakpoint prop for legacy compatability + + return props; + } + + BREAKPOINTS$1.forEach(function (breakpoint) { + // Grab the value if the label column breakpoint prop + var propVal = _this[makePropName(breakpoint, 'labelCols')]; // Handle case where the prop's value is an empty string, which represents true + + + propVal = propVal === '' ? true : propVal || false; + + if (typeof propVal !== 'boolean') { + // Convert to column size to number + propVal = parseInt(propVal, 10) || 0; // Ensure column size is greater than 0 + + propVal = propVal > 0 ? propVal : false; + } + + if (propVal) { + // Add the prop to the list of props to give to b-col. + // if breakpoint is '' (labelCols=true), then we use the col prop to make equal width at xs + var bColPropName = breakpoint || (typeof propVal === 'boolean' ? 'col' : 'cols'); // Add it to the props + + props[bColPropName] = propVal; + } + }); + return props; + }, + labelAlignClasses: function labelAlignClasses() { + var _this2 = this; + + var classes = []; + BREAKPOINTS$1.forEach(function (breakpoint) { + // assemble the label column breakpoint align classes + var propVal = _this2[makePropName(breakpoint, 'labelAlign')] || null; + + if (propVal) { + var className = breakpoint ? "text-".concat(breakpoint, "-").concat(propVal) : "text-".concat(propVal); + classes.push(className); + } + }); + return classes; + }, + isHorizontal: function isHorizontal() { + // Determine if the resultant form-group will be rendered + // horizontal (meaning it has label-col breakpoints) + return keys(this.labelColProps).length > 0; + }, + labelId: function labelId() { + return this.$slots['label'] || this.label ? this.safeId('_BV_label_') : null; + }, + descriptionId: function descriptionId() { + return this.$slots['description'] || this.description ? this.safeId('_BV_description_') : null; + }, + hasInvalidFeedback: function hasInvalidFeedback() { + // used for computing aria-describedby + var $slots = this.$slots; + return this.computedState === false && ($slots['invalid-feedback'] || this.invalidFeedback); + }, + invalidFeedbackId: function invalidFeedbackId() { + return this.hasInvalidFeedback ? this.safeId('_BV_feedback_invalid_') : null; + }, + hasValidFeedback: function hasValidFeedback() { + // used for computing aria-describedby + return this.computedState === true && (this.$slots['valid-feedback'] || this.validFeedback); + }, + validFeedbackId: function validFeedbackId() { + return this.hasValidFeedback ? this.safeId('_BV_feedback_valid_') : null; + }, + describedByIds: function describedByIds() { + // Screen readers will read out any content linked to by aria-describedby + // even if the content is hidden with 'display: none', hence we only include + // feedback IDs if the form-group's state is explicitly valid or invalid. + return [this.descriptionId, this.invalidFeedbackId, this.validFeedbackId].filter(function (i) { + return i; + }).join(' ') || null; + } + }, + watch: { + describedByIds: function describedByIds(add, remove) { + if (add !== remove) { + this.setInputDescribedBy(add, remove); + } + } + }, + mounted: function mounted() { + var _this3 = this; + + this.$nextTick(function () { + // Set the adia-describedby IDs on the input specified by label-for + // We do this in a nextTick to ensure the children have finished rendering + _this3.setInputDescribedBy(_this3.describedByIds); + }); + }, + methods: { + legendClick: function legendClick(evt) { + if (this.labelFor) { + // don't do anything if labelFor is set + return; + } + + var tagName = evt.target ? evt.target.tagName : ''; + + if (/^(input|select|textarea|label|button|a)$/i.test(tagName)) { + // If clicked an interactive element inside legend, we just let the default happen + return; + } + + var inputs = selectAll(SELECTOR, this.$refs.content).filter(isVisible); + + if (inputs && inputs.length === 1 && inputs[0].focus) { + // if only a single input, focus it, emulating label behaviour + inputs[0].focus(); + } + }, + setInputDescribedBy: function setInputDescribedBy(add, remove) { + // Sets the `aria-describedby` attribute on the input if label-for is set. + // Optionally accepts a string of IDs to remove as the second parameter + if (this.labelFor && typeof document !== 'undefined') { + var input = select("#".concat(this.labelFor), this.$refs.content); + + if (input) { + var adb = 'aria-describedby'; + var ids = (getAttr(input, adb) || '').split(/\s+/); + remove = (remove || '').split(/\s+/); // Update ID list, preserving any original IDs + + ids = ids.filter(function (id) { + return !arrayIncludes(remove, id); + }).concat(add || '').join(' ').trim(); + + if (ids) { + setAttr(input, adb, ids); + } else { + // No IDs, so remove the attribute + removeAttr(input, adb); + } + } + } + } + }, + render: function render(h) { + var isFieldset = !this.labelFor; + var isHorizontal = this.isHorizontal; // Generate the label + + var label = renderLabel(h, this); // Generate the content + + var content = h(isHorizontal ? 'b-col' : 'div', { + ref: 'content', + attrs: { + tabindex: isFieldset ? '-1' : null, + role: isFieldset ? 'group' : null, + 'aria-labelledby': isFieldset ? this.labelId : null, + 'aria-describedby': isFieldset ? this.ariaDescribedBy : null + } + }, [this.$slots['default'] || h(false), renderInvalidFeedback(h, this), renderValidFeedback(h, this), renderHelpText(h, this)]); // Create the form-group + + var data = { + staticClass: 'form-group', + class: [this.validated ? 'was-validated' : null, this.stateClass], + attrs: { + id: this.safeId(), + disabled: isFieldset ? this.disabled : null, + role: isFieldset ? null : 'group', + 'aria-invalid': this.computedState === false ? 'true' : null, + 'aria-labelledby': this.labelId || null, + 'aria-describedby': this.describedByIds || null + } // Return it wrapped in a form-group. + // Note: fieldsets do not support adding `row` or `form-row` directly to them + // due to browser specific render issues, so we move the form-row to an + // inner wrapper div when horizontal and using a fieldset + + }; + return h(isFieldset ? 'fieldset' : isHorizontal ? 'b-form-row' : 'div', data, isHorizontal && isFieldset ? [h('b-form-row', {}, [label, content])] : [label, content]); + } + }; + + var components$e = { + BFormGroup: BFormGroup, + BFormFieldset: BFormGroup + }; + var index$c = { + install: function install(Vue) { + registerComponents(Vue, components$e); + } + }; + + // @vue/component + var formRadioCheckMixin = { + model: { + prop: 'checked', + event: 'input' + }, + props: { + value: {// Value when checked + // type: Object, + // default: undefined + }, + checked: {// This is the v-model + // type: Object, + // default: undefined + }, + inline: { + type: Boolean, + default: false + }, + plain: { + type: Boolean, + default: false + }, + button: { + // Only applicable in standalone mode (non group) + type: Boolean, + default: false + }, + buttonVariant: { + // Only applicable when rendered with button style + type: String, + default: null + } + }, + data: function data() { + return { + localChecked: this.is_Group ? this.bvGroup.checked : this.checked, + hasFocus: false + }; + }, + computed: { + computedLocalChecked: { + get: function get() { + return this.is_Group ? this.bvGroup.localChecked : this.localChecked; + }, + set: function set(val) { + if (this.is_Group) { + this.bvGroup.localChecked = val; + } else { + this.localChecked = val; + } + } + }, + is_Group: function is_Group() { + // Is this check/radio a child of check-group or radio-group? + return Boolean(this.bvGroup); + }, + is_BtnMode: function is_BtnMode() { + // Support button style in single input mode + return this.is_Group ? this.bvGroup.buttons : this.button; + }, + is_Plain: function is_Plain() { + return this.is_BtnMode ? false : this.is_Group ? this.bvGroup.plain : this.plain; + }, + is_Custom: function is_Custom() { + return this.is_BtnMode ? false : !this.is_Plain; + }, + is_Switch: function is_Switch() { + // Custom switch styling (checkboxes only) + return this.is_BtnMode || this.is_Radio || this.is_Plain ? false : this.is_Group ? this.bvGroup.switches : this.switch; + }, + is_Inline: function is_Inline() { + return this.is_Group ? this.bvGroup.inline : this.inline; + }, + is_Disabled: function is_Disabled() { + // Child can be disabled while parent isn't, but is always disabled if group is + return this.is_Group ? this.bvGroup.disabled || this.disabled : this.disabled; + }, + is_Required: function is_Required() { + // Required only works when a name is provided for the input(s) + // Child can only be required when parent is + // Groups will always have a name (either user supplied or auto generated) + return Boolean(this.get_Name && (this.bvGroup.is_Group ? this.bvGroup.required : this.required)); + }, + get_Name: function get_Name() { + // Group name preferred over local name + return (this.is_Group ? this.bvGroup.groupName : this.name) || null; + }, + get_Form: function get_Form() { + return (this.is_Group ? this.bvGroup.form : this.form) || null; + }, + get_Size: function get_Size() { + return (this.is_Group ? this.bvGroup.size : this.size) || ''; + }, + get_State: function get_State() { + return this.is_Group ? this.bvGroup.computedState : this.computedState; + }, + get_ButtonVariant: function get_ButtonVariant() { + // Local variant preferred over group variant + if (this.buttonVariant) { + return this.buttonVariant; + } else if (this.is_Group && this.bvGroup.buttonVariant) { + return this.bvGroup.buttonVariant; + } // default variant + + + return 'secondary'; + }, + buttonClasses: function buttonClasses() { + // Same for radio & check + return ['btn', "btn-".concat(this.get_ButtonVariant), this.get_Size ? "btn-".concat(this.get_Size) : '', // 'disabled' class makes "button" look disabled + this.is_Disabled ? 'disabled' : '', // 'active' class makes "button" look pressed + this.is_Checked ? 'active' : '', // Focus class makes button look focused + this.hasFocus ? 'focus' : '']; + } + }, + watch: { + checked: function checked(newVal, oldVal) { + this.computedLocalChecked = newVal; + } + }, + methods: { + handleFocus: function handleFocus(evt) { + // When in buttons mode, we need to add 'focus' class to label when input focused + // As it is the hidden input which has actual focus + if (evt.target) { + if (evt.type === 'focus') { + this.hasFocus = true; + } else if (evt.type === 'blur') { + this.hasFocus = false; + } + } + }, + // Convenience methods for focusing the input + focus: function focus() { + if (!this.is_Disabled && this.$refs.input && this.$refs.input.focus) { + this.$refs.input.focus(); + } + }, + blur: function blur() { + if (!this.is_Disabled && this.$refs.input && this.$refs.input.blur) { + this.$refs.input.blur(); + } + } + }, + render: function render(h) { + var defaultSlot = this.$slots.default; // Generate the input element + + var on = { + change: this.handleChange + }; + + if (this.is_BtnMode) { + // Handlers for focus styling when in button mode + on.focus = on.blur = this.handleFocus; + } + + var input = h('input', { + ref: 'input', + key: 'input', + on: on, + class: { + 'form-check-input': this.is_Plain, + 'custom-control-input': this.is_Custom, + 'is-valid': this.get_State === true && !this.is_BtnMode, + 'is-invalid': this.get_State === false && !this.is_BtnMode + }, + directives: [{ + name: 'model', + rawName: 'v-model', + value: this.computedLocalChecked, + expression: 'computedLocalChecked' + }], + attrs: { + id: this.safeId(), + type: this.is_Radio ? 'radio' : 'checkbox', + name: this.get_Name, + form: this.get_Form, + disabled: this.is_Disabled, + required: this.is_Required, + autocomplete: 'off', + 'aria-required': this.is_Required || null + }, + domProps: { + value: this.value, + checked: this.is_Checked + } + }); + + if (this.is_BtnMode) { + // Button mode + var button = h('label', { + class: this.buttonClasses + }, [input, defaultSlot]); + + if (!this.is_Group) { + // Standalone button mode, so wrap in 'btn-group-toggle' + // and flag it as inline-block to mimic regular buttons + button = h('div', { + class: ['btn-group-toggle', 'd-inline-block'] + }, [button]); + } + + return button; + } else { + // Not button mode + var label = h('label', { + class: { + 'form-check-label': this.is_Plain, + 'custom-control-label': this.is_Custom + }, + attrs: { + for: this.safeId() + } + }, defaultSlot); // Wrap it in a div + + return h('div', { + class: _defineProperty({ + 'form-check': this.is_Plain, + 'form-check-inline': this.is_Plain && this.is_Inline, + 'custom-control': this.is_Custom, + 'custom-control-inline': this.is_Custom && this.is_Inline, + 'custom-checkbox': this.is_Custom && this.is_Check && !this.is_Switch, + 'custom-switch': this.is_Switch, + 'custom-radio': this.is_Custom && this.is_Radio + }, "form-control-".concat(this.get_Size), Boolean(this.get_Size && !this.is_BtnMode)) + }, [input, label]); + } + } + }; + + // @vue/component + var formMixin = { + props: { + name: { + type: String // default: undefined + + }, + id: { + type: String // default: undefined + + }, + disabled: { + type: Boolean + }, + required: { + type: Boolean, + default: false + }, + form: { + type: String, + default: null + } + } + }; + + // @vue/component + var formSizeMixin = { + props: { + size: { + type: String, + default: null + } + }, + computed: { + sizeFormClass: function sizeFormClass() { + return [this.size ? "form-control-".concat(this.size) : null]; + }, + sizeBtnClass: function sizeBtnClass() { + return [this.size ? "btn-".concat(this.size) : null]; + } + } + }; + + function isDate(obj) { + return obj instanceof Date; + } + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + * Returns boolean true or false + */ + + + function looseEqual(a, b) { + if (a === b) { + return true; + } + + var aValidType = isDate(a); + var bValidType = isDate(b); + + if (aValidType || bValidType) { + return aValidType && bValidType ? a.getTime() === b.getTime() : false; + } + + aValidType = isArray(a); + bValidType = isArray(b); + + if (aValidType || bValidType) { + return aValidType && bValidType ? a.length === b.length && a.every(function (e, i) { + return looseEqual(e, b[i]); + }) : false; + } + + aValidType = isObject(a); + bValidType = isObject(b); + + if (aValidType || bValidType) { + /* istanbul ignore if: this if will probably never be called */ + if (!aValidType || !bValidType) { + return false; + } + + var aKeysCount = keys(a).length; + var bKeysCount = keys(b).length; + + if (aKeysCount !== bKeysCount) { + return false; + } + + for (var key in a) { + var aHasKey = a.hasOwnProperty(key); + var bHasKey = b.hasOwnProperty(key); + + if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) { + return false; + } + } + } + + return String(a) === String(b); + } + + function looseIndexOf (arr, val) { + // Assumes that the first argument is an array + for (var i = 0; i < arr.length; i++) { + if (looseEqual(arr[i], val)) { + return i; + } + } + + return -1; + } + + var BFormCheckbox = { + name: 'BFormCheckbox', + mixins: [formRadioCheckMixin, // Includes shared render function + idMixin, formMixin, formSizeMixin, formStateMixin], + inject: { + bvGroup: { + from: 'bvCheckGroup', + default: false + } + }, + props: { + value: { + // type: [Object, Boolean], + default: true + }, + uncheckedValue: { + // type: [Object, Boolean], + // Not applicable in multi-check mode + default: false + }, + indeterminate: { + // Not applicable in multi-check mode + type: Boolean, + default: false + }, + switch: { + // Custom switch styling + type: Boolean, + default: false + }, + checked: { + // v-model + type: [String, Number, Object, Array, Boolean], + default: null + } + }, + computed: { + is_Checked: function is_Checked() { + var checked = this.computedLocalChecked; + var value = this.value; + + if (isArray(checked)) { + return looseIndexOf(checked, value) > -1; + } else { + return looseEqual(checked, value); + } + }, + is_Radio: function is_Radio() { + return false; + }, + is_Check: function is_Check() { + return true; + } + }, + watch: { + computedLocalChecked: function computedLocalChecked(newVal, oldVal) { + this.$emit('input', newVal); + + if (this.$refs && this.$refs.input) { + this.$emit('update:indeterminate', this.$refs.input.indeterminate); + } + }, + indeterminate: function indeterminate(newVal, oldVal) { + this.setIndeterminate(newVal); + } + }, + mounted: function mounted() { + // Set initial indeterminate state + this.setIndeterminate(this.indeterminate); + }, + methods: { + handleChange: function handleChange(_ref) { + var _ref$target = _ref.target, + checked = _ref$target.checked, + indeterminate = _ref$target.indeterminate; + var localChecked = this.computedLocalChecked; + var value = this.value; + var isArr = isArray(localChecked); + var uncheckedValue = isArr ? null : this.uncheckedValue; // Update computedLocalChecked + + if (isArr) { + var idx = looseIndexOf(localChecked, value); + + if (checked && idx < 0) { + // Add value to array + localChecked = localChecked.concat(value); + } else if (!checked && idx > -1) { + // Remove value from array + localChecked = localChecked.slice(0, idx).concat(localChecked.slice(idx + 1)); + } + } else { + localChecked = checked ? value : uncheckedValue; + } + + this.computedLocalChecked = localChecked; // Change is only emitted on user interaction + + this.$emit('change', checked ? value : uncheckedValue); // If this is a child of form-checkbox-group, we emit a change event on it as well + + if (this.is_Group) { + this.bvGroup.$emit('change', localChecked); + } + + this.$emit('update:indeterminate', indeterminate); + }, + setIndeterminate: function setIndeterminate(state) { + // Indeterminate only supported in single checkbox mode + if (isArray(this.computedLocalChecked)) { + state = false; + } + + if (this.$refs && this.$refs.input) { + this.$refs.input.indeterminate = state; // Emit update event to prop + + this.$emit('update:indeterminate', state); + } + } + } + }; + + function isObject$1(obj) { + return obj && {}.toString.call(obj) === '[object Object]'; + } // @vue/component + + + var formOptionsMixin = { + props: { + options: { + type: [Array, Object], + default: function _default() { + return []; + } + }, + valueField: { + type: String, + default: 'value' + }, + textField: { + type: String, + default: 'text' + }, + htmlField: { + type: String, + default: 'html' + }, + disabledField: { + type: String, + default: 'disabled' + } + }, + computed: { + formOptions: function formOptions() { + var options = this.options; + var valueField = this.valueField; + var textField = this.textField; + var htmlField = this.htmlField; + var disabledField = this.disabledField; + + if (isArray(options)) { + // Normalize flat-ish arrays to Array of Objects + return options.map(function (option) { + if (isObject$1(option)) { + var value = option[valueField]; + var text = String(option[textField]); + return { + value: typeof value === 'undefined' ? text : value, + text: stripTags(text), + html: option[htmlField], + disabled: Boolean(option[disabledField]) + }; + } + + return { + value: option, + text: stripTags(String(option)), + disabled: false + }; + }); + } else { + // options is Object + // Normalize Objects to Array of Objects + return keys(options).map(function (key) { + var option = options[key] || {}; + + if (isObject$1(option)) { + var value = option[valueField]; + var text = option[textField]; + return { + value: typeof value === 'undefined' ? key : value, + text: typeof text === 'undefined' ? stripTags(String(key)) : stripTags(String(text)), + html: option[htmlField], + disabled: Boolean(option[disabledField]) + }; + } + + return { + value: key, + text: stripTags(String(option)), + disabled: false + }; + }); + } + } + } + }; + + var formRadioCheckGroupMixin = { + model: { + prop: 'checked', + event: 'input' + }, + props: { + validated: { + type: Boolean, + default: false + }, + ariaInvalid: { + type: [Boolean, String], + default: false + }, + stacked: { + type: Boolean, + default: false + }, + plain: { + type: Boolean, + default: false + }, + buttons: { + // Render as button style + type: Boolean, + default: false + }, + buttonVariant: { + // Only applicable when rendered with button style + type: String, + default: 'secondary' + } + }, + computed: { + inline: function inline() { + return !this.stacked; + }, + groupName: function groupName() { + // Checks/Radios tied to the same model must have the same name, + // especially for ARIA accessibility. + return this.name || this.safeId(); + }, + groupClasses: function groupClasses() { + if (this.buttons) { + return ['btn-group-toggle', this.inline ? 'btn-group' : 'btn-group-vertical', this.size ? "btn-group-".concat(this.size) : '', this.validated ? "was-validated" : '']; + } + + return [this.validated ? "was-validated" : '']; + }, + computedAriaInvalid: function computedAriaInvalid() { + var ariaInvalid = this.ariaInvalid; + + if (ariaInvalid === true || ariaInvalid === 'true' || ariaInvalid === '') { + return 'true'; + } + + return this.computedState === false ? 'true' : null; + } + }, + watch: { + checked: function checked(newVal, oldVal) { + this.localChecked = newVal; + }, + localChecked: function localChecked(newVal, oldVal) { + this.$emit('input', newVal); + } + }, + render: function render(h) { + var _this = this; + + var $slots = this.$slots; + var inputs = this.formOptions.map(function (option, idx) { + var uid = "_BV_option_".concat(idx, "_"); + return h(_this.is_RadioGroup ? 'b-form-radio' : 'b-form-checkbox', { + key: uid, + props: { + id: _this.safeId(uid), + value: option.value, + // Individual radios or checks can be disabled in a group + disabled: option.disabled || false // We don't need to include these, since the input's will know they are inside here + // name: this.groupName, + // form: this.form || null, + // required: Boolean(this.name && this.required) + + } + }, [h('span', { + domProps: htmlOrText(option.html, option.text) + })]); + }); + return h('div', { + class: this.groupClasses, + attrs: { + id: this.safeId(), + role: this.is_RadioGroup ? 'radiogroup' : 'group', + // Tabindex to allow group to be focused if needed + tabindex: '-1', + 'aria-required': this.required ? 'true' : null, + 'aria-invalid': this.computedAriaInvalid + } + }, [$slots.first, inputs, $slots.default]); + } + }; + + var BFormCheckboxGroup = { + name: 'BFormCheckboxGroup', + components: { + BFormCheckbox: BFormCheckbox + }, + mixins: [idMixin, formMixin, formRadioCheckGroupMixin, // Includes render function + formOptionsMixin, formSizeMixin, formStateMixin], + provide: function provide() { + return { + bvCheckGroup: this + }; + }, + props: { + switches: { + // Custom switch styling + type: Boolean, + default: false + }, + checked: { + type: [String, Number, Object, Array, Boolean], + default: null + } + }, + data: function data() { + return { + localChecked: this.checked || [] + }; + }, + computed: { + is_RadioGroup: function is_RadioGroup() { + return false; + } + } + }; + + var components$f = { + BFormCheckbox: BFormCheckbox, + BCheckbox: BFormCheckbox, + BCheck: BFormCheckbox, + BFormCheckboxGroup: BFormCheckboxGroup, + BCheckboxGroup: BFormCheckboxGroup, + BCheckGroup: BFormCheckboxGroup + }; + var index$d = { + install: function install(Vue) { + registerComponents(Vue, components$f); + } + }; + + var BFormRadio = { + name: 'BFormRadio', + mixins: [idMixin, formRadioCheckMixin, // Includes shared render function + formMixin, formSizeMixin, formStateMixin], + inject: { + bvGroup: { + from: 'bvRadioGroup', + default: false + } + }, + props: { + checked: { + // v-model + type: [String, Object, Number, Boolean], + default: null + } + }, + computed: { + // Radio Groups can only have a single value, so determining if checked is simple + is_Checked: function is_Checked() { + return looseEqual(this.value, this.computedLocalChecked); + }, + // Flags for form-radio-check mixin + is_Radio: function is_Radio() { + return true; + }, + is_Check: function is_Check() { + return false; + } + }, + watch: { + // Radio Groups can only have a single value, so our watchers are simple + computedLocalChecked: function computedLocalChecked(newVal, oldVal) { + this.$emit('input', this.computedLocalChecked); + } + }, + methods: { + handleChange: function handleChange(_ref) { + var checked = _ref.target.checked; + var value = this.value; + this.computedLocalChecked = value; // Change is only emitted on user interaction + + this.$emit('change', checked ? value : null); // If this is a child of form-radio-group, we emit a change event on it as well + + if (this.is_Group) { + this.bvGroup.$emit('change', checked ? value : null); + } + } + } + }; + + var BFormRadioGroup = { + name: 'BFormRadioGroup', + components: { + BFormRadio: BFormRadio + }, + mixins: [idMixin, formMixin, formRadioCheckGroupMixin, // Includes render function + formOptionsMixin, formSizeMixin, formStateMixin], + provide: function provide() { + return { + bvRadioGroup: this + }; + }, + props: { + checked: { + type: [String, Object, Number, Boolean], + default: null + } + }, + data: function data() { + return { + localChecked: this.checked + }; + }, + computed: { + is_RadioGroup: function is_RadioGroup() { + return true; + } + } + }; + + var components$g = { + BFormRadio: BFormRadio, + BRadio: BFormRadio, + BFormRadioGroup: BFormRadioGroup, + BRadioGroup: BFormRadioGroup + }; + var index$e = { + install: function install(Vue) { + registerComponents(Vue, components$g); + } + }; + + // @vue/component + var formTextMixin = { + model: { + prop: 'value', + event: 'update' + }, + props: { + value: { + type: [String, Number], + default: '' + }, + ariaInvalid: { + type: [Boolean, String], + default: false + }, + readonly: { + type: Boolean, + default: false + }, + plaintext: { + type: Boolean, + default: false + }, + autocomplete: { + type: String, + default: null + }, + placeholder: { + type: String, + default: null + }, + formatter: { + type: Function, + default: null + }, + trim: { + type: Boolean, + default: false + }, + number: { + type: Boolean, + default: false + }, + lazyFormatter: { + type: Boolean, + value: false + } + }, + data: function data() { + return { + localValue: this.stringifyValue(this.value) + }; + }, + computed: { + computedClass: function computedClass() { + return [{ + // Range input needs class custom-range + 'custom-range': this.type === 'range', + // plaintext not supported by type=range or type=color + 'form-control-plaintext': this.plaintext && this.type !== 'range' && this.type !== 'color', + // form-control not used by type=range or plaintext. Always used by type=color + 'form-control': !this.plaintext && this.type !== 'range' || this.type === 'color' + }, this.sizeFormClass, this.stateClass]; + }, + computedAriaInvalid: function computedAriaInvalid() { + if (!this.ariaInvalid || this.ariaInvalid === 'false') { + // this.ariaInvalid is null or false or 'false' + return this.computedState === false ? 'true' : null; + } + + if (this.ariaInvalid === true) { + // User wants explicit aria-invalid=true + return 'true'; + } // Most likely a string value (which could be the string 'true') + + + return this.ariaInvalid; + } + }, + watch: { + value: function value(newVal, oldVal) { + if (newVal !== oldVal && newVal !== this.localValue) { + this.localValue = this.stringifyValue(newVal); + } + } + }, + mounted: function mounted() { + var value = this.stringifyValue(this.value); + + if (value !== this.localValue) { + /* istanbul ignore next */ + this.localValue = value; + } + }, + methods: { + stringifyValue: function stringifyValue(value) { + return value === null || typeof value === 'undefined' ? '' : String(value); + }, + getFormatted: function getFormatted(value, event) { + var force = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + value = this.stringifyValue(value); + + if ((!this.lazyFormatter || force) && typeof this.formatter === 'function') { + value = this.formatter(value, event); + } + + return value; + }, + updateValue: function updateValue(value) { + value = this.stringifyValue(value); + + if (this.localValue !== value) { + // keep the input set to the value before modifiers + this.localValue = value; + + if (this.number) { + // Emulate .number modifier behaviour + var num = parseFloat(value); + value = isNaN(num) ? value : num; + } else if (this.trim) { + // Emulate .trim modifier behaviour + value = value.trim(); + } // Update the v-model + + + this.$emit('update', value); + } + }, + onInput: function onInput(evt) { + // evt.target.composing is set by Vue + // https://github.com/vuejs/vue/blob/dev/src/platforms/web/runtime/directives/model.js + + /* istanbul ignore if: hard to test composition events */ + if (evt.target.composing) { + return; + } + + var formatted = this.getFormatted(evt.target.value, evt); + + if (formatted === false || evt.defaultPrevented) { + evt.preventDefault(); + return; + } + + this.updateValue(formatted); + this.$emit('input', formatted); + }, + onChange: function onChange(evt) { + // evt.target.composing is set by Vue + // https://github.com/vuejs/vue/blob/dev/src/platforms/web/runtime/directives/model.js + + /* istanbul ignore if: hard to test composition events */ + if (evt.target.composing) { + return; + } + + var formatted = this.getFormatted(evt.target.value, evt); + + if (formatted === false) { + return; + } + + this.updateValue(formatted); + this.$emit('change', formatted); + }, + onBlur: function onBlur(evt) { + // lazy formatter + if (this.lazyFormatter) { + var formatted = this.getFormatted(evt.target.value, evt, true); + + if (formatted === false) { + return; + } + + this.updateValue(formatted); + } // Emit native blur event + + + this.$emit('blur', evt); + }, + focus: function focus() { + // For external handler that may want a focus method + if (!this.disabled) { + this.$el.focus(); + } + }, + blur: function blur() { + // For external handler that may want a blur method + if (!this.disabled) { + this.$el.blur(); + } + } + } + }; + + // @vue/component + var formSelectionMixin = { + computed: { + selectionStart: { + // Expose selectionStart for formatters, etc + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.selectionStart; + }, + set: function set(val) + /* istanbul ignore next */ + { + this.$refs.input.selectionStart = val; + } + }, + selectionEnd: { + // Expose selectionEnd for formatters, etc + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.selectionEnd; + }, + set: function set(val) + /* istanbul ignore next */ + { + this.$refs.input.selectionEnd = val; + } + }, + selectionDirection: { + // Expose selectionDirection for formatters, etc + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.selectionDirection; + }, + set: function set(val) + /* istanbul ignore next */ + { + this.$refs.input.selectionDirection = val; + } + } + }, + methods: { + select: function select() + /* istanbul ignore next */ + { + var _this$$refs$input; + + // For external handler that may want a select() method + (_this$$refs$input = this.$refs.input).select.apply(_this$$refs$input, arguments); + }, + setSelectionRange: function setSelectionRange() + /* istanbul ignore next */ + { + var _this$$refs$input2; + + // For external handler that may want a setSelectionRange(a,b,c) method + (_this$$refs$input2 = this.$refs.input).setSelectionRange.apply(_this$$refs$input2, arguments); + }, + setRangeText: function setRangeText() + /* istanbul ignore next */ + { + var _this$$refs$input3; + + // For external handler that may want a setRangeText(a,b,c) method + (_this$$refs$input3 = this.$refs.input).setRangeText.apply(_this$$refs$input3, arguments); + } + } + }; + + // @vue/component + var formValidityMixin = { + computed: { + validity: { + // Expose validity property + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.validity; + } + }, + validationMessage: { + // Expose validationMessage property + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.validationMessage; + } + }, + willValidate: { + // Expose willValidate property + cache: false, + get: function get() + /* istanbul ignore next */ + { + return this.$refs.input.willValidate; + } + } + }, + methods: { + setCustomValidity: function setCustomValidity() + /* istanbul ignore next */ + { + var _this$$refs$input; + + // For external handler that may want a setCustomValidity(...) method + return (_this$$refs$input = this.$refs.input).setCustomValidity.apply(_this$$refs$input, arguments); + }, + checkValidity: function checkValidity() + /* istanbul ignore next */ + { + var _this$$refs$input2; + + // For external handler that may want a checkValidity(...) method + return (_this$$refs$input2 = this.$refs.input).checkValidity.apply(_this$$refs$input2, arguments); + }, + reportValidity: function reportValidity() + /* istanbul ignore next */ + { + var _this$$refs$input3; + + // For external handler that may want a reportValidity(...) method + return (_this$$refs$input3 = this.$refs.input).reportValidity.apply(_this$$refs$input3, arguments); + } + } + }; + + var TYPES = ['text', 'password', 'email', 'number', 'url', 'tel', 'search', 'range', 'color', 'date', 'time', 'datetime', 'datetime-local', 'month', 'week']; // @vue/component + + var BFormInput = { + name: 'BFormInput', + mixins: [idMixin, formMixin, formSizeMixin, formStateMixin, formTextMixin, formSelectionMixin, formValidityMixin], + props: { + // value prop defined in form-text mixin + // value: { }, + type: { + type: String, + default: 'text', + validator: function validator(type) { + return arrayIncludes(TYPES, type); + } + }, + noWheel: { + // Disable mousewheel to prevent wheel from changing values (i.e. number/date). + type: Boolean, + default: false + }, + min: { + type: [String, Number], + default: null + }, + max: { + type: [String, Number], + default: null + }, + step: { + type: [String, Number], + default: null + }, + list: { + type: String, + default: null + } + }, + computed: { + localType: function localType() { + // We only allow certain types + return arrayIncludes(TYPES, this.type) ? this.type : 'text'; + } + }, + watch: { + noWheel: function noWheel(newVal) { + this.setWheelStopper(newVal); + } + }, + mounted: function mounted() { + this.setWheelStopper(this.noWheel); + }, + deactivated: function deactivated() { + // Turn off listeners when keep-alive component deactivated + + /* istanbul ignore next */ + this.setWheelStopper(false); + }, + activated: function activated() { + // Turn on listeners (if no-wheel) when keep-alive component activated + + /* istanbul ignore next */ + this.setWheelStopper(this.noWheel); + }, + beforeDestroy: function beforeDestroy() { + /* istanbul ignore next */ + this.setWheelStopper(false); + }, + methods: { + setWheelStopper: function setWheelStopper(on) { + var input = this.$el; // We use native events, so that we don't interfere with propgation + + if (on) { + eventOn(input, 'focus', this.onWheelFocus); + eventOn(input, 'blur', this.onWheelBlur); + } else { + eventOff(input, 'focus', this.onWheelFocus); + eventOff(input, 'blur', this.onWheelBlur); + eventOff(document, 'wheel', this.stopWheel); + } + }, + onWheelFocus: function onWheelFocus(evt) { + eventOn(document, 'wheel', this.stopWheel); + }, + onWheelBlur: function onWheelBlur(evt) { + eventOff(document, 'wheel', this.stopWheel); + }, + stopWheel: function stopWheel(evt) { + evt.preventDefault(); + this.$el.blur(); + } + }, + render: function render(h) { + var self = this; + return h('input', { + ref: 'input', + class: self.computedClass, + directives: [{ + name: 'model', + rawName: 'v-model', + value: self.localValue, + expression: 'localValue' + }], + attrs: { + id: self.safeId(), + name: self.name, + form: self.form || null, + type: self.localType, + disabled: self.disabled, + placeholder: self.placeholder, + required: self.required, + autocomplete: self.autocomplete || null, + readonly: self.readonly || self.plaintext, + min: self.min, + max: self.max, + step: self.step, + list: self.localType !== 'password' ? self.list : null, + 'aria-required': self.required ? 'true' : null, + 'aria-invalid': self.computedAriaInvalid + }, + domProps: { + value: self.localValue + }, + on: _objectSpread({}, self.$listeners, { + input: self.onInput, + change: self.onChange, + blur: self.onBlur + }) + }); + } + }; + + var components$h = { + BFormInput: BFormInput, + BInput: BFormInput + }; + var index$f = { + install: function install(Vue) { + registerComponents(Vue, components$h); + } + }; + + var BFormTextarea = { + name: 'BFormTextarea', + mixins: [idMixin, formMixin, formSizeMixin, formStateMixin, formTextMixin, formSelectionMixin, formValidityMixin], + props: { + rows: { + type: [Number, String], + default: 2 + }, + maxRows: { + type: [Number, String], + default: null + }, + wrap: { + // 'soft', 'hard' or 'off'. Browser default is 'soft' + type: String, + default: 'soft' + }, + noResize: { + // Disable the resize handle of textarea + type: Boolean, + default: false + }, + noAutoShrink: { + // When in auto resize mode, disable shrinking to content height + type: Boolean, + default: false + } + }, + data: function data() { + return { + dontResize: true + }; + }, + computed: { + computedStyle: function computedStyle() { + var styles = { + // Setting `noResize` to true will disable the ability for the user to + // manually resize the textarea. We also disable when in auto resize mode + resize: !this.computedRows || this.noResize ? 'none' : null + }; + + if (!this.computedRows) { + // The computed height for auto resize. + // We avoid setting the style to null, which can override user manual resize. + styles.height = this.computedHeight; + } + + return styles; + }, + computedMinRows: function computedMinRows() { + // Ensure rows is at least 2 and positive (2 is the native textarea value). + // A value of 1 can cause issues in some browsers, and most browsers only support + // 2 as the smallest value. + return Math.max(parseInt(this.rows, 10) || 2, 2); + }, + computedMaxRows: function computedMaxRows() { + return Math.max(this.computedMinRows, parseInt(this.maxRows, 10) || 0); + }, + computedRows: function computedRows() { + // This is used to set the attribute 'rows' on the textarea. + // If auto-resize is enabled, then we return null as we use CSS to control height. + return this.computedMinRows === this.computedMaxRows ? this.computedMinRows : null; + }, + computedHeight: function computedHeight() + /* istanbul ignore next: can't test getComputedProperties */ + { + // We compare `computedRows` and `localValue` to `true`, a value + // they both can't have at any time, to ensure reactivity + if (this.$isServer || this.dontResize || this.computedRows === true || this.localValue === true) { + return null; + } + + var el = this.$el; // Element must be visible (not hidden) and in document + // *Must* be checked after above checks + + if (!isVisible(el)) { + return null; + } // Remember old height (includes `px` units) and reset it temporarily to `auto` + + + var oldHeight = el.style.height; + el.style.height = 'auto'; // Get current computed styles + + var computedStyle = getCS(el); // Height of one line of text in px + + var lineHeight = parseFloat(computedStyle.lineHeight); // Minimum height for min rows (browser dependant) + + var minHeight = parseInt(computedStyle.height, 10) || lineHeight * this.computedMinRows; // Calculate height of content + + var offset = (parseFloat(computedStyle.borderTopWidth) || 0) + (parseFloat(computedStyle.borderBottomWidth) || 0) + (parseFloat(computedStyle.paddingTop) || 0) + (parseFloat(computedStyle.paddingBottom) || 0); // Calculate content height in "rows" + + var contentRows = Math.max((el.scrollHeight - offset) / lineHeight, 2); // Calculate number of rows to display (limited within min/max rows) + + var rows = Math.min(Math.max(contentRows, this.computedMinRows), this.computedMaxRows); // Calculate the required height of the textarea including border and padding (in pixels) + + var height = Math.max(Math.ceil(rows * lineHeight + offset), minHeight); // Place old height back on element, just in case this computed prop returns the same value + + el.style.height = oldHeight; // Value of previous height (without px units appended) + + var oldHeightPx = parseFloat(oldHeight) || 0; + + if (this.noAutoShrink && oldHeightPx > height) { + // Computed height remains the larger of oldHeight and new height + // When height is `sticky` (no-auto-shrink is true) + return oldHeight; + } // Return the new computed height in px units + + + return "".concat(height, "px"); + } + }, + mounted: function mounted() { + var _this = this; + + // Enable opt-in resizing once mounted + this.$nextTick(function () { + _this.dontResize = false; + }); + }, + activated: function activated() { + var _this2 = this; + + // If we are being re-activated in , enable opt-in resizing + this.$nextTick(function () { + _this2.dontResize = false; + }); + }, + deactivated: function deactivated() { + // If we are in a deactivated , disable opt-in resizing + this.dontResize = true; + }, + beforeDestroy: function beforeDestroy() { + /* istanbul ignore next */ + this.dontResize = true; + }, + render: function render(h) { + // Using self instead of this helps reduce code size during minification + var self = this; + return h('textarea', { + ref: 'input', + class: self.computedClass, + style: self.computedStyle, + directives: [{ + name: 'model', + rawName: 'v-model', + value: self.localValue, + expression: 'localValue' + }], + attrs: { + id: self.safeId(), + name: self.name, + form: self.form || null, + disabled: self.disabled, + placeholder: self.placeholder, + required: self.required, + autocomplete: self.autocomplete || null, + readonly: self.readonly || self.plaintext, + rows: self.computedRows, + wrap: self.wrap || null, + 'aria-required': self.required ? 'true' : null, + 'aria-invalid': self.computedAriaInvalid + }, + domProps: { + value: self.localValue + }, + on: _objectSpread({}, self.$listeners, { + input: self.onInput, + change: self.onChange, + blur: self.onBlur + }) + }); + } + }; + + var components$i = { + BFormTextarea: BFormTextarea, + BTextarea: BFormTextarea + }; + var index$g = { + install: function install(Vue) { + registerComponents(Vue, components$i); + } + }; + + // @vue/component + var formCustomMixin = { + props: { + plain: { + type: Boolean, + default: false + } + }, + computed: { + custom: function custom() { + return !this.plain; + } + } + }; + + var BFormFile = { + name: 'BFormFile', + mixins: [idMixin, formMixin, formStateMixin, formCustomMixin], + props: { + value: { + // type: Object, + default: null + }, + accept: { + type: String, + default: '' + }, + // Instruct input to capture from camera + capture: { + type: Boolean, + default: false + }, + placeholder: { + type: String, + default: 'No file chosen' // Chrome default file prompt + + }, + browseText: { + type: String, + default: null + }, + dropPlaceholder: { + type: String, + default: null + }, + multiple: { + type: Boolean, + default: false + }, + directory: { + type: Boolean, + default: false + }, + noTraverse: { + type: Boolean, + default: false + }, + noDrop: { + type: Boolean, + default: false + } + }, + data: function data() { + return { + selectedFile: null, + dragging: false, + hasFocus: false + }; + }, + computed: { + selectLabel: function selectLabel() { + // Draging active + if (this.dragging && this.dropPlaceholder) { + return this.dropPlaceholder; + } // No file chosen + + + if (!this.selectedFile || this.selectedFile.length === 0) { + return this.placeholder; + } // Multiple files + + + if (this.multiple) { + return this.selectedFile.map(function (file) { + return file.name; + }).join(', '); + } // Single file + + + return this.selectedFile.name; + } + }, + watch: { + selectedFile: function selectedFile(newVal, oldVal) { + // The following test is needed when the file input is "reset" or the + // exact same file(s) are selected to prevent an infinite loop. + // When in `multiple` mode we need to check for two empty arrays or + // two arrays with identical files + if (newVal === oldVal || isArray(newVal) && isArray(oldVal) && newVal.length === oldVal.length && newVal.every(function (v, i) { + return v === oldVal[i]; + })) { + return; + } + + if (!newVal && this.multiple) { + this.$emit('input', []); + } else { + this.$emit('input', newVal); + } + }, + value: function value(newVal) { + if (!newVal || isArray(newVal) && newVal.length === 0) { + this.reset(); + } + } + }, + methods: { + focusHandler: function focusHandler(evt) { + // Bootstrap v4 doesn't have focus styling for custom file input + // Firefox has a '[type=file]:focus ~ sibling' selector issue, + // so we add a 'focus' class to get around these bugs + if (this.plain || evt.type === 'focusout') { + this.hasFocus = false; + } else { + // Add focus styling for custom file input + this.hasFocus = true; + } + }, + reset: function reset() { + try { + // Wrapped in try in case IE 11 craps out + this.$refs.input.value = ''; + } catch (e) {} // IE 11 doesn't support setting `input.value` to '' or null + // So we use this little extra hack to reset the value, just in case. + // This also appears to work on modern browsers as well. + + + this.$refs.input.type = ''; + this.$refs.input.type = 'file'; + this.selectedFile = this.multiple ? [] : null; + }, + onFileChange: function onFileChange(evt) { + var _this = this; + + // Always emit original event + this.$emit('change', evt); // Check if special `items` prop is available on event (drop mode) + // Can be disabled by setting no-traverse + + var items = evt.dataTransfer && evt.dataTransfer.items; + + if (items && !this.noTraverse) { + /* istanbul ignore next: not supported in JSDOM */ + var queue = []; + + for (var i = 0; i < items.length; i++) { + var item = items[i].webkitGetAsEntry(); + + if (item) { + queue.push(this.traverseFileTree(item)); + } + } + + Promise.all(queue).then(function (filesArr) { + _this.setFiles(from(filesArr)); + }); + return; + } // Normal handling + + + this.setFiles(evt.target.files || evt.dataTransfer.files); + }, + setFiles: function setFiles() { + var files = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + + if (!files) { + /* istanbul ignore next: this will probably not happen */ + this.selectedFile = null; + } else if (this.multiple) { + // Convert files to array + var filesArray = []; + + for (var i = 0; i < files.length; i++) { + filesArray.push(files[i]); + } // Return file(s) as array + + + this.selectedFile = filesArray; + } else { + // Return single file object + this.selectedFile = files[0] || null; + } + }, + onReset: function onReset() { + // Triggered when the parent form (if any) is reset + this.selectedFile = this.multiple ? [] : null; + }, + onDragover: function onDragover(evt) { + evt.preventDefault(); + evt.stopPropagation(); + + if (this.noDrop || !this.custom) { + return; + } + + this.dragging = true; + evt.dataTransfer.dropEffect = 'copy'; + }, + onDragleave: function onDragleave(evt) { + evt.preventDefault(); + evt.stopPropagation(); + this.dragging = false; + }, + onDrop: function onDrop(evt) { + evt.preventDefault(); + evt.stopPropagation(); + + if (this.noDrop) { + return; + } + + this.dragging = false; + + if (evt.dataTransfer.files && evt.dataTransfer.files.length > 0) { + this.onFileChange(evt); + } + }, + traverseFileTree: function traverseFileTree(item, path) + /* istanbul ignore next: not supported in JSDOM */ + { + var _this2 = this; + + // Based on http://stackoverflow.com/questions/3590058 + return new Promise(function (resolve) { + path = path || ''; + + if (item.isFile) { + // Get file + item.file(function (file) { + file.$path = path; // Inject $path to file obj + + resolve(file); + }); + } else if (item.isDirectory) { + // Get folder contents + item.createReader().readEntries(function (entries) { + var queue = []; + + for (var i = 0; i < entries.length; i++) { + queue.push(_this2.traverseFileTree(entries[i], path + item.name + '/')); + } + + Promise.all(queue).then(function (filesArr) { + resolve(from(filesArr)); + }); + }); + } + }); + } + }, + render: function render(h) { + // Form Input + var input = h('input', { + ref: 'input', + class: [{ + 'form-control-file': this.plain, + 'custom-file-input': this.custom, + focus: this.custom && this.hasFocus + }, this.stateClass], + attrs: { + type: 'file', + id: this.safeId(), + name: this.name, + disabled: this.disabled, + required: this.required, + form: this.form || null, + capture: this.capture || null, + accept: this.accept || null, + multiple: this.multiple, + webkitdirectory: this.directory, + 'aria-required': this.required ? 'true' : null + }, + on: { + change: this.onFileChange, + focusin: this.focusHandler, + focusout: this.focusHandler, + reset: this.onReset + } + }); + + if (this.plain) { + return input; + } // Overlay Labels + + + var label = h('label', { + class: ['custom-file-label', this.dragging ? 'dragging' : null], + attrs: { + for: this.safeId(), + 'data-browse': this.browseText || null + } + }, this.selectLabel); // Return rendered custom file input + + return h('div', { + class: ['custom-file', 'b-form-file', this.stateClass], + attrs: { + id: this.safeId('_BV_file_outer_') + }, + on: { + dragover: this.onDragover, + dragleave: this.onDragleave, + drop: this.onDrop + } + }, [input, label]); + } + }; + + var components$j = { + BFormFile: BFormFile, + BFile: BFormFile + }; + var index$h = { + install: function install(Vue) { + registerComponents(Vue, components$j); + } + }; + + var BFormSelect = { + name: 'BFormSelect', + mixins: [idMixin, formMixin, formSizeMixin, formStateMixin, formCustomMixin, formOptionsMixin], + props: { + value: {// type: Object, + // default: undefined + }, + multiple: { + type: Boolean, + default: false + }, + selectSize: { + // Browsers default size to 0, which shows 4 rows in most browsers in multiple mode + // Size of 1 can bork out Firefox + type: Number, + default: 0 + }, + ariaInvalid: { + type: [Boolean, String], + default: false + } + }, + data: function data() { + return { + localValue: this.value + }; + }, + computed: { + computedSelectSize: function computedSelectSize() { + // Custom selects with a size of zero causes the arrows to be hidden, + // so dont render the size attribute in this case + return !this.plain && this.selectSize === 0 ? null : this.selectSize; + }, + inputClass: function inputClass() { + return [this.plain ? 'form-control' : 'custom-select', this.size && this.plain ? "form-control-".concat(this.size) : null, this.size && !this.plain ? "custom-select-".concat(this.size) : null, this.stateClass]; + }, + computedAriaInvalid: function computedAriaInvalid() { + if (this.ariaInvalid === true || this.ariaInvalid === 'true') { + return 'true'; + } + + return this.stateClass === 'is-invalid' ? 'true' : null; + } + }, + watch: { + value: function value(newVal, oldVal) { + this.localValue = newVal; + }, + localValue: function localValue(newVal, oldVal) { + this.$emit('input', this.localValue); + } + }, + methods: { + focus: function focus() { + this.$refs.input.focus(); + }, + blur: function blur() { + this.$refs.input.blur(); + } + }, + render: function render(h) { + var _this = this; + + var $slots = this.$slots; + var options = this.formOptions.map(function (option, index) { + return h('option', { + key: "option_".concat(index, "_opt"), + attrs: { + disabled: Boolean(option.disabled) + }, + domProps: _objectSpread({}, htmlOrText(option.html, option.text), { + value: option.value + }) + }); + }); + return h('select', { + ref: 'input', + class: this.inputClass, + directives: [{ + name: 'model', + rawName: 'v-model', + value: this.localValue, + expression: 'localValue' + }], + attrs: { + id: this.safeId(), + name: this.name, + form: this.form || null, + multiple: this.multiple || null, + size: this.computedSelectSize, + disabled: this.disabled, + required: this.required, + 'aria-required': this.required ? 'true' : null, + 'aria-invalid': this.computedAriaInvalid + }, + on: { + change: function change(evt) { + var target = evt.target; + var selectedVal = from(target.options).filter(function (o) { + return o.selected; + }).map(function (o) { + return '_value' in o ? o._value : o.value; + }); + _this.localValue = target.multiple ? selectedVal : selectedVal[0]; + + _this.$nextTick(function () { + _this.$emit('change', _this.localValue); + }); + } + } + }, [$slots.first, options, $slots.default]); + } + }; + + var components$k = { + BFormSelect: BFormSelect, + BSelect: BFormSelect + }; + var index$i = { + install: function install(Vue) { + registerComponents(Vue, components$k); + } + }; + + var components$l = { + BImg: BImg, + BImgLazy: BImgLazy + }; + var index$j = { + install: function install(Vue) { + registerComponents(Vue, components$l); + } + }; + + var props$w = { + fluid: { + type: Boolean, + default: false + }, + containerFluid: { + type: Boolean, + default: false + }, + header: { + type: String, + default: null + }, + headerHtml: { + type: String, + default: null + }, + headerTag: { + type: String, + default: 'h1' + }, + headerLevel: { + type: [Number, String], + default: '3' + }, + lead: { + type: String, + default: null + }, + leadHtml: { + type: String, + default: null + }, + leadTag: { + type: String, + default: 'p' + }, + tag: { + type: String, + default: 'div' + }, + bgVariant: { + type: String, + default: null + }, + borderVariant: { + type: String, + default: null + }, + textVariant: { + type: String, + default: null + } // @vue/component + + }; + var BJumbotron = { + name: 'BJumbotron', + functional: true, + props: props$w, + render: function render(h, _ref) { + var _class2; + + var props = _ref.props, + data = _ref.data, + slots = _ref.slots; + // The order of the conditionals matter. + // We are building the component markup in order. + var childNodes = []; + var $slots = slots(); // Header + + if (props.header || $slots.header || props.headerHtml) { + childNodes.push(h(props.headerTag, { + class: _defineProperty({}, "display-".concat(props.headerLevel), Boolean(props.headerLevel)) + }, $slots.header || props.headerHtml || stripTags(props.header))); + } // Lead + + + if (props.lead || $slots.lead || props.leadHtml) { + childNodes.push(h(props.leadTag, { + staticClass: 'lead' + }, $slots.lead || props.leadHtml || stripTags(props.lead))); + } // Default slot + + + if ($slots.default) { + childNodes.push($slots.default); + } // If fluid, wrap content in a container/container-fluid + + + if (props.fluid) { + // Children become a child of a container + childNodes = [h(Container, { + props: { + fluid: props.containerFluid + } + }, childNodes)]; + } // Return the jumbotron + + + return h(props.tag, mergeData(data, { + staticClass: 'jumbotron', + class: (_class2 = { + 'jumbotron-fluid': props.fluid + }, _defineProperty(_class2, "text-".concat(props.textVariant), Boolean(props.textVariant)), _defineProperty(_class2, "bg-".concat(props.bgVariant), Boolean(props.bgVariant)), _defineProperty(_class2, "border-".concat(props.borderVariant), Boolean(props.borderVariant)), _defineProperty(_class2, "border", Boolean(props.borderVariant)), _class2) + }), childNodes); + } + }; + + var components$m = { + BJumbotron: BJumbotron + }; + var index$k = { + install: function install(Vue) { + registerComponents(Vue, components$m); + } + }; + + var components$n = { + BLink: BLink + }; + var index$l = { + install: function install(Vue) { + registerComponents(Vue, components$n); + } + }; + + var props$x = { + tag: { + type: String, + default: 'div' + }, + flush: { + type: Boolean, + default: false + }, + horizontal: { + type: [Boolean, String], + default: false + } // @vue/component + + }; + var BListGroup = { + name: 'BListGroup', + functional: true, + props: props$x, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var horizontal = props.horizontal === '' ? true : props.horizontal; + horizontal = props.flush ? false : horizontal; + var componentData = { + staticClass: 'list-group', + class: _defineProperty({ + 'list-group-flush': props.flush, + 'list-group-horizontal': horizontal === true + }, "list-group-horizontal-".concat(horizontal), typeof horizontal === 'string') + }; + return h(props.tag, mergeData(data, componentData), children); + } + }; + + var actionTags = ['a', 'router-link', 'button', 'b-link']; + var linkProps$2 = propsFactory(); + delete linkProps$2.href.default; + delete linkProps$2.to.default; + var props$y = _objectSpread({ + tag: { + type: String, + default: 'div' + }, + action: { + type: Boolean, + default: null + }, + button: { + type: Boolean, + default: null + }, + variant: { + type: String, + default: null + } + }, linkProps$2); // @vue/component + + var BListGroupItem = { + name: 'BListGroupItem', + functional: true, + props: props$y, + render: function render(h, _ref) { + var _class; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var tag = props.button ? 'button' : !props.href && !props.to ? props.tag : BLink; + var isAction = Boolean(props.href || props.to || props.action || props.button || arrayIncludes(actionTags, props.tag)); + var attrs = {}; + var itemProps = {}; + + if (tag === 'button') { + if (!data.attrs || !data.attrs.type) { + // Add a type for button is one not provided in passed attributes + attrs.type = 'button'; + } + + if (props.disabled) { + // Set disabled attribute if button and disabled + attrs.disabled = true; + } + } else { + itemProps = pluckProps(linkProps$2, props); + } + + var componentData = { + attrs: attrs, + props: itemProps, + staticClass: 'list-group-item', + class: (_class = {}, _defineProperty(_class, "list-group-item-".concat(props.variant), Boolean(props.variant)), _defineProperty(_class, 'list-group-item-action', isAction), _defineProperty(_class, "active", props.active), _defineProperty(_class, "disabled", props.disabled), _class) + }; + return h(tag, mergeData(data, componentData), children); + } + }; + + var components$o = { + BListGroup: BListGroup, + BListGroupItem: BListGroupItem + }; + var index$m = { + install: function install(Vue) { + registerComponents(Vue, components$o); + } + }; + + var props$z = { + tag: { + type: String, + default: 'div' + } + }; + var BMediaBody = { + name: 'BMediaBody', + functional: true, + props: props$z, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'media-body' + }), children); + } + }; + + var props$A = { + tag: { + type: String, + default: 'div' + }, + verticalAlign: { + type: String, + default: 'top' + } // @vue/component + + }; + var BMediaAside = { + name: 'BMediaAside', + functional: true, + props: props$A, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'd-flex', + class: _defineProperty({}, "align-self-".concat(props.verticalAlign), props.verticalAlign) + }), children); + } + }; + + var props$B = { + tag: { + type: String, + default: 'div' + }, + rightAlign: { + type: Boolean, + default: false + }, + verticalAlign: { + type: String, + default: 'top' + }, + noBody: { + type: Boolean, + default: false + } // @vue/component + + }; + var BMedia = { + name: 'BMedia', + functional: true, + props: props$B, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + slots = _ref.slots, + children = _ref.children; + var childNodes = props.noBody ? children : []; + var $slots = slots(); + + if (!props.noBody) { + if ($slots.aside && !props.rightAlign) { + childNodes.push(h(BMediaAside, { + staticClass: 'mr-3', + props: { + verticalAlign: props.verticalAlign + } + }, $slots.aside)); + } + + childNodes.push(h(BMediaBody, $slots.default)); + + if ($slots.aside && props.rightAlign) { + childNodes.push(h(BMediaAside, { + staticClass: 'ml-3', + props: { + verticalAlign: props.verticalAlign + } + }, $slots.aside)); + } + } + + return h(props.tag, mergeData(data, { + staticClass: 'media' + }), childNodes); + } + }; + + var components$p = { + BMedia: BMedia, + BMediaAside: BMediaAside, + BMediaBody: BMediaBody + }; + var index$n = { + install: function install(Vue) { + registerComponents(Vue, components$p); + } + }; + + var Selector$1 = { + FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', + STICKY_CONTENT: '.sticky-top', + NAVBAR_TOGGLER: '.navbar-toggler' // ObserveDom config + + }; + var OBSERVER_CONFIG = { + subtree: true, + childList: true, + characterData: true, + attributes: true, + attributeFilter: ['style', 'class'] // modal wrapper ZINDEX offset incrememnt + + }; + var ZINDEX_OFFSET = 2000; // Modal open count helpers + + function getModalOpenCount() { + return parseInt(getAttr(document.body, 'data-modal-open-count') || 0, 10); + } + + function setModalOpenCount(count) { + setAttr(document.body, 'data-modal-open-count', String(count)); + return count; + } + + function incrementModalOpenCount() { + return setModalOpenCount(getModalOpenCount() + 1); + } + + function decrementModalOpenCount() { + return setModalOpenCount(Math.max(getModalOpenCount() - 1, 0)); + } // Returns the current visible modal highest z-index + + + function getModalMaxZIndex() { + return selectAll('div.modal') + /* find all modals that are in document */ + .filter(isVisible) + /* filter only visible ones */ + .map(function (m) { + return m.parentElement; + }) + /* select the outer div */ + .reduce(function (max, el) { + /* compute the highest z-index */ + return Math.max(max, parseInt(el.style.zIndex || 0, 10)); + }, 0); + } // Returns the next z-index to be used by a modal to ensure proper stacking + // regardless of document order. Increments by 2000 + + + function getModalNextZIndex() { + return getModalMaxZIndex() + ZINDEX_OFFSET; + } // @vue/component + + + var BModal = { + name: 'BModal', + components: { + BButton: BButton, + BButtonClose: BButtonClose + }, + mixins: [idMixin, listenOnRootMixin], + model: { + prop: 'visible', + event: 'change' + }, + props: { + title: { + type: String, + default: '' + }, + titleHtml: { + type: String + }, + titleTag: { + type: String, + default: 'h5' + }, + size: { + type: String, + default: 'md' + }, + centered: { + type: Boolean, + default: false + }, + scrollable: { + type: Boolean, + default: false + }, + buttonSize: { + type: String, + default: '' + }, + noStacking: { + type: Boolean, + default: false + }, + noFade: { + type: Boolean, + default: false + }, + noCloseOnBackdrop: { + type: Boolean, + default: false + }, + noCloseOnEsc: { + type: Boolean, + default: false + }, + noEnforceFocus: { + type: Boolean, + default: false + }, + headerBgVariant: { + type: String, + default: null + }, + headerBorderVariant: { + type: String, + default: null + }, + headerTextVariant: { + type: String, + default: null + }, + headerCloseVariant: { + type: String, + default: null + }, + headerClass: { + type: [String, Array], + default: null + }, + bodyBgVariant: { + type: String, + default: null + }, + bodyTextVariant: { + type: String, + default: null + }, + modalClass: { + type: [String, Array], + default: null + }, + dialogClass: { + type: [String, Array], + default: null + }, + contentClass: { + type: [String, Array], + default: null + }, + bodyClass: { + type: [String, Array], + default: null + }, + footerBgVariant: { + type: String, + default: null + }, + footerBorderVariant: { + type: String, + default: null + }, + footerTextVariant: { + type: String, + default: null + }, + footerClass: { + type: [String, Array], + default: null + }, + hideHeader: { + type: Boolean, + default: false + }, + hideFooter: { + type: Boolean, + default: false + }, + hideHeaderClose: { + type: Boolean, + default: false + }, + hideBackdrop: { + type: Boolean, + default: false + }, + okOnly: { + type: Boolean, + default: false + }, + okDisabled: { + type: Boolean, + default: false + }, + cancelDisabled: { + type: Boolean, + default: false + }, + visible: { + type: Boolean, + default: false + }, + returnFocus: { + // type: Object, + default: null + }, + headerCloseLabel: { + type: String, + default: 'Close' + }, + cancelTitle: { + type: String, + default: 'Cancel' + }, + cancelTitleHtml: { + type: String + }, + okTitle: { + type: String, + default: 'OK' + }, + okTitleHtml: { + type: String + }, + cancelVariant: { + type: String, + default: 'secondary' + }, + okVariant: { + type: String, + default: 'primary' + }, + lazy: { + type: Boolean, + default: false + }, + busy: { + type: Boolean, + default: false + } + }, + data: function data() { + return { + is_hidden: this.lazy || false, + // for lazy modals + is_visible: false, + // controls modal visible state + is_transitioning: false, + // Used for style control + is_show: false, + // Used for style control + is_block: false, + // Used for style control + is_opening: false, + // Semaphore for previnting incorrect modal open counts + is_closing: false, + // Semapbore for preventing incorrect modal open counts + scrollbarWidth: 0, + zIndex: ZINDEX_OFFSET, + // z-index for modal stacking + isTop: true, + // If the modal is the topmost opened modal + isBodyOverflowing: false, + return_focus: this.returnFocus || null + }; + }, + computed: { + contentClasses: function contentClasses() { + return ['modal-content', this.contentClass]; + }, + modalClasses: function modalClasses() { + return [{ + fade: !this.noFade, + show: this.is_show, + 'd-block': this.is_block + }, this.modalClass]; + }, + dialogClasses: function dialogClasses() { + var _ref; + + return [(_ref = {}, _defineProperty(_ref, "modal-".concat(this.size), Boolean(this.size)), _defineProperty(_ref, 'modal-dialog-centered', this.centered), _defineProperty(_ref, 'modal-dialog-scrollable', this.scrollable), _ref), this.dialogClass]; + }, + backdropClasses: function backdropClasses() { + return { + fade: !this.noFade, + show: this.is_show || this.noFade + }; + }, + headerClasses: function headerClasses() { + var _ref2; + + return [(_ref2 = {}, _defineProperty(_ref2, "bg-".concat(this.headerBgVariant), Boolean(this.headerBgVariant)), _defineProperty(_ref2, "text-".concat(this.headerTextVariant), Boolean(this.headerTextVariant)), _defineProperty(_ref2, "border-".concat(this.headerBorderVariant), Boolean(this.headerBorderVariant)), _ref2), this.headerClass]; + }, + bodyClasses: function bodyClasses() { + var _ref3; + + return [(_ref3 = {}, _defineProperty(_ref3, "bg-".concat(this.bodyBgVariant), Boolean(this.bodyBgVariant)), _defineProperty(_ref3, "text-".concat(this.bodyTextVariant), Boolean(this.bodyTextVariant)), _ref3), this.bodyClass]; + }, + footerClasses: function footerClasses() { + var _ref4; + + return [(_ref4 = {}, _defineProperty(_ref4, "bg-".concat(this.footerBgVariant), Boolean(this.footerBgVariant)), _defineProperty(_ref4, "text-".concat(this.footerTextVariant), Boolean(this.footerTextVariant)), _defineProperty(_ref4, "border-".concat(this.footerBorderVariant), Boolean(this.footerBorderVariant)), _ref4), this.footerClass]; + }, + modalOuterStyle: function modalOuterStyle() { + return { + // We only set these styles on the stacked modals (ones with next z-index > 0). + position: 'absolute', + zIndex: this.zIndex + }; + } + }, + watch: { + visible: function visible(newVal, oldVal) { + if (newVal === oldVal) { + return; + } + + this[newVal ? 'show' : 'hide'](); + } + }, + created: function created() { + // create non-reactive property + this._observer = null; + }, + mounted: function mounted() { + // Listen for events from others to either open or close ourselves + // And listen to all modals to enable/disable enforce focus + this.listenOnRoot('bv::show::modal', this.showHandler); + this.listenOnRoot('bv::modal::shown', this.shownHandler); + this.listenOnRoot('bv::hide::modal', this.hideHandler); + this.listenOnRoot('bv::modal::hidden', this.hiddenHandler); + this.listenOnRoot('bv::toggle::modal', this.toggleHandler); // Listen for bv:modal::show events, and close ourselves if the opening modal not us + + this.listenOnRoot('bv::modal::show', this.modalListener); // Initially show modal? + + if (this.visible === true) { + this.show(); + } + }, + beforeDestroy: function beforeDestroy() + /* instanbul ignore next */ + { + // Ensure everything is back to normal + if (this._observer) { + this._observer.disconnect(); + + this._observer = null; + } // Ensure our root "once" listener is gone + + + this.$root.$off('bv::modal::hidden', this.doShow); + this.setEnforceFocus(false); + this.setResizeEvent(false); + + if (this.is_visible) { + this.is_visible = false; + this.is_show = false; + this.is_transitioning = false; + var count = decrementModalOpenCount(); + + if (count === 0) { + // Re-adjust body/navbar/fixed padding/margins (as we were the last modal open) + this.setModalOpenClass(false); + this.resetScrollbar(); + this.resetDialogAdjustments(); + } + } + }, + methods: { + // Public Methods + show: function show() { + if (this.is_visible || this.is_opening) { + // if already open, on in the process of opening, do nothing + return; + } + + if (this.is_closing) { + // if we are in the process of closing, wait until hidden before re-opening + this.$once('hidden', this.show); + return; + } + + this.is_opening = true; + var showEvt = new BvEvent('show', { + cancelable: true, + vueTarget: this, + target: this.$refs.modal, + modalId: this.safeId(), + relatedTarget: null + }); + this.emitEvent(showEvt); // Don't show if canceled + + if (showEvt.defaultPrevented || this.is_visible) { + this.is_opening = false; + return; + } + + if (!this.noStacking) { + // Find the z-index to use + this.zIndex = getModalNextZIndex(); // Show the modal + + this.doShow(); + return; + } + + if (hasClass(document.body, 'modal-open')) { + // If another modal is already open, wait for it to close + this.$root.$once('bv::modal::hidden', this.doShow); + return; + } // Show the modal + + + this.doShow(); + }, + hide: function hide(trigger) { + if (!this.is_visible || this.is_closing) { + return; + } + + this.is_closing = true; + var hideEvt = new BvEvent('hide', { + cancelable: true, + vueTarget: this, + target: this.$refs.modal, + modalId: this.safeId(), + // this could be the trigger element/component reference + relatedTarget: null, + isOK: trigger || null, + trigger: trigger || null, + cancel: function cancel() { + // Backwards compatibility + warn('b-modal: evt.cancel() is deprecated. Please use evt.preventDefault().'); + this.preventDefault(); + } + }); + + if (trigger === 'ok') { + this.$emit('ok', hideEvt); + } else if (trigger === 'cancel') { + this.$emit('cancel', hideEvt); + } + + this.emitEvent(hideEvt); // Hide if not canceled + + if (hideEvt.defaultPrevented || !this.is_visible) { + this.is_closing = false; + return; + } // stop observing for content changes + + + if (this._observer) { + this._observer.disconnect(); + + this._observer = null; + } + + this.is_visible = false; + this.$emit('change', false); + }, + // Public method to toggle modal visibility + toggle: function toggle(triggerEl) { + if (triggerEl) { + this.return_focus = triggerEl; + } + + if (this.is_visible) { + this.hide('toggle'); + } else { + this.show(); + } + }, + // Private method to finish showing modal + doShow: function doShow() { + var _this = this; + + // Place modal in DOM if lazy + this.is_hidden = false; + this.$nextTick(function () { + // We do this in nextTick to ensure the modal is in DOM first before we show it + _this.is_visible = true; + _this.is_opening = false; + + _this.$emit('change', true); // Observe changes in modal content and adjust if necessary + + + _this._observer = observeDOM(_this.$refs.content, _this.adjustDialog.bind(_this), OBSERVER_CONFIG); + }); + }, + // Transition Handlers + onBeforeEnter: function onBeforeEnter() { + this.getScrollbarWidth(); + this.is_transitioning = true; + this.checkScrollbar(); + var count = incrementModalOpenCount(); + + if (count === 1) { + this.setScrollbar(); + } + + this.adjustDialog(); + this.setModalOpenClass(true); + this.setResizeEvent(true); + }, + onEnter: function onEnter() { + this.is_block = true; + }, + onAfterEnter: function onAfterEnter() { + var _this2 = this; + + this.is_show = true; + this.is_transitioning = false; + this.$nextTick(function () { + var shownEvt = new BvEvent('shown', { + cancelable: false, + vueTarget: _this2, + target: _this2.$refs.modal, + modalId: _this2.safeId(), + relatedTarget: null + }); + + _this2.emitEvent(shownEvt); + + _this2.focusFirst(); + + _this2.setEnforceFocus(true); + }); + }, + onBeforeLeave: function onBeforeLeave() { + this.is_transitioning = true; + this.setResizeEvent(false); + }, + onLeave: function onLeave() { + // Remove the 'show' class + this.is_show = false; + }, + onAfterLeave: function onAfterLeave() { + var _this3 = this; + + this.is_block = false; + this.resetDialogAdjustments(); + this.is_transitioning = false; + var count = decrementModalOpenCount(); + + if (count === 0) { + this.resetScrollbar(); + this.setModalOpenClass(false); + } + + this.setEnforceFocus(false); + this.$nextTick(function () { + _this3.is_hidden = _this3.lazy || false; + _this3.zIndex = ZINDEX_OFFSET; + + _this3.returnFocusTo(); + + _this3.is_closing = false; + var hiddenEvt = new BvEvent('hidden', { + cancelable: false, + vueTarget: _this3, + target: _this3.lazy ? null : _this3.$refs.modal, + modalId: _this3.safeId(), + relatedTarget: null + }); + + _this3.emitEvent(hiddenEvt); + }); + }, + // Event emitter + emitEvent: function emitEvent(bvEvt) { + var type = bvEvt.type; + this.$emit(type, bvEvt); + this.$root.$emit("bv::modal::".concat(type), bvEvt, this.safeId()); + }, + // UI Event Handlers + onClickOut: function onClickOut(evt) { + // Do nothing if not visible, backdrop click disabled, or element that generated + // click event is no longer in document + if (!this.is_visible || this.noCloseOnBackdrop || !contains(document, evt.target)) { + return; + } // If backdrop clicked, hide modal + + + if (!contains(this.$refs.content, evt.target)) { + this.hide('backdrop'); + } + }, + onEsc: function onEsc(evt) { + // If ESC pressed, hide modal + if (evt.keyCode === KeyCodes.ESC && this.is_visible && !this.noCloseOnEsc) { + this.hide('esc'); + } + }, + // Document focusin listener + focusHandler: function focusHandler(evt) { + // If focus leaves modal, bring it back + var modal = this.$refs.modal; + + if (!this.noEnforceFocus && this.isTop && this.is_visible && modal && document !== evt.target && !contains(modal, evt.target)) { + modal.focus({ + preventScroll: true + }); + } + }, + // Turn on/off focusin listener + setEnforceFocus: function setEnforceFocus(on) { + var options = { + passive: true, + capture: false + }; + + if (on) { + eventOn(document, 'focusin', this.focusHandler, options); + } else { + eventOff(document, 'focusin', this.focusHandler, options); + } + }, + // Resize Listener + setResizeEvent: function setResizeEvent(on) + /* istanbul ignore next: can't easily test in JSDOM */ + { + var _this4 = this; + ['resize', 'orientationchange'].forEach(function (evtName) { + var options = { + passive: true, + capture: false + }; + + if (on) { + eventOn(window, evtName, _this4.adjustDialog, options); + } else { + eventOff(window, evtName, _this4.adjustDialog, options); + } + }); + }, + // Root Listener handlers + showHandler: function showHandler(id, triggerEl) { + if (id === this.id) { + this.return_focus = triggerEl || null; + this.show(); + } + }, + hideHandler: function hideHandler(id) { + if (id === this.id) { + this.hide('event'); + } + }, + toggleHandler: function toggleHandler(id, triggerEl) { + if (id === this.id) { + this.toggle(triggerEl); + } + }, + shownHandler: function shownHandler() { + this.setTop(); + }, + hiddenHandler: function hiddenHandler() { + this.setTop(); + }, + setTop: function setTop() { + // Determine if we are the topmost visible modal + this.isTop = this.zIndex >= getModalMaxZIndex(); + }, + modalListener: function modalListener(bvEvt) { + // If another modal opens, close this one + if (this.noStacking && bvEvt.vueTarget !== this) { + this.hide(); + } + }, + // Focus control handlers + focusFirst: function focusFirst() { + // Don't try and focus if we are SSR + if (typeof document === 'undefined') { + return; + } + + var modal = this.$refs.modal; + var activeElement = document.activeElement; + + if (activeElement && contains(modal, activeElement)) { + // If activeElement is child of modal or is modal, no need to change focus + return; + } + + if (modal) { + // make sure top of modal is showing (if longer than the viewport) and + // focus the modal content wrapper + this.$nextTick(function () { + modal.scrollTop = 0; + modal.focus(); + }); + } + }, + returnFocusTo: function returnFocusTo() { + // Prefer returnFocus prop over event specified return_focus value + var el = this.returnFocus || this.return_focus || null; + + if (typeof el === 'string') { + // CSS Selector + el = select(el); + } + + if (el) { + el = el.$el || el; + + if (isVisible(el)) { + el.focus(); + } + } + }, + // Utility methods + getScrollbarWidth: function getScrollbarWidth() { + var scrollDiv = document.createElement('div'); + scrollDiv.className = 'modal-scrollbar-measure'; + document.body.appendChild(scrollDiv); + this.scrollbarWidth = getBCR(scrollDiv).width - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + }, + setModalOpenClass: function setModalOpenClass(open) { + if (open) { + addClass(document.body, 'modal-open'); + } else { + removeClass(document.body, 'modal-open'); + } + }, + adjustDialog: function adjustDialog() { + if (!this.is_visible) { + return; + } + + var modal = this.$refs.modal; + var isModalOverflowing = modal.scrollHeight > document.documentElement.clientHeight; + + if (!this.isBodyOverflowing && isModalOverflowing) { + modal.style.paddingLeft = "".concat(this.scrollbarWidth, "px"); + } else { + modal.style.paddingLeft = ''; + } + + if (this.isBodyOverflowing && !isModalOverflowing) { + modal.style.paddingRight = "".concat(this.scrollbarWidth, "px"); + } else { + modal.style.paddingRight = ''; + } + }, + resetDialogAdjustments: function resetDialogAdjustments() { + var modal = this.$refs.modal; + + if (modal) { + modal.style.paddingLeft = ''; + modal.style.paddingRight = ''; + } + }, + checkScrollbar: function checkScrollbar() + /* istanbul ignore next: getBCR can't be tested in JSDOM */ + { + var _getBCR = getBCR(document.body), + left = _getBCR.left, + right = _getBCR.right, + height = _getBCR.height; // Extra check for body.height needed for stacked modals + + + this.isBodyOverflowing = left + right < window.innerWidth || height > window.innerHeight; + }, + setScrollbar: function setScrollbar() { + /* istanbul ignore if: get Computed Style can't be tested in JSDOM */ + 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 + var body = document.body; + var scrollbarWidth = this.scrollbarWidth; + body._paddingChangedForModal = []; + body._marginChangedForModal = []; // Adjust fixed content padding + + selectAll(Selector$1.FIXED_CONTENT).forEach(function (el) { + var actualPadding = el.style.paddingRight; + var calculatedPadding = getCS(el).paddingRight || 0; + setAttr(el, 'data-padding-right', actualPadding); + el.style.paddingRight = "".concat(parseFloat(calculatedPadding) + scrollbarWidth, "px"); + + body._paddingChangedForModal.push(el); + }); // Adjust sticky content margin + + selectAll(Selector$1.STICKY_CONTENT).forEach(function (el) { + var actualMargin = el.style.marginRight; + var calculatedMargin = getCS(el).marginRight || 0; + setAttr(el, 'data-margin-right', actualMargin); + el.style.marginRight = "".concat(parseFloat(calculatedMargin) - scrollbarWidth, "px"); + + body._marginChangedForModal.push(el); + }); // Adjust navbar-toggler margin + + selectAll(Selector$1.NAVBAR_TOGGLER).forEach(function (el) { + var actualMargin = el.style.marginRight; + var calculatedMargin = getCS(el).marginRight || 0; + setAttr(el, 'data-margin-right', actualMargin); + el.style.marginRight = "".concat(parseFloat(calculatedMargin) + scrollbarWidth, "px"); + + body._marginChangedForModal.push(el); + }); // Adjust body padding + + var actualPadding = body.style.paddingRight; + var calculatedPadding = getCS(body).paddingRight; + setAttr(body, 'data-padding-right', actualPadding); + body.style.paddingRight = "".concat(parseFloat(calculatedPadding) + scrollbarWidth, "px"); + } + }, + resetScrollbar: function resetScrollbar() { + var body = document.body; + + if (body._paddingChangedForModal) { + // Restore fixed content padding + body._paddingChangedForModal.forEach(function (el) { + if (hasAttr(el, 'data-padding-right')) { + el.style.paddingRight = getAttr(el, 'data-padding-right') || ''; + removeAttr(el, 'data-padding-right'); + } + }); + } + + if (body._marginChangedForModal) { + // Restore sticky content and navbar-toggler margin + body._marginChangedForModal.forEach(function (el) { + if (hasAttr(el, 'data-margin-right')) { + el.style.marginRight = getAttr(el, 'data-margin-right') || ''; + removeAttr(el, 'data-margin-right'); + } + }); + } + + body._paddingChangedForModal = null; + body._marginChangedForModal = null; // Restore body padding + + if (hasAttr(body, 'data-padding-right')) { + body.style.paddingRight = getAttr(body, 'data-padding-right') || ''; + removeAttr(body, 'data-padding-right'); + } + } + }, + render: function render(h) { + var _this5 = this; + + var $slots = this.$slots; // Modal Header + + var header = h(false); + + if (!this.hideHeader) { + var modalHeader = $slots['modal-header']; + + if (!modalHeader) { + var closeButton = h(false); + + if (!this.hideHeaderClose) { + closeButton = h('b-button-close', { + props: { + disabled: this.is_transitioning, + ariaLabel: this.headerCloseLabel, + textVariant: this.headerCloseVariant || this.headerTextVariant + }, + on: { + click: function click(evt) { + _this5.hide('headerclose'); + } + } + }, [$slots['modal-header-close']]); + } + + modalHeader = [h(this.titleTag, { + class: ['modal-title'] + }, [$slots['modal-title'] || this.titleHtml || stripTags(this.title)]), closeButton]; + } + + header = h('header', { + ref: 'header', + staticClass: 'modal-header', + class: this.headerClasses, + attrs: { + id: this.safeId('__BV_modal_header_') + } + }, [modalHeader]); + } // Modal Body + + + var body = h('div', { + ref: 'body', + staticClass: 'modal-body', + class: this.bodyClasses, + attrs: { + id: this.safeId('__BV_modal_body_') + } + }, [$slots.default]); // Modal Footer + + var footer = h(false); + + if (!this.hideFooter) { + var modalFooter = $slots['modal-footer']; + + if (!modalFooter) { + var cancelButton = h(false); + + if (!this.okOnly) { + cancelButton = h('b-button', { + props: { + variant: this.cancelVariant, + size: this.buttonSize, + disabled: this.cancelDisabled || this.busy || this.is_transitioning + }, + on: { + click: function click(evt) { + _this5.hide('cancel'); + } + } + }, [$slots['modal-cancel'] || this.cancelTitleHtml || stripTags(this.cancelTitle)]); + } + + var okButton = h('b-button', { + props: { + variant: this.okVariant, + size: this.buttonSize, + disabled: this.okDisabled || this.busy || this.is_transitioning + }, + on: { + click: function click(evt) { + _this5.hide('ok'); + } + } + }, [$slots['modal-ok'] || this.okTitleHtml || stripTags(this.okTitle)]); + modalFooter = [cancelButton, okButton]; + } + + footer = h('footer', { + ref: 'footer', + staticClass: 'modal-footer', + class: this.footerClasses, + attrs: { + id: this.safeId('__BV_modal_footer_') + } + }, [modalFooter]); + } // Assemble Modal Content + + + var modalContent = h('div', { + ref: 'content', + class: this.contentClasses, + attrs: { + role: 'document', + id: this.safeId('__BV_modal_content_'), + 'aria-labelledby': this.hideHeader ? null : this.safeId('__BV_modal_header_'), + 'aria-describedby': this.safeId('__BV_modal_body_') + } + }, [header, body, footer]); // Modal Dialog wrapper + + var modalDialog = h('div', { + staticClass: 'modal-dialog', + class: this.dialogClasses + }, [modalContent]); // Modal + + var modal = h('div', { + ref: 'modal', + staticClass: 'modal', + class: this.modalClasses, + directives: [{ + name: 'show', + rawName: 'v-show', + value: this.is_visible, + expression: 'is_visible' + }], + attrs: { + id: this.safeId(), + role: 'dialog', + tabindex: '-1', + 'aria-hidden': this.is_visible ? null : 'true', + 'aria-modal': this.is_visible ? 'true' : null + }, + on: { + keydown: this.onEsc, + click: this.onClickOut + } + }, [modalDialog]); // Wrap modal in transition + + modal = h('transition', { + props: { + enterClass: '', + enterToClass: '', + enterActiveClass: '', + leaveClass: '', + leaveActiveClass: '', + leaveToClass: '' + }, + on: { + 'before-enter': this.onBeforeEnter, + enter: this.onEnter, + 'after-enter': this.onAfterEnter, + 'before-leave': this.onBeforeLeave, + leave: this.onLeave, + 'after-leave': this.onAfterLeave + } + }, [modal]); // Modal Backdrop + + var backdrop = h(false); + + if (!this.hideBackdrop && (this.is_visible || this.is_transitioning)) { + backdrop = h('div', { + staticClass: 'modal-backdrop', + class: this.backdropClasses, + attrs: { + id: this.safeId('__BV_modal_backdrop_') + } + }, [$slots['modal-backdrop']]); + } // Tab trap to prevent page from scrolling to next element in tab index during enforce focus tab cycle + + + var tabTrap = h(false); + + if (this.is_visible && this.isTop && !this.noEnforceFocus) { + tabTrap = h('div', { + attrs: { + tabindex: '0' + } + }); + } // Assemble modal and backdrop in an outer div needed for lazy modals + + + var outer = h(false); + + if (!this.is_hidden) { + outer = h('div', { + key: 'modal-outer', + style: this.modalOuterStyle, + attrs: { + id: this.safeId('__BV_modal_outer_') + } + }, [modal, tabTrap, backdrop]); + } // Wrap in DIV to maintain thi.$el reference for hide/show method aceess + + + return h('div', {}, [outer]); + } + }; + + var listenTypes$1 = { + click: true + }; + var bModal = { + // eslint-disable-next-line no-shadow-restricted-names + bind: function bind(el, binding, vnode) { + bindTargets(vnode, binding, listenTypes$1, function (_ref) { + var targets = _ref.targets, + vnode = _ref.vnode; + targets.forEach(function (target) { + vnode.context.$root.$emit('bv::show::modal', target, vnode.elm); + }); + }); + + if (el.tagName !== 'BUTTON') { + // If element is not a button, we add `role="button"` for accessibility + setAttr(el, 'role', 'button'); + } + }, + unbind: function unbind(el, binding, vnode) { + unbindTargets(vnode, binding, listenTypes$1); + + if (el.tagName !== 'BUTTON') { + // If element is not a button, we add `role="button"` for accessibility + removeAttr(el, 'role', 'button'); + } + } + }; + + var directives$1 = { + bModal: bModal + }; + var modalDirectivePlugin = { + install: function install(Vue) { + registerDirectives(Vue, directives$1); + } + }; + + var components$q = { + BModal: BModal + }; + var index$o = { + install: function install(Vue) { + registerComponents(Vue, components$q); + Vue.use(modalDirectivePlugin); + } + }; + + var props$C = { + tag: { + type: String, + default: 'ul' + }, + fill: { + type: Boolean, + default: false + }, + justified: { + type: Boolean, + default: false + }, + tabs: { + type: Boolean, + default: false + }, + pills: { + type: Boolean, + default: false + }, + vertical: { + type: Boolean, + default: false + }, + isNavBar: { + type: Boolean, + default: false + } // @vue/component + + }; + var BNav = { + name: 'BNav', + functional: true, + props: props$C, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + + if (props.isNavBar) { + /* istanbul ignore next */ + warn("b-nav: Prop 'is-nav-bar' is deprecated. Please use component '' instead."); + } + + return h(props.tag, mergeData(data, { + class: { + nav: !props.isNavBar, + 'navbar-nav': props.isNavBar, + 'nav-tabs': props.tabs && !props.isNavBar, + 'nav-pills': props.pills && !props.isNavBar, + 'flex-column': props.vertical && !props.isNavBar, + 'nav-fill': props.fill, + 'nav-justified': props.justified + } + }), children); + } + }; + + var props$D = propsFactory(); // @vue/component + + var BNavItem = { + name: 'BNavItem', + functional: true, + props: _objectSpread({}, props$D, { + linkAttrs: { + type: Object, + default: function _default() { + return {}; + } + }, + linkClasses: { + type: [String, Object, Array], + default: null + } + }), + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + listeners = _ref.listeners, + children = _ref.children; + // We transfer the listeners to the link + delete data.on; + return h('li', mergeData(data, { + staticClass: 'nav-item' + }), [h(BLink, { + staticClass: 'nav-link', + class: props.linkClasses, + attrs: props.linkAttrs, + props: props, + on: listeners + }, children)]); + } + }; + + var props$E = { + tag: { + type: String, + default: 'span' + } // @vue/component + + }; + var BNavText = { + name: 'BNavText', + functional: true, + props: props$E, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'navbar-text' + }), children); + } + }; + + var BNavForm = { + name: 'BNavForm', + functional: true, + props: { + id: { + type: String, + default: null + } + }, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(BForm, mergeData(data, { + attrs: { + id: props.id + }, + props: { + inline: true + } + }), children); + } + }; + + var BNavItemDropdown = { + name: 'BNavItemDropdown', + mixins: [idMixin, dropdownMixin], + props: { + noCaret: { + type: Boolean, + default: false + }, + extraToggleClasses: { + // Extra Toggle classes + type: String, + default: '' + }, + extraMenuClasses: { + // Extra Menu classes + type: String, + default: '' + }, + role: { + type: String, + default: 'menu' + } + }, + computed: { + isNav: function isNav() { + // Signal to dropdown mixin that we are in a navbar + return true; + }, + dropdownClasses: function dropdownClasses() { + return ['nav-item', 'b-nav-dropdown', 'dropdown', this.dropup ? 'dropup' : '', this.visible ? 'show' : '']; + }, + toggleClasses: function toggleClasses() { + return ['nav-link', this.noCaret ? '' : 'dropdown-toggle', this.disabled ? 'disabled' : '', this.extraToggleClasses ? this.extraToggleClasses : '']; + }, + menuClasses: function menuClasses() { + return ['dropdown-menu', this.right ? 'dropdown-menu-right' : 'dropdown-menu-left', this.visible ? 'show' : '', this.extraMenuClasses ? this.extraMenuClasses : '']; + } + }, + render: function render(h) { + var button = h('a', { + class: this.toggleClasses, + ref: 'toggle', + attrs: { + href: '#', + id: this.safeId('_BV_button_'), + disabled: this.disabled, + 'aria-haspopup': 'true', + 'aria-expanded': this.visible ? 'true' : 'false' + }, + on: { + click: this.toggle, + keydown: this.toggle // space, enter, down + + } + }, [this.$slots['button-content'] || this.$slots.text || h('span', { + domProps: htmlOrText(this.html, this.text) + })]); + var menu = h('div', { + class: this.menuClasses, + ref: 'menu', + attrs: { + tabindex: '-1', + 'aria-labelledby': this.safeId('_BV_button_') + }, + on: { + mouseover: this.onMouseOver, + keydown: this.onKeydown // tab, up, down, esc + + } + }, [this.$slots.default]); + return h('li', { + attrs: { + id: this.safeId() + }, + class: this.dropdownClasses + }, [button, menu]); + } + }; + + var components$r = { + BNav: BNav, + BNavItem: BNavItem, + BNavText: BNavText, + BNavForm: BNavForm, + BNavItemDropdown: BNavItemDropdown, + BNavItemDd: BNavItemDropdown, + BNavDropdown: BNavItemDropdown, + BNavDd: BNavItemDropdown + }; + var navPlugin = { + install: function install(Vue) { + registerComponents(Vue, components$r); + Vue.use(dropdownPlugin); + } + }; + + var props$F = { + tag: { + type: String, + default: 'nav' + }, + type: { + type: String, + default: 'light' + }, + variant: { + type: String + }, + toggleable: { + type: [Boolean, String], + default: false + }, + fixed: { + type: String + }, + sticky: { + type: Boolean, + default: false + }, + print: { + type: Boolean, + default: false + } // @vue/component + + }; + var BNavbar = { + name: 'BNavbar', + functional: true, + props: props$F, + render: function render(h, _ref) { + var _class; + + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var breakpoint = ''; + + if (props.toggleable && typeof props.toggleable === 'string' && props.toggleable !== 'xs') { + breakpoint = "navbar-expand-".concat(props.toggleable); + } else if (props.toggleable === false) { + breakpoint = 'navbar-expand'; + } + + return h(props.tag, mergeData(data, { + staticClass: 'navbar', + class: (_class = { + 'd-print': props.print, + 'sticky-top': props.sticky + }, _defineProperty(_class, "navbar-".concat(props.type), Boolean(props.type)), _defineProperty(_class, "bg-".concat(props.variant), Boolean(props.variant)), _defineProperty(_class, "fixed-".concat(props.fixed), Boolean(props.fixed)), _defineProperty(_class, "".concat(breakpoint), Boolean(breakpoint)), _class), + attrs: { + role: props.tag === 'nav' ? null : 'navigation' + } + }), children); + } + }; + + var props$G = { + tag: { + type: String, + default: 'ul' + }, + fill: { + type: Boolean, + default: false + }, + justified: { + type: Boolean, + default: false + } // @vue/component + + }; + var BNavbarNav = { + name: 'BNavbarNav', + functional: true, + props: props$G, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + return h(props.tag, mergeData(data, { + staticClass: 'navbar-nav', + class: { + 'nav-fill': props.fill, + 'nav-justified': props.justified + } + }), children); + } + }; + + var linkProps$3 = propsFactory(); + linkProps$3.href.default = undefined; + linkProps$3.to.default = undefined; + var props$H = _objectSpread({}, linkProps$3, { + tag: { + type: String, + default: 'div' + } // @vue/component + + }); + var BNavbarBrand = { + name: 'BNavbarBrand', + functional: true, + props: props$H, + render: function render(h, _ref) { + var props = _ref.props, + data = _ref.data, + children = _ref.children; + var isLink = Boolean(props.to || props.href); + var tag = isLink ? BLink : props.tag; + return h(tag, mergeData(data, { + staticClass: 'navbar-brand', + props: isLink ? pluckProps(linkProps$3, props) : {} + }), children); + } + }; + + var BNavbarToggle = { + name: 'BNavbarToggle', + mixins: [listenOnRootMixin], + props: { + label: { + type: String, + default: 'Toggle navigation' + }, + target: { + type: String, + required: true + } + }, + data: function data() { + return { + toggleState: false + }; + }, + created: function created() { + this.listenOnRoot('bv::collapse::state', this.handleStateEvt); + }, + methods: { + onClick: function onClick(evt) { + this.$emit('click', evt); + /* istanbul ignore next */ + + if (!evt.defaultPrevented) { + this.$root.$emit('bv::toggle::collapse', this.target); + } + }, + handleStateEvt: function handleStateEvt(id, state) { + if (id === this.target) { + this.toggleState = state; + } + } + }, + render: function render(h) { + return h('button', { + class: ['navbar-toggler'], + attrs: { + type: 'button', + 'aria-label': this.label, + 'aria-controls': this.target, + 'aria-expanded': this.toggleState ? 'true' : 'false' + }, + on: { + click: this.onClick + } + }, [this.$slots.default || h('span', { + class: ['navbar-toggler-icon'] + })]); + } + }; + + var components$s = { + BNavbar: BNavbar, + BNavbarNav: BNavbarNav, + BNavbarBrand: BNavbarBrand, + BNavbarToggle: BNavbarToggle, + BNavToggle: BNavbarToggle + }; + var index$p = { + install: function install(Vue) { + registerComponents(Vue, components$s); + Vue.use(navPlugin); + Vue.use(collapsePlugin); + Vue.use(dropdownPlugin); + } + }; + + /** + * @param {number} length + * @return {Array} + */ + var range = (function (length) { + return Array.apply(null, { + length: length + }); + }); + + /** + * Convert a value to a string that can be rendered. + */ + + var toString = (function (val) { + var spaces = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2; + return val === null || val === undefined ? '' : isArray(val) || isPlainObject(val) && val.toString === Object.prototype.toString ? JSON.stringify(val, null, spaces) : String(val); + }); + + /* + * Comon props, computed, data, render function, and methods for b-pagination and b-pagination-nav + */ + + var ELLIPSIS_THRESHOLD = 3; // Default # of buttons limit + + var DEFAULT_LIMIT = 5; // Make an array of N to N+X + + function makePageArray(startNum, numPages) { + return range(numPages).map(function (value, index) { + return { + number: index + startNum, + classes: null + }; + }); + } // Sanitize the provided Limit value (converting to a number) + + + function sanitizeLimit(value) { + var limit = parseInt(value, 10) || 1; + return limit < 1 ? DEFAULT_LIMIT : limit; + } // Sanitize the provided numberOfPages value (converting to a number) + + + function sanitizeNumPages(value) { + var num = parseInt(value, 10) || 1; + return num < 1 ? 1 : num; + } // Sanitize the provided current page number (converting to a number) + + + function sanitizeCurPage(value, numPages) { + var page = parseInt(value, 10) || 1; + return page > numPages ? numPages : page < 1 ? 1 : page; + } // Links don't normally respond to SPACE, so we add that functionality via this handler + + + function onSpaceKey(evt) { + if (evt.keyCode === KeyCodes.SPACE) { + evt.preventDefault(); // Stop page from scrolling + + evt.stopImmediatePropagation(); + evt.stopPropagation(); // Trigger the click event on the link + + evt.currentTarget.click(); + return false; + } + } // Props object + + + var props$I = { + disabled: { + type: Boolean, + default: false + }, + value: { + type: [Number, String], + default: 1, + validator: function validator(value) { + var num = parseInt(value, 10); + /* istanbul ignore if */ + + if (isNaN(num) || num < 1) { + warn('pagination: v-model value must be a number greater than 0'); + return false; + } + + return true; + } + }, + limit: { + type: [Number, String], + default: DEFAULT_LIMIT, + validator: function validator(value) { + var num = parseInt(value, 10); + /* istanbul ignore if */ + + if (isNaN(num) || num < 1) { + warn('pagination: prop "limit" must be a number greater than 0'); + return false; + } + + return true; + } + }, + size: { + type: String, + default: 'md' + }, + align: { + type: String, + default: 'left' + }, + hideGotoEndButtons: { + type: Boolean, + default: false + }, + ariaLabel: { + type: String, + default: 'Pagination' + }, + labelFirstPage: { + type: String, + default: 'Go to first page' + }, + firstText: { + type: String, + default: '«' + }, + labelPrevPage: { + type: String, + default: 'Go to previous page' + }, + prevText: { + type: String, + default: '‹' + }, + labelNextPage: { + type: String, + default: 'Go to next page' + }, + nextText: { + type: String, + default: '›' + }, + labelLastPage: { + type: String, + default: 'Go to last page' + }, + lastText: { + type: String, + default: '»' + }, + labelPage: { + type: String, + default: 'Go to page' + }, + hideEllipsis: { + type: Boolean, + default: false + }, + ellipsisText: { + type: String, + default: '…' + } // @vue/component + + }; + var paginationMixin = { + components: { + BLink: BLink + }, + props: props$I, + data: function data() { + return { + currentPage: 1, + localNumPages: 1, + localLimit: DEFAULT_LIMIT + }; + }, + computed: { + btnSize: function btnSize() { + return this.size ? "pagination-".concat(this.size) : ''; + }, + alignment: function alignment() { + if (this.align === 'center') { + return 'justify-content-center'; + } else if (this.align === 'end' || this.align === 'right') { + return 'justify-content-end'; + } + + return ''; + }, + paginationParams: function paginationParams() { + // Determine if we should show the the ellipsis + var limit = this.limit; + var numPages = this.localNumPages; + var curPage = this.currentPage; + var hideEllipsis = this.hideEllipsis; + var showFirstDots = false; + var showLastDots = false; + var numLinks = limit; + var startNum = 1; + + if (numPages <= limit) { + // Special Case: Less pages available than the limit of displayed pages + numLinks = numPages; + } else if (curPage < limit - 1 && limit > ELLIPSIS_THRESHOLD) { + // We are near the beginning of the page list + if (!hideEllipsis) { + showLastDots = true; + numLinks = limit - 1; + } + } else if (numPages - curPage + 2 < limit && limit > ELLIPSIS_THRESHOLD) { + // We are near the end of the list + if (!hideEllipsis) { + numLinks = limit - 1; + showFirstDots = true; + } + + startNum = numPages - numLinks + 1; + } else { + // We are somewhere in the middle of the page list + if (limit > ELLIPSIS_THRESHOLD && !hideEllipsis) { + numLinks = limit - 2; + showFirstDots = showLastDots = true; + } + + startNum = curPage - Math.floor(numLinks / 2); + } // Sanity checks + + + if (startNum < 1) { + startNum = 1; + } else if (startNum > numPages - numLinks) { + startNum = numPages - numLinks + 1; + } + + return { + showFirstDots: showFirstDots, + showLastDots: showLastDots, + numLinks: numLinks, + startNum: startNum + }; + }, + pageList: function pageList() { + // Generates the pageList array + var _this$paginationParam = this.paginationParams, + numLinks = _this$paginationParam.numLinks, + startNum = _this$paginationParam.startNum; // Generate list of page numbers + + var pages = makePageArray(startNum, numLinks); // We limit to a total of 3 page buttons on XS screens + // So add classes to page links to hide them for XS breakpoint + // Note: Ellipsis will also be hidden on XS screens + // TODO: Make this visual limit configurable based on breakpoint(s) + + if (pages.length > 3) { + var idx = this.currentPage - startNum; + + if (idx === 0) { + // Keep leftmost 3 buttons visible when current page is first page + for (var i = 3; i < pages.length; i++) { + pages[i].classes = 'd-none d-sm-flex'; + } + } else if (idx === pages.length - 1) { + // Keep rightmost 3 buttons visible when current page is last page + for (var _i = 0; _i < pages.length - 3; _i++) { + pages[_i].classes = 'd-none d-sm-flex'; + } + } else { + // Hide all except current page, current page - 1 and current page + 1 + for (var _i2 = 0; _i2 < idx - 1; _i2++) { + // hide some left button(s) + pages[_i2].classes = 'd-none d-sm-flex'; + } + + for (var _i3 = pages.length - 1; _i3 > idx + 1; _i3--) { + // hide some right button(s) + pages[_i3].classes = 'd-none d-sm-flex'; + } + } + } + + return pages; + } + }, + watch: { + value: function value(newValue, oldValue) { + if (newValue !== oldValue) { + this.currentPage = sanitizeCurPage(newValue, this.localNumPages); + } + }, + currentPage: function currentPage(newValue, oldValue) { + if (newValue !== oldValue) { + this.$emit('input', newValue); + } + }, + numberOfPages: function numberOfPages(newValue, oldValue) { + if (newValue !== oldValue) { + this.localNumPages = sanitizeNumPages(newValue); + } + }, + limit: function limit(newValue, oldValue) { + if (newValue !== oldValue) { + this.localLimit = sanitizeLimit(newValue); + } + } + }, + created: function created() { + // Set our default values in data + this.localLimit = sanitizeLimit(this.limit); + this.localNumPages = sanitizeNumPages(this.numberOfPages); + this.currentPage = sanitizeCurPage(this.value, this.localNumPages); + }, + methods: { + getButtons: function getButtons() { + // Return only buttons that are visible + return selectAll('a.page-link', this.$el).filter(function (btn) { + return isVisible(btn); + }); + }, + setBtnFocus: function setBtnFocus(btn) { + btn.focus(); + }, + focusCurrent: function focusCurrent() { + var _this = this; + + // We do this in next tick to ensure buttons have finished rendering + this.$nextTick(function () { + var btn = _this.getButtons().find(function (el) { + return parseInt(getAttr(el, 'aria-posinset'), 10) === _this.currentPage; + }); + + if (btn && btn.focus) { + _this.setBtnFocus(btn); + } else { + // Fallback if current page is not in button list + _this.focusFirst(); + } + }); + }, + focusFirst: function focusFirst() { + var _this2 = this; + + // We do this in next tick to ensure buttons have finished rendering + this.$nextTick(function () { + var btn = _this2.getButtons().find(function (el) { + return !isDisabled(el); + }); + + if (btn && btn.focus && btn !== document.activeElement) { + _this2.setBtnFocus(btn); + } + }); + }, + focusLast: function focusLast() { + var _this3 = this; + + // We do this in next tick to ensure buttons have finished rendering + this.$nextTick(function () { + var btn = _this3.getButtons().reverse().find(function (el) { + return !isDisabled(el); + }); + + if (btn && btn.focus && btn !== document.activeElement) { + _this3.setBtnFocus(btn); + } + }); + }, + focusPrev: function focusPrev() { + var _this4 = this; + + // We do this in next tick to ensure buttons have finished rendering + this.$nextTick(function () { + var buttons = _this4.getButtons(); + + var idx = buttons.indexOf(document.activeElement); + + if (idx > 0 && !isDisabled(buttons[idx - 1]) && buttons[idx - 1].focus) { + _this4.setBtnFocus(buttons[idx - 1]); + } + }); + }, + focusNext: function focusNext() { + var _this5 = this; + + // We do this in next tick to ensure buttons have finished rendering + this.$nextTick(function () { + var buttons = _this5.getButtons(); + + var idx = buttons.indexOf(document.activeElement); + var cnt = buttons.length - 1; + + if (idx < cnt && !isDisabled(buttons[idx + 1]) && buttons[idx + 1].focus) { + _this5.setBtnFocus(buttons[idx + 1]); + } + }); + } + }, + render: function render(h) { + var _this6 = this; + + var buttons = []; + var numberOfPages = this.localNumPages; + var disabled = this.disabled; + var _this$paginationParam2 = this.paginationParams, + showFirstDots = _this$paginationParam2.showFirstDots, + showLastDots = _this$paginationParam2.showLastDots; // Helper function + + var isActivePage = function isActivePage(pageNum) { + return pageNum === _this6.currentPage; + }; // Factory function for prev/next/first/last buttons + + + var makeEndBtn = function makeEndBtn(linkTo, ariaLabel, btnSlot, btnText, pageTest, key) { + var button; + var btnContent = btnSlot || toString(btnText) || h(false); + var attrs = { + role: 'none presentation', + 'aria-hidden': disabled ? 'true' : null + }; + + if (disabled || isActivePage(pageTest) || linkTo < 1 || linkTo > numberOfPages) { + button = h('li', { + key: key, + attrs: attrs, + staticClass: 'page-item', + class: ['disabled'] + }, [h('span', { + staticClass: 'page-link' + }, [btnContent])]); + } else { + button = h('li', { + key: key, + attrs: attrs, + staticClass: 'page-item' + }, [h('b-link', { + staticClass: 'page-link', + props: _this6.linkProps(linkTo), + attrs: { + role: 'menuitem', + tabindex: '-1', + 'aria-label': ariaLabel, + 'aria-controls': _this6.ariaControls || null + }, + on: { + click: function click(evt) { + _this6.onClick(linkTo, evt); + }, + keydown: onSpaceKey + } + }, [btnContent])]); + } + + return button; + }; // Ellipsis factory + + + var makeEllipsis = function makeEllipsis(isLast) { + return h('li', { + key: "elipsis-".concat(isLast ? 'last' : 'first'), + staticClass: 'page-item', + class: ['disabled', 'd-none', 'd-sm-flex'], + attrs: { + role: 'separator' + } + }, [h('div', { + staticClass: 'page-link' + }, [_this6.$slots['ellipsis-text'] || toString(_this6.ellipsisText) || h(false)])]); + }; // Goto First Page button bookend + + + buttons.push(this.hideGotoEndButtons ? h(false) : makeEndBtn(1, this.labelFirstPage, this.$slots['first-text'], this.firstText, 1, 'bookend-goto-first')); // Goto Previous page button bookend + + buttons.push(makeEndBtn(this.currentPage - 1, this.labelPrevPage, this.$slots['prev-text'], this.prevText, 1, 'bookend-goto-prev')); // First Ellipsis Bookend + + buttons.push(showFirstDots ? makeEllipsis(false) : h(false)); // Individual Page links + + this.pageList.forEach(function (page) { + var active = isActivePage(page.number); + var staticClass = 'page-link'; + var attrs = { + role: 'menuitemradio', + 'aria-disabled': disabled ? 'true' : null, + 'aria-controls': _this6.ariaControls || null, + 'aria-label': "".concat(_this6.labelPage, " ").concat(page.number), + 'aria-checked': active ? 'true' : 'false', + 'aria-posinset': page.number, + 'aria-setsize': numberOfPages, + // ARIA "roving tabindex" method + tabindex: disabled ? null : active ? '0' : '-1' + }; + var inner = h(disabled ? 'span' : "b-link", { + props: disabled ? {} : _this6.linkProps(page.number), + staticClass: staticClass, + attrs: attrs, + on: disabled ? {} : { + click: function click(evt) { + _this6.onClick(page.number, evt); + }, + keydown: onSpaceKey + } + }, toString(_this6.makePage(page.number))); + buttons.push(h('li', { + key: "page-".concat(page.number), + staticClass: 'page-item', + class: [disabled ? 'disabled' : '', active ? 'active' : '', page.classes], + attrs: { + role: 'none presentation' + } + }, [inner])); + }); // Last Ellipsis Bookend + + buttons.push(showLastDots ? makeEllipsis(true) : h(false)); // Goto Next page button bookend + + buttons.push(makeEndBtn(this.currentPage + 1, this.labelNextPage, this.$slots['next-text'], this.nextText, numberOfPages, 'bookend-goto-next')); // Goto Last Page button bookend + + buttons.push(this.hideGotoEndButtons ? h(false) : makeEndBtn(numberOfPages, this.labelLastPage, this.$slots['last-text'], this.lastText, numberOfPages, 'bookend-goto-last')); // Assemble the paginatiom buttons + + var pagination = h('ul', { + ref: 'ul', + class: ['pagination', 'b-pagination', this.btnSize, this.alignment], + attrs: { + role: 'menubar', + 'aria-disabled': disabled ? 'true' : 'false', + 'aria-label': this.ariaLabel || null + }, + on: { + keydown: function keydown(evt) { + var keyCode = evt.keyCode; + var shift = evt.shiftKey; + + if (keyCode === KeyCodes.LEFT) { + evt.preventDefault(); + shift ? _this6.focusFirst() : _this6.focusPrev(); + } else if (keyCode === KeyCodes.RIGHT) { + evt.preventDefault(); + shift ? _this6.focusLast() : _this6.focusNext(); + } + } + } + }, buttons); // if we are pagination-nav, wrap in '