started upgrade to bootstrap4

This commit is contained in:
Vicky Steeves 2018-03-07 10:38:00 -05:00
parent 8d5c3dabfb
commit c8ab741026
54 changed files with 42744 additions and 6968 deletions

View File

@ -144,7 +144,7 @@ NAVIGATION_LINKS = {
} }
# Name of the theme to use. # Name of the theme to use.
THEME = "custom" THEME = "bootstrap4"
# Primary color of your theme. This will be used to customize your theme and # Primary color of your theme. This will be used to customize your theme and
# auto-generate related colors in POSTS_SECTION_COLORS. Must be a HEX value. # auto-generate related colors in POSTS_SECTION_COLORS. Must be a HEX value.
@ -973,7 +973,7 @@ DEPLOY_DRAFTS = False
# it's faster and the output looks better. # it's faster and the output looks better.
# If you set USE_KATEX to True, you also need to add an extra CSS file # If you set USE_KATEX to True, you also need to add an extra CSS file
# like this: # like this:
# EXTRA_HEAD_DATA = """<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css">""" EXTRA_HEAD_DATA = """<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">"""
# USE_KATEX = False # USE_KATEX = False
# Do you want to customize the nbconversion of your IPython notebook? # Do you want to customize the nbconversion of your IPython notebook?

View File

@ -10,67 +10,76 @@
--> -->
<html> <html>
<body> <body>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="#int" data-toggle="tab" aria-expanded="false">Interests & Skills</a></li> <li class="nav-item"><a class="nav-link active show" data-toggle="tab" href="#int">Interests & Skills</a></li>
<li><a href="#edu" data-toggle="tab" aria-expanded="true">Education</a></li> <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#edu">Education</a></li>
<li><a href="#emp" data-toggle="tab" aria-expanded="true">Employment</a></li> <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#emp">Employment</a></li>
<li><a href="#pub" data-toggle="tab" aria-expanded="true">Publications</a></li> <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#pub">Publications</a></li>
<li><a href="#pre" data-toggle="tab" aria-expanded="true">Presentations</a></li> <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#pre">Presentations</a></li>
<li><a href="#mis" data-toggle="tab" aria-expanded="true">Other Professional Output</a></li> <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#mis">Creative Works</a></li>
</ul> </ul>
<div class="tab-content">
<!-- INTERESTS --> <!- -----------------TAB CONTENT--------------------- -->
<div class="tab-pane fade active in" id="int"> <div id="myTabContent" class="tab-content">
<h4>Interests</h4>
<!- -----------------BEGIN INTERESTS--------------------- -->
<div class="tab-pane fade active show" id="int">
<h4 style="margin-top: 1em;">Interests</h4>
<p>Data management, reproducibility, labour theory, activism, digital preservation, digital archiving, science librarianship, systems engineering, database management, web development, software engineering and development, computational linguistics, social networking, programming for mobile devices, LGBTQA studies, gender studies, animal care, outdoorsmanship, music, and creative writing.</p> <p>Data management, reproducibility, labour theory, activism, digital preservation, digital archiving, science librarianship, systems engineering, database management, web development, software engineering and development, computational linguistics, social networking, programming for mobile devices, LGBTQA studies, gender studies, animal care, outdoorsmanship, music, and creative writing.</p>
<h4>Skills</h4> <h4>Skills</h4>
<ul> <ul>
<li>Platforms: Microsoft Windows, Mac OSX, and Ubuntu Linux </li> <li>Platforms: Microsoft Windows, Mac OSX, and Ubuntu Linux </li>
<li>Programming Languages: Java, Java for Android, C, C++, Objective C, Python, Perl, PHP, SQL, HTML, CSS, JavaScript, jQuery (library), AJAX, XML, and Praat <li>Programming Languages: Java, Java for Android, C, C++, Objective C, Python, Perl, PHP, SQL, HTML, CSS, JavaScript, jQuery (library), AJAX, XML, and Praat</li>
<li>Library Standards: MARC, Dublin Core, DACS, EAD, LC, and Dewey</li> <li>Library Standards: MARC, Dublin Core, DACS, EAD, LC, and Dewey</li>
</ul> </ul>
</div> </div>
<!- -----------------END--------------------- -->
<!-- EDUCATION --> <!- -----------------BEGIN EDUCATION--------------------- -->
<div class="tab-pane fade" id="edu"> <div class="tab-pane fade" id="edu">
<ul style="margin-top: 1em;"> <h3 style="margin-top: 1em;">Simmons College <small class="text-muted">Boston, MA, USA</small></h3>
<li><strong>Simmons College</strong>, Boston, MA, USA:</li>
<ul> <h4>Master of Library and Information Science <small class="text-muted">August 2014</small></h4>
<li><em>Master of Library and Information Science</em>, August 2014
<ul><li>GPA: 3.85</li></ul> <ul><li>GPA: 3.85</li></ul>
<li>Research Opportunities</li>
<ul><li><a href="http://gslis.simmons.edu/smallworld/smallworldProjectStaff.html">Small World Project:</a></li> <p><strong>Research Opportunities</strong></p>
<ul><li>Research done accompanying Dr. Kathy Wisser, March-June 2014
<li>Research completed. I provided software analysis using Gephi, a data visualization software, on researchers' social network analysis of historical relationships between literary figures.</li></ul></ul>
<li><em>Bachelor of Science in Computer Science and Information Technology</em>, May 2013</li>
<ul><li>GPA: 3.75</li>
<li>Honours Thesis: Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning</li></ul>
<ul> <ul>
<li>Study Abroad:</li> <li><a href="http://gslis.simmons.edu/smallworld/smallworldProjectStaff.html">Small World Project.</a> Research done accompanying Dr. Kathy Wisser, March-June 2014. I provided software analysis using Gephi, a data visualization software, on researchers' social network analysis of historical relationships between literary figures.</li>
<ul><li>Celtic Studies, University College Cork, Cork, Ireland, Summer 2012</li></ul> </ul>
<li>Research Opportunities</li> <h4>Bachelor of Science in Computer Science and Information Technology <small class="text-muted">May 2013</small></h4>
<ul><li>"A Computational Linguistics Approach to Inflection and Difference in Meaning"</li> <ul>
<ul><li>Research completed accompanying Dr. Nanette Veilleux, August 2012-August 2013.</li></ul></ul> <li>GPA: 3.75</li>
<li>Honours Thesis: Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning</li>
</ul>
<ul><li>"No Place To Go: A Discussion on LGBTQ Youth Homelessness in Boston"</li> <p><strong>Study Abroad:</strong></p>
<ul><li>Research completed as a part of the Simmons World Challenge, January 2011</li></ul></ul> <ul><li><li>Celtic Studies, University College Cork, Cork, Ireland, Summer 2012</li></ul>
<ul><li>"The Harm (or lack thereof) of Marijuana"</li> <p><strong>Research Opportunities:</strong></p>
<ul><li>Research completed as a part of an introductory research methods course.</li></ul></ul></ul> <ul>
<li>"A Computational Linguistics Approach to Inflection and Difference in Meaning". Research completed accompanying Dr. Nanette Veilleux, August 2012-August 2013.</li>
<li>"No Place To Go: A Discussion on LGBTQ Youth Homelessness in Boston" Research completed as a part of the Simmons World Challenge, January 2011</li>
</ul>
<ul><li>Activities:</li> <p><strong>Activities:</strong></p>
<ul><li>Honours Program: Participant 2010-2013</li> <ul>
<li>Honours Program: Participant 2010-2013</li>
<li>CS-Math Liaison: President 2012-2013; Treasurer 2011-2012</li> <li>CS-Math Liaison: President 2012-2013; Treasurer 2011-2012</li>
<li>Simmons College LGBTQA Alliance: President 2012-2013; Treasurer 2011-2012; Political Activism Chair 2010-2011</li> <li>Simmons College LGBTQA Alliance: President 2012-2013; Treasurer 2011-2012; Political Activism Chair 2010-2011</li>
<li>Simmons College SciFi and Fantasy Club: Founder and Participant 2010-2013</li> <li>Simmons College SciFi and Fantasy Club: Founder and Participant 2010-2013</li>
<li>Simmons World Challenge: Participant January 2012</li></ul></ul> <li>Simmons World Challenge: Participant January 2012</li>
</ul>
<ul><li>Honours and Achievements: </li> <p><strong>Honours and Achievements:</strong></p>
<ul><li>College of Arts and Sciences Dean's Fellow Award; 2013-2014</li> <ul>
<li>College of Arts and Sciences Dean's Fellow Award; 2013-2014</li>
<li>The Computer Science Award, given for academic excellence in comp sci; 2013</li> <li>The Computer Science Award, given for academic excellence in comp sci; 2013</li>
<li>Collaborative Research Experience for Undergraduates, $15,000 research grant, given to encourage women in the STEM fields to complete research studies in their field, 2012-2013</li> <li>Collaborative Research Experience for Undergraduates, $15,000 research grant, given to encourage women in the STEM fields to complete research studies in their field, 2012-2013</li>
<li>Alumnae Endowed Scholarship; 2012-2013</li> <li>Alumnae Endowed Scholarship; 2012-2013</li>
@ -80,14 +89,15 @@
<li>President Merit Scholarship, given to students whose academic achievement and personal qualities indicate they will perform to the highest academic level, 2010-2013</li> <li>President Merit Scholarship, given to students whose academic achievement and personal qualities indicate they will perform to the highest academic level, 2010-2013</li>
<li>Simmons College 3-1 Program, where students complete an undergraduate degree in three years and a master's degree in one year, first participant; 2010-present</li> <li>Simmons College 3-1 Program, where students complete an undergraduate degree in three years and a master's degree in one year, first participant; 2010-present</li>
<li>Simmons College Dean's List, 2010-2013</li> <li>Simmons College Dean's List, 2010-2013</li>
<li>United Pilgrimage for Youth, sponsored participant; Summer 2009</li></ul></ul></ul> <li>United Pilgrimage for Youth, sponsored participant; Summer 2009</li>
</ul> </ul>
</div> </div>
<!- -----------------END EDUCATION--------------------- -->
<!-- EMPLOYMENT --> <!- -----------------BEGIN EMPLOYMENT--------------------- -->
<div class="tab-pane fade" id="emp"> <div class="tab-pane fade" id="emp">
<ul style="margin-top: 1em;"> <ul style="margin-top: 1em;">
<li><strong>New York University, Librarian for Research Data Management and Reproducibility, August 2015-present</strong></li> <p><strong>Honours and Achievements:</strong></p>New York University, Librarian for Research Data Management and Reproducibility, August 2015-present</strong></p>
<ul> <ul>
<li>Dual appointment between NYU Division of Libraries and NYU Center for Data Science</li> <li>Dual appointment between NYU Division of Libraries and NYU Center for Data Science</li>
<li>Provide instructional and consultation services in RDM and reproducibility to faculty and advanced students</li> <li>Provide instructional and consultation services in RDM and reproducibility to faculty and advanced students</li>
@ -95,59 +105,89 @@
<li>Moore/Sloan Data Science Environment team member; Reproducibility Working Group member and Libraries Working Group member</li> <li>Moore/Sloan Data Science Environment team member; Reproducibility Working Group member and Libraries Working Group member</li>
<li>Assist in efforts to design a data repository and storage infrastructure for researchers at the University.</li> <li>Assist in efforts to design a data repository and storage infrastructure for researchers at the University.</li>
</ul> </ul>
<li><strong>Metropolitan New York Library Council, Interim Program Coordinator, June 2015-July 2015; December 2015-May 2016</strong></li> <p><strong>Metropolitan New York Library Council, Interim Program Coordinator, June 2015-July 2015; December 2015-May 2016</strong></p>
<ul><li>Day-to-day coordinator of METROs National Digital Stewardship Residency in New York <a href="http://ndsr.nycdigital.org/">(NDSR-NY)</a> program</li> <ul>
<li>Day-to-day coordinator of METROs National Digital Stewardship Residency in New York <a href="http://ndsr.nycdigital.org/">(NDSR-NY)</a> program</li>
<li>Contribute to project planning, communications, documentation, evaluations, outreach, and help maintain the programs web presence and online platforms.</li> <li>Contribute to project planning, communications, documentation, evaluations, outreach, and help maintain the programs web presence and online platforms.</li>
<li>Plan, organize, and help run NDSR-affiliated events, meetings, and workshops.</li> <li>Plan, organize, and help run NDSR-affiliated events, meetings, and workshops.</li>
<li>Serve as a representative and contact for NDSR-NY program in collaboration with host institutions, NDSR residents and Library of Congress and NDSR-Boston program staff.</li></ul> <li>Serve as a representative and contact for NDSR-NY program in collaboration with host institutions, NDSR residents and Library of Congress and NDSR-Boston program staff.</li>
<li><strong>American Museum of Natural History, National Digital Stewardship Resident, September 2014-May 2015</strong></li> </ul>
<ul><li>See my NDSR application video <a href="https://youtu.be/3oS4boUD9ms">here!</a></li> <p><strong>American Museum of Natural History, National Digital Stewardship Resident, September 2014-May 2015</strong></p>
<ul>
<li>See my NDSR application video <a href="https://youtu.be/3oS4boUD9ms">here!</a></li>
<li>Survey the Science divisions to better understand their data storage, curation, and preservation needs.</li> <li>Survey the Science divisions to better understand their data storage, curation, and preservation needs.</li>
<li>Identify existing practices and policies for integrated data storage, access, and management.</li> <li>Identify existing practices and policies for integrated data storage, access, and management.</li>
<li>Recommend strategies to digitally preserve the scientific research at the AMNH.</li></ul> <li>Recommend strategies to digitally preserve the scientific research at the AMNH.</li>
<li><strong>Sasaki Associates, Archives Intern; January 2014-April 2014</strong></li> </ul>
<ul><li>Process historical architectural material and write the accompanying finding aid.</li> <p><strong>Sasaki Associates, Archives Intern; January 2014-April 2014</strong></p>
<li>Create records for each collection processed and catalogue them in Koha ILS.</li></ul> <ul>
<li><strong>Simmons College, Dean's Fellow for Technology; September 2013-June 2014</strong></li> <li>Process historical architectural material and write the accompanying finding aid.</li>
<ul><li>Manage social media technology for undergraduate science departments.</li> <li>Create records for each collection processed and catalogue them in Koha ILS.</li>
<li>Generate interest in STEM at Simmons through social media outreach to alumnae, current students, and prospective students, through working on content creation with faculty.</li></ul> </ul>
<li><strong>Simmons College, Technical Resource Assistant; September 2013-June 2014</strong></li> <p><strong>Simmons College, Dean's Fellow for Technology; September 2013-June 2014</strong></p>
<ul><li>Provide technical instruction to students, staff, and faculty at the Graduate School of Library and Information Science (GSLIS).</li> <ul>
<li>Troubleshoot hard/software issues within GSLIS for students, staff, and faculty.</li></ul> <li>Manage social media technology for undergraduate science departments.</li>
<li><strong>IES Technical Sales, Contracted Web Developer; May 2013-September 2013</strong></li> <li>Generate interest in STEM at Simmons through social media outreach to alumnae, current students, and prospective students, through working on content creation with faculty.</li>
<ul><li>Update the company website, developing and implementing design and operational upgrades.</li> </ul>
<li>Assess and adapt to changing client needs through the development and deployment phase.</li></ul> <p><strong>Simmons College, Technical Resource Assistant; September 2013-June 2014</strong></p>
<li><strong>Simmons College, Tutor in Computer Science; September 2012-2013</strong></li> <ul>
<ul><li>Provide one-on-one tutoring for students in computer science classes.</li> <li>Provide technical instruction to students, staff, and faculty at the Graduate School of Library and Information Science (GSLIS).</li>
<li>Lead group study sessions for upcoming evaluations, tests, and projects.</li></ul> <li>Troubleshoot hard/software issues within GSLIS for students, staff, and faculty.</li>
<li><strong>Tutors For All, Campus Representative; January 2012-May 2012</strong></li> </ul>
<ul><li>Enhance Tutors for All's social media pages through graphic design work and constant updating.</li> <p><strong>IES Technical Sales, Contracted Web Developer; May 2013-September 2013</strong></li>
<li>Generate new leads among college campus populations through social media outreach.</li></ul> <ul>
<li><strong>Simmons College, Teacher's Assistant in Computer Science; 2011-2013</strong></li> <li>Update the company website, developing and implementing design and operational upgrades.</li>
<ul><li>Create classwork, homework, and quizzes for students to complete.</li> <li>Assess and adapt to changing client needs through the development and deployment phase.</li>
<li>Grade students' work and report these grades to the instructor.</li></ul> </ul>
<li><strong>Simmons College, Lab Monitor for Computer Science Laboratory; 2011-2013</strong></li> <p><strong>Simmons College, Tutor in Computer Science; September 2012-2013</strong></li>
<ul><li>Assist students with troubleshooting soft/hardware issues.</li> <ul>
<li>Repair and maintain computers in the computer science laboratory.</li></ul> <li>Provide one-on-one tutoring for students in computer science classes.</li>
<li><strong>Not Your Average Joe's, Server; May 2011-present</strong></li> <li>Lead group study sessions for upcoming evaluations, tests, and projects.</li>
<ul><li>Communicate positively and effectively with guests and coworkers.</li> </ul>
<li>Assimilate guest information rapidly while anticipating guests' needs.</li></ul> <p><strong>Tutors For All, Campus Representative; January 2012-May 2012</strong></li>
<li><strong>Tutors For All, Lead Tutor; September 2010-May 2011</strong></li> <ul>
<ul><li>Mentor and lead a group of tutors, including reviewing and editing their lesson plans and progress reports.</li> <li>Enhance Tutors for All's social media pages through graphic design work and constant updating.</li>
<li>Evaluate student performance with progress reports and communicate that progress to their guardians.</li></ul> <li>Generate new leads among college campus populations through social media outreach.</li>
<li><strong>Cape Ann Community Theatre, Choreographer; March 2010-June 2010</strong></li> </ul>
<ul><li>Choreograph dance routines for the musical "How to Succeed in Business Without Really Trying."</li> <p><strong>Simmons College, Teacher's Assistant in Computer Science; 2011-2013</strong></li>
<li>Teach these routines to performers and provide dance direction throughout rehearsal process.</li></ul> <ul>
<li><strong>Hamilton-Wenham Regional Library, Page/Librarian's Assistant; September 2009- August 2010</strong></li> <li>Create classwork, homework, and quizzes for students to complete.</li>
<ul><li>Re-shelve library materials after patrons have returned them.</li> <li>Grade students' work and report these grades to the instructor.</li>
<li>Remain flexible and assist librarians in their various projects around the library.</li></li></ul> </ul>
<li><strong>Safe Harbor Tang Soo Do, Instructor & Camp Counselor; May 2005-September 2009</strong></li> <p><strong>Simmons College, Lab Monitor for Computer Science Laboratory; 2011-2013</strong></li>
<ul><li>Teach a one-hour martial arts class for students ages 4-13, of all ranks.</li> <ul>
<li>Lead the students in activities daily including arts and crafts, athletic activities, and many others.</li></ul> <li>Assist students with troubleshooting soft/hardware issues.</li>
<li>Repair and maintain computers in the computer science laboratory.</li>
</ul>
<p><strong>Not Your Average Joe's, Server; May 2011-present</strong></li>
<ul>
<li>Communicate positively and effectively with guests and coworkers.</li>
<li>Assimilate guest information rapidly while anticipating guests' needs.</li>
</ul>
<p><strong>Tutors For All, Lead Tutor; September 2010-May 2011</strong></li>
<ul>
<li>Mentor and lead a group of tutors, including reviewing and editing their lesson plans and progress reports.</li>
<li>Evaluate student performance with progress reports and communicate that progress to their guardians.</li>
</ul>
<p><strong>Cape Ann Community Theatre, Choreographer; March 2010-June 2010</strong></li>
<ul>
<li>Choreograph dance routines for the musical "How to Succeed in Business Without Really Trying."</li>
<li>Teach these routines to performers and provide dance direction throughout rehearsal process.</li>
</ul>
<p><strong>Hamilton-Wenham Regional Library, Page/Librarian's Assistant; September 2009- August 2010</strong></li>
<ul>
<li>Re-shelve library materials after patrons have returned them.</li>
<li>Remain flexible and assist librarians in their various projects around the library.</li>
</ul>
<p><strong>Safe Harbor Tang Soo Do, Instructor & Camp Counselor; May 2005-September 2009</strong></li>
<ul>
<li>Teach a one-hour martial arts class for students ages 4-13, of all ranks.</li>
<li>Lead the students in activities daily including arts and crafts, athletic activities, and many others.</li>
</ul> </ul>
</div> </div>
<!- -----------------END EMPLOYMENT--------------------- -->
<!-- PUB --> <!- -----------------BEGIN PUBLICATIONS--------------------- -->
<div class="tab-pane fade" id="pub"> <div class="tab-pane fade" id="pub">
<ul style="margin-top: 1em;"> <ul style="margin-top: 1em;">
<li>Steeves, V., Chirigati, F., Rampin R. (2017) "<a href="https://osf.io/preprints/lissa/5tm8d">Using ReproZip for Reproducibility and Library Services</a>", Pre-Print available on the <a href="https://osf.io/preprints/lissa">LIS Scholarship Archive</a>.</li> <li>Steeves, V., Chirigati, F., Rampin R. (2017) "<a href="https://osf.io/preprints/lissa/5tm8d">Using ReproZip for Reproducibility and Library Services</a>", Pre-Print available on the <a href="https://osf.io/preprints/lissa">LIS Scholarship Archive</a>.</li>
@ -156,85 +196,61 @@
<li>Blumenthal, K.-R., Griesinger, P., Julia, Peltzman, S., & Steeves, V. (2016, October 6) "<a href="https://osf.io/zndwq">What makes a digital steward: A competency profile based on the National Digital Stewardship Residencies</a>", iPres 2016: The 13th International Conference on Digital Preservation. Bern, Switzerland.</li> <li>Blumenthal, K.-R., Griesinger, P., Julia, Peltzman, S., & Steeves, V. (2016, October 6) "<a href="https://osf.io/zndwq">What makes a digital steward: A competency profile based on the National Digital Stewardship Residencies</a>", iPres 2016: The 13th International Conference on Digital Preservation. Bern, Switzerland.</li>
</ul> </ul>
</div> </div>
<!- -----------------END PUBLICATIONS--------------------- -->
<!-- PRESENT --> <!- -----------------BEGIN PRESENTATIONS--------------------- -->
<div class="tab-pane fade" id="pre"> <div class="tab-pane fade" id="pre">
<ul style="margin-top: 1em;"> <ul style="margin-top: 1em;">
<li><a href="https://vickysteeves.gitlab.io/2017-SciPy">Creating Reproducible Experiments with ReproZip</a>, Steeves, V. & <a href="https://remram.fr/" >Rampin, R.</a>, Chirigati, F. <a href="https://scipy2017.scipy.org/ehome/index.php?eventid=220975&"> SciPy 2017</a>, July 2017. Austin, Texas.</li> <li><a href="https://vickysteeves.gitlab.io/2017-SciPy">Creating Reproducible Experiments with ReproZip</a>, Steeves, V. & <a href="https://remram.fr/" >Rampin, R.</a>, Chirigati, F. <a href="https://scipy2017.scipy.org/ehome/index.php?eventid=220975&"> SciPy 2017</a>, July 2017. Austin, Texas.</li>
<li><a href="https://osf.io/sy2zf/">Open, Public Goods Infrastructure for Research Management & Discovery</a>, Spitzer, M., Steeves, V., & Hudson-Vitale, C., IASSIST 2017, May 2017. Kansas City, Kansas.</li> <li><a href="https://osf.io/sy2zf/">Open, Public Goods Infrastructure for Research Management & Discovery</a>, Spitzer, M., Steeves, V., & Hudson-Vitale, C., IASSIST 2017, May 2017. Kansas City, Kansas.</li>
<li><a href="https://gitlab.com/VickySteeves/2017-IASSIST-ReproZip">Reproducing and Preserving Research with ReproZip</a>, Steeves, V., & Rampin, R., IASSIST 2017, May 2017. Kansas City, Kansas.</li> <li><a href="https://gitlab.com/VickySteeves/2017-IASSIST-ReproZip">Reproducing and Preserving Research with ReproZip</a>, Steeves, V., & Rampin, R., IASSIST 2017, May 2017. Kansas City, Kansas.</li>
<li><a href="https://osf.io/s6fw9/">ReproZip for Reproducible Research</a>, Steeves, V., & Rampin, R., PresQT Workshop One, May 2017. Notre Dame University.</li> <li><a href="https://osf.io/s6fw9/">ReproZip for Reproducible Research</a>, Steeves, V., & Rampin, R., PresQT Workshop One, May 2017. Notre Dame University. Additional Roles:<li>Invited Panelist</li><li>Lead 2 breakout sessions focusing on reproducing workshop participants' work with ReproZip</li>
<ul>Additional Roles:<li>Invited Panelist</li><li>Lead 2 breakout sessions focusing on reproducing workshop participants' work with ReproZip</li></ul>
<li><a href="https://osf.io/umy6g/">Reproducible computational research in the publication cycle</a>, Steeves, V., Rampin, R., & Nüst, D, European Geosciences Union General Assembly 2017, April 2017. Vienna, Austria.</li> <li><a href="https://osf.io/umy6g/">Reproducible computational research in the publication cycle</a>, Steeves, V., Rampin, R., & Nüst, D, European Geosciences Union General Assembly 2017, April 2017. Vienna, Austria.</li>
<li><a href="https://drive.google.com/open?id=1ia7MYiIEoJF1D5G0YsolwsxHGPT6pmiwC8-yqUg1q_Q">Panel Presentation: Reproducibility: the What, Why, & How</a> in 'Open Science: Understanding Modern Research Practices.' Vicky Steeves, Robin Champieux, Jeff Leek, Brett Davidson, Eka Grguric. Association of Research and College Libraries Conference, March 25, 2017. Balitmore, Maryland.</li> <li><a href="https://drive.google.com/open?id=1ia7MYiIEoJF1D5G0YsolwsxHGPT6pmiwC8-yqUg1q_Q">Panel Presentation: Reproducibility: the What, Why, & How</a> in 'Open Science: Understanding Modern Research Practices.' Vicky Steeves, Robin Champieux, Jeff Leek, Brett Davidson, Eka Grguric. Association of Research and College Libraries Conference, March 25, 2017. Balitmore, Maryland.</li>
<li><a href="https://vickysteeves.github.io/2016-LITA-OpenDataServices/#/">"Using Openness as Foundation for Research Data Management Services"</a>. Wolf, N., & Steeves, V., <a href="http://litaforum.org/">Library and Information Technology Association Forum</a>, November 2016. Fort Worth, Texas.</li> <li><a href="https://vickysteeves.github.io/2016-LITA-OpenDataServices/#/">"Using Openness as Foundation for Research Data Management Services"</a>. Wolf, N., & Steeves, V., <a href="http://litaforum.org/">Library and Information Technology Association Forum</a>, November 2016. Fort Worth, Texas.</li>
<li><a href="https://osf.io/wvrpg/" >ReproZip: Reproducibility with Ease"</a>, Steeves, V. & <a href="https://remram.fr/" >Rampin, R.</a>, <a href="https://daspos.crc.nd.edu/index.php/workshops/container-strategies-for-data-software-preservation-that-promote-open-science" > DASPOS: Container Strategies for Data & Software Preservation that Promote Open Science</a>, May 2016. Notre Dame University. Additional Roles:<li>Invited Panelist</li><li>Lead 3 breakout sessions focusing on reproducing workshop participants' work with ReproZip</li>
<li><a href="https://osf.io/wvrpg/" >ReproZip: Reproducibility with Ease"</a>, Steeves, V. & <a href="https://remram.fr/" >Rampin, R.</a>, <a href="https://daspos.crc.nd.edu/index.php/workshops/container-strategies-for-data-software-preservation-that-promote-open-science" > DASPOS: Container Strategies for Data & Software Preservation that Promote Open Science</a>, May 2016. Notre Dame University.</li>
<ul>Additional Roles:<li>Invited Panelist</li><li>Lead 3 breakout sessions focusing on reproducing workshop participants' work with ReproZip</li></ul>
<li><a href="http://www.asis.org/rdap/program-2016/#poster" >"Collaborating to Create a Culture of Data Stewardship"</a> Poster presented with Andrew S. Gordon (Databrary) and Kevin B. Read (NYU Health Science Library), at the Research Data Access and Preservation Summit 2016</li> <li><a href="http://www.asis.org/rdap/program-2016/#poster" >"Collaborating to Create a Culture of Data Stewardship"</a> Poster presented with Andrew S. Gordon (Databrary) and Kevin B. Read (NYU Health Science Library), at the Research Data Access and Preservation Summit 2016</li>
<li><a href="http://escholarship.umassmed.edu/escience_symposium/2016/posters/2/" >"Bridging the Gap: Improving Data Services through Cross-Campus Collaboration"</a> (2016). Poster presented with Andrew S. Gordon (Databrary) and Kevin B. Read (NYU Health Science Library), 2016 University of Massachusetts and New England Area Librarian e-Science Symposium.</li> <li><a href="http://escholarship.umassmed.edu/escience_symposium/2016/posters/2/" >"Bridging the Gap: Improving Data Services through Cross-Campus Collaboration"</a> (2016). Poster presented with Andrew S. Gordon (Databrary) and Kevin B. Read (NYU Health Science Library), 2016 University of Massachusetts and New England Area Librarian e-Science Symposium.</li>
<li>Steeves, V., <a href="http://escience.washington.edu/people/jenny-mullenburg/" >Muilenburg, J.</a>, & <a href="https://bids.berkeley.edu/people/erik-mitchell" >Mitchell, E.</a> (2015, December 14) <a href="http://doi.org/10.17605/OSF.IO/RHGQ3">"Organizational Implications of Data Science Environments in Education, Research, and Research Management in Libraries"</a>. <a href="https://www.cni.org/events/membership-meetings/past-meetings/fall-2015" >Coalition of Networked Information Fall Meeting</a>. Washington D.C., USA. <a href="https://www.youtube.com/watch?v=L0G9JsPMEXY">Video</a></li>
<li>Steeves, V., <a href="http://escience.washington.edu/people/jenny-mullenburg/" >Muilenburg, J.</a>, & <a href="https://bids.berkeley.edu/people/erik-mitchell" >Mitchell, E.</a> (2015, December 14) <a href="http://doi.org/10.17605/OSF.IO/RHGQ3">"Organizational Implications of Data Science Environments in Education, Research, and Research Management in Libraries"</a>. <a href="https://www.cni.org/events/membership-meetings/past-meetings/fall-2015" >Coalition of Networked Information Fall Meeting</a>. Washington D.C., USA. </li>
<ul><li><a href="https://www.youtube.com/watch?v=L0G9JsPMEXY" >video</a></li></ul>
<li><a href="http://www2.archivists.org/sites/all/files/MAS%20Newsletter%20Summer%202015-new.pdf" >Preserving Scientific Research Data at the American Museum of Natural History</a>, MAS Newsletter, August 2015</li> <li><a href="http://www2.archivists.org/sites/all/files/MAS%20Newsletter%20Summer%202015-new.pdf" >Preserving Scientific Research Data at the American Museum of Natural History</a>, MAS Newsletter, August 2015</li>
<li>"Managing and Preservation Data Sets” METRO Webinar, July 2015</li> <li>"Managing and Preservation Data Sets” METRO Webinar, July 2015</li>
<li>"Panel Discussion with the National Digital Stewardship NY Residents” ARLIS/NY Meeting, March 2015</li> <li>"Panel Discussion with the National Digital Stewardship NY Residents” ARLIS/NY Meeting, March 2015</li>
<li>"The Next Generation of Digital Stewards: the NDSR Program” Mid-Atlantic Regional Archives & New England Archivists Conference, March 2015</li> <li>"The Next Generation of Digital Stewards: the NDSR Program” Mid-Atlantic Regional Archives & New England Archivists Conference, March 2015</li>
<li>"NDSR-NY Notes from the Field: Preserving Scientific Data at the American Museum of Natural History” Preservation and Archiving Special Interest Group Meeting, March 2015</li> <li>"NDSR-NY Notes from the Field: Preserving Scientific Data at the American Museum of Natural History” Preservation and Archiving Special Interest Group Meeting, March 2015</li>
<li>"The Next Frontier of Stewardship: the Value of Field Books in a Digital Age” Fieldbook Project Blog, National Museum of Natural History, February 2015</li> <li>"The Next Frontier of Stewardship: the Value of Field Books in a Digital Age” Fieldbook Project Blog, National Museum of Natural History, February 2015</li>
<li>"Scientific Data: A Needs Assessment Journey” American Library Association Mid-Winter Conference, January 2015</li> <li>"Scientific Data: A Needs Assessment Journey” American Library Association Mid-Winter Conference, January 2015</li>
<li>"NDSR-NY Panel Discussion” METRO 2015 Annual Conference, January 2015</li> <li>"NDSR-NY Panel Discussion” METRO 2015 Annual Conference, January 2015</li>
<li>"The Value of the NDSR: Residents and Mentors Weigh In” The SIGNAL Blog, Library of Congress, November 2015</li> <li>"The Value of the NDSR: Residents and Mentors Weigh In” The SIGNAL Blog, Library of Congress, November 2015</li>
<li>2014-2015 NDSR-NY Resident's Blog, Contributor, September 2014-June 2015 <li>2014-2015 NDSR-NY Resident's Blog, Contributor, September 2014-June 2015
<li>"A Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning” Simmons College Undergraduate Conference, April 2013</li> <li>"A Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning” Simmons College Undergraduate Conference, April 2013</li>
<li>"A Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning” Richard Tapia Celebration of Diversity in Computing Conference, February 2013</li> <li>"A Computational Linguistic Approach to Inflection in Human Speech and Difference in Meaning” Richard Tapia Celebration of Diversity in Computing Conference, February 2013</li>
<li>"No Place To Go: A Discussion on LGBTQ Youth Homeless in Boston” Simmons College Undergraduate Conference, April 2012</li> <li>"No Place To Go: A Discussion on LGBTQ Youth Homeless in Boston” Simmons College Undergraduate Conference, April 2012</li>
<li>"The Harm (or lack thereof) of Marijuana” Simmons College Undergraduate Conference, April 2011</li> <li>"The Harm (or lack thereof) of Marijuana” Simmons College Undergraduate Conference, April 2011</li>
</ul> </ul>
</div> </div>
<!- -----------------END PRESENTATIONS--------------------- -->
<!-- PROFESSIONAL OUTPUT --> <!- -----------BEGIN OTHER OUTPUT----------------- -->
<div class="tab-pane fade" id="mis"> <div class="tab-pane fade" id="mis">
<h4>Professional Memberships</h4> <ul style="margin-top: 1em;">
<ul>
<li>American Library Association; September 2014-present</li>
<li>Association for Computing Machinery; September 2012-present</li>
</ul>
<h4>Professional Output</h4>
<ul>
<li><a href="https://about.gitlab.com/2017/08/25/gitlab-and-reproducibility/">Guest blog post on GitLab and reproducibility</a></li> <li><a href="https://about.gitlab.com/2017/08/25/gitlab-and-reproducibility/">Guest blog post on GitLab and reproducibility</a></li>
<li><a href="http://www.simmons.edu/news/school-of-library-and-information-science/2017/may/vicky-steeves">Simmons SLIS Interview</a></li> <li><a href="http://www.simmons.edu/news/school-of-library-and-information-science/2017/may/vicky-steeves">Simmons SLIS Interview</a></li>
<li>Created a <a href="https://fyoaw.vickysteeves.com/">database</a> of women working in openness [<a href="https://gitlab.com/VickySteeves/Women-Leaders-Openness">source</a>]</li> <li>Created a <a href="https://fyoaw.vickysteeves.com/">database</a> of women working in openness [<a href="https://gitlab.com/VickySteeves/Women-Leaders-Openness">source</a>]</li>
<li><a href="https://libraries.io/gitlab/VickySteeves">Open Source Contributions</a></li> <li><a href="https://libraries.io/gitlab/VickySteeves">Open Source Contributions</a></li>
<li>Collaborated with Rémi Rampin to create a <a href="https://github.com/ViDA-NYU/reproducibility-news">Reproducibility RSS feed</a> to publish, in a single place, news, resources, and other links surrounding reproducibility, replication, and good and open science.</li> <li>Collaborated with Rémi Rampin to create a <a href="https://github.com/ViDA-NYU/reproducibility-news">Reproducibility RSS feed</a> to publish, in a single place, news, resources, and other links surrounding reproducibility, replication, and good and open science. Wrote a Twitter bot <a href="https://twitter.com/ReproFeed">@ReproFeed</a> to automatically tweet out RSS feed as it updates.</li>
<ul><li>Wrote a Twitter bot <a href="https://twitter.com/ReproFeed">@ReproFeed</a> to automatically tweet out RSS feed as it updates.</li></ul> <li>Ongoing collaborations with the <a href="https://cos.io/" >Center for Open Science</a>: Serve as an Open Science Framework Ambassador, Participant in the 2016 SHARE Curation Associates program. </li>
<li>Ongoing collaborations with the <a href="https://cos.io/" >Center for Open Science</a>:</li>
<ul><li>Serve as an Open Science Framework Ambassador</li>
<li>Participant in the 2016 SHARE Curation Associates program.</li></ul>
<li>Participant in the 2016 eDLF Data Management Cohort.</li> <li>Participant in the 2016 eDLF Data Management Cohort.</li>
<li>Created and currently maintain a soft incentives program to encourage RDM amongst library patrons, called the <a href="https://osf.io/85bnv/wiki/home/" >Research Data Management Badges</a>, where patrons can accrue points through attending Data Services classes, practicing good data management, and making their research open access to earn a badge. </li> <li>Created and currently maintain a soft incentives program to encourage RDM amongst library patrons, called the <a href="https://osf.io/85bnv/wiki/home/" >Research Data Management Badges</a>, where patrons can accrue points through attending Data Services classes, practicing good data management, and making their research open access to earn a badge. </li>
<li>Redeveloped and currently maintain the new Data Services blog, <a href="http://data-services.hosting.nyu.edu/" >Data Dispatch</a>, through piloting a new web hosting service for the Digital Scholarship Services called reClaim.</li> <li>Redeveloped and currently maintain the new Data Services blog, <a href="http://data-services.hosting.nyu.edu/" >Data Dispatch</a>, through piloting a new web hosting service for the Digital Scholarship Services called reClaim.</li>
<li>Organized <a href="https://reproduciblescience.org/nyu/events/reproducibility-symposium-2016/" >the NYU Reproducibility Symposium</a>, a showcase of tools to help make the reproducibility process easy. There were also keynotes and lightning talks on case studies showing how creating reproducible experiments has helped other research groups. May 2016.</li> <li>Organized <a href="https://reproduciblescience.org/nyu/events/reproducibility-symposium-2016/" >the NYU Reproducibility Symposium</a>, a showcase of tools to help make the reproducibility process easy. There were also keynotes and lightning talks on case studies showing how creating reproducible experiments has helped other research groups. May 2016.</li>
<li>Created and currently maintain <a href="https://reproduciblescience.org" >reproduciblescience.org</a>, a source of information for the general community on resources, news, and tools for reproducibility.</li> <li>Created and currently maintain <a href="https://reproduciblescience.org" >reproduciblescience.org</a>, a source of information for the general community on resources, news, and tools for reproducibility. Created and currently maintain <a href="https://reproduciblescience.org/nyu/" >reproduciblescience.org/nyu</a>, a source of information for the NYU community on events, resources, and expertise on campus for reproducibility.</li>
<ul><li>Created and currently maintain <a href="https://reproduciblescience.org/nyu/" >reproduciblescience.org/nyu</a>, a source of information for the NYU community on events, resources, and expertise on campus for reproducibility.</li></ul>
<li>Planned and executed <a href="https://loveyourdata.wordpress.com/" >"Love Your Data Week 2016"</a> at New York University with Nick Wolf, Kevin B. Read, and Alisa Surkis.</li> <li>Planned and executed <a href="https://loveyourdata.wordpress.com/" >"Love Your Data Week 2016"</a> at New York University with Nick Wolf, Kevin B. Read, and Alisa Surkis.</li>
</ul> </ul>
</div> </div>
<!- -----------END OTHER OUTPUT----------------- -->
</div>
<!- -----------END TAB CONTENT----------------- -->
</body> </body>
</html> </html>

View File

@ -0,0 +1,13 @@
This is a theme based on Bootstrap 4.
Should work:
* posts and pages (duh!)
* navbar
* pagination
* tag page
* galleries
With minor issues:
* listings (icons replaced by emoji — Bootstrap 4 provides none, and we are not forcing an icon theme yet)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,330 @@
/*!
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: transparent;
}
@-ms-viewport {
width: device-width;
}
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg:not(:root) {
overflow: hidden;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: .5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

9144
themes/bootstrap4/assets/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
.bg-dark{
background-color: #6695c4 !important;
}
.navbar{
font-size: 1.07em;
}
.navbar-brand{
font-size: 1.07em;
}

View File

@ -0,0 +1,250 @@
#container {
width: 960px;
margin: 0 auto;
}
#contentcolumn {
max-width: 760px;
}
#q {
width: 150px;
}
img {
max-width: 90%;
}
.postbox {
border-bottom: 2px solid darkgrey;
margin-bottom: 12px;
}
.titlebox {
text-align: right;
}
#addthisbox {margin-bottom: 12px;}
td.label {
/* Issue #290 */
background-color: inherit;
}
.footnote-reference {
/* Issue 290 */
vertical-align: super;
font-size: xx-small;
}
.caption {
/* Issue 292 */
text-align: center;
padding-top: 1em;
}
div.figure > img,
div.figure > a > img {
/* Issue 292 */
display: block;
margin-left: auto;
margin-right: auto;
}
blockquote p, blockquote {
font-size: 17.5px;
font-weight: 300;
line-height: 1.25;
}
ul.bricks > li {
display: inline;
background-color: lightblue;
padding: 8px;
border-radius: 5px;
line-height: 3;
white-space:nowrap;
margin: 3px;
}
.at300b, .stMainServices, .stButton, .stButton_gradient {
box-sizing: content-box;
}
pre, pre code {
white-space: pre;
word-wrap: normal;
overflow: auto;
}
article.post-micro {
font-family: Georgia, 'Times New Roman', Times, serif;
font-size: 1.5em;
}
.image-block {
display: inline-block;
}
.flowr_row {
width: 100%;
}
.tags {
padding-left: 0;
margin-left: -5px;
list-style: none;
text-align: center;
}
.tags > li {
display: inline-block;
padding: .25em .4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25rem;
color: #fff;
background-color: #868e96;
}
.tags > li a {
color: #fff;
}
.tags > li a:hover {
color: #fff;
text-decoration: none;
background-color: #6c757d;
}
.metadata p:before,
.postlist .listdate:after {
content: " — ";
}
.metadata p:first-of-type:before {
content: "";
}
.metadata p {
display: inline;
}
.posttranslations h3 {
display: inline;
font-size: 1em;
font-weight: bold;
}
.posttranslations h3:last-child {
display: none;
}
.entry-content {
margin-top: 1em;
}
.row {
margin: 0;
}
/* for alignment with Bootstrap's .entry-content styling */
.entry-summary {
margin-top: 1em;
}
/* Custom page footer */
#footer {
padding-top: 19px;
color: #777;
border-top: 1px solid #e5e5e5;
}
.codetable {
table-layout: fixed;
}
.codetable pre {
overflow-x: scroll;
}
/* hat tip bootstrap/html5 boilerplate */
@media print {
*, *:before, *:after {
font-family: Garamond, Junicode, serif;
}
body {
font-size: 12pt;
}
article .entry-title a[href]:after,
article .metadata a[href]:after,
article .tags a[href]:after {
content: "";
}
article .metadata .sourceline {
display: none;
}
article .metadata .linkline a[href]:after {
content: " (" attr(href) ")";
}
.navbar {
display: none;
}
}
pre {
border: 1px solid #ccc;
border-radius: 0.25rem;
padding: 0.75rem;
}
.postindexpager {
padding-bottom: 1rem;
}
ul.navbar-nav {
margin-top: 0;
}
ul.pager {
display: flex;
padding-left: 0;
list-style: none;
border-radius: .25rem;
padding-left: 0;
margin: 0.5rem 0;
}
ul.pager li.previous {
margin-right: auto;
display: inline;
}
ul.pager li.next {
margin-left: auto;
display: inline;
}
ul.pager li a {
display: inline;
position: relative;
padding: .5rem .75rem;
margin-left: -1px;
line-height: 1.25;
color: #007bff;
background-color: #fff;
border: 1px solid #ddd;
border-radius: .25rem;
}

View File

@ -0,0 +1,181 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Alert = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'alert'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.alert'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 150
const Selector = {
DISMISS : '[data-dismiss="alert"]'
}
const Event = {
CLOSE : `close${EVENT_KEY}`,
CLOSED : `closed${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
ALERT : 'alert',
FADE : 'fade',
SHOW : 'show'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Alert {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
close(element) {
element = element || this._element
const rootElement = this._getRootElement(element)
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_getRootElement(element) {
const selector = Util.getSelectorFromElement(element)
let parent = false
if (selector) {
parent = $(selector)[0]
}
if (!parent) {
parent = $(element).closest(`.${ClassName.ALERT}`)[0]
}
return parent
}
_triggerCloseEvent(element) {
const closeEvent = $.Event(Event.CLOSE)
$(element).trigger(closeEvent)
return closeEvent
}
_removeElement(element) {
$(element).removeClass(ClassName.SHOW)
if (!Util.supportsTransitionEnd() ||
!$(element).hasClass(ClassName.FADE)) {
this._destroyElement(element)
return
}
$(element)
.one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
.emulateTransitionEnd(TRANSITION_DURATION)
}
_destroyElement(element) {
$(element)
.detach()
.trigger(Event.CLOSED)
.remove()
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
}
if (config === 'close') {
data[config](this)
}
})
}
static _handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
alertInstance.close(this)
}
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
Event.CLICK_DATA_API,
Selector.DISMISS,
Alert._handleDismiss(new Alert())
)
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface
}
return Alert
})($)
export default Alert

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3894
themes/bootstrap4/assets/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,175 @@
import $ from 'jquery'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Button = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'button'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.button'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const ClassName = {
ACTIVE : 'active',
BUTTON : 'btn',
FOCUS : 'focus'
}
const Selector = {
DATA_TOGGLE_CARROT : '[data-toggle^="button"]',
DATA_TOGGLE : '[data-toggle="buttons"]',
INPUT : 'input',
ACTIVE : '.active',
BUTTON : '.btn'
}
const Event = {
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +
`blur${EVENT_KEY}${DATA_API_KEY}`
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Button {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
toggle() {
let triggerChangeEvent = true
let addAriaPressed = true
const rootElement = $(this._element).closest(
Selector.DATA_TOGGLE
)[0]
if (rootElement) {
const input = $(this._element).find(Selector.INPUT)[0]
if (input) {
if (input.type === 'radio') {
if (input.checked &&
$(this._element).hasClass(ClassName.ACTIVE)) {
triggerChangeEvent = false
} else {
const activeElement = $(rootElement).find(Selector.ACTIVE)[0]
if (activeElement) {
$(activeElement).removeClass(ClassName.ACTIVE)
}
}
}
if (triggerChangeEvent) {
if (input.hasAttribute('disabled') ||
rootElement.hasAttribute('disabled') ||
input.classList.contains('disabled') ||
rootElement.classList.contains('disabled')) {
return
}
input.checked = !$(this._element).hasClass(ClassName.ACTIVE)
$(input).trigger('change')
}
input.focus()
addAriaPressed = false
}
}
if (addAriaPressed) {
this._element.setAttribute('aria-pressed',
!$(this._element).hasClass(ClassName.ACTIVE))
}
if (triggerChangeEvent) {
$(this._element).toggleClass(ClassName.ACTIVE)
}
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
if (!data) {
data = new Button(this)
$(this).data(DATA_KEY, data)
}
if (config === 'toggle') {
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
event.preventDefault()
let button = event.target
if (!$(button).hasClass(ClassName.BUTTON)) {
button = $(button).closest(Selector.BUTTON)
}
Button._jQueryInterface.call($(button), 'toggle')
})
.on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {
const button = $(event.target).closest(Selector.BUTTON)[0]
$(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Button._jQueryInterface
$.fn[NAME].Constructor = Button
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Button._jQueryInterface
}
return Button
})($)
export default Button

View File

@ -0,0 +1,518 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Carousel = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'carousel'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 600
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const Default = {
interval : 5000,
keyboard : true,
slide : false,
pause : 'hover',
wrap : true
}
const DefaultType = {
interval : '(number|boolean)',
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
wrap : 'boolean'
}
const Direction = {
NEXT : 'next',
PREV : 'prev',
LEFT : 'left',
RIGHT : 'right'
}
const Event = {
SLIDE : `slide${EVENT_KEY}`,
SLID : `slid${EVENT_KEY}`,
KEYDOWN : `keydown${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
CAROUSEL : 'carousel',
ACTIVE : 'active',
SLIDE : 'slide',
RIGHT : 'carousel-item-right',
LEFT : 'carousel-item-left',
NEXT : 'carousel-item-next',
PREV : 'carousel-item-prev',
ITEM : 'carousel-item'
}
const Selector = {
ACTIVE : '.active',
ACTIVE_ITEM : '.active.carousel-item',
ITEM : '.carousel-item',
NEXT_PREV : '.carousel-item-next, .carousel-item-prev',
INDICATORS : '.carousel-indicators',
DATA_SLIDE : '[data-slide], [data-slide-to]',
DATA_RIDE : '[data-ride="carousel"]'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
this._items = null
this._interval = null
this._activeElement = null
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this._config = this._getConfig(config)
this._element = $(element)[0]
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]
this._addEventListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
next() {
if (!this._isSliding) {
this._slide(Direction.NEXT)
}
}
nextWhenVisible() {
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden &&
($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
this.next()
}
}
prev() {
if (!this._isSliding) {
this._slide(Direction.PREV)
}
}
pause(event) {
if (!event) {
this._isPaused = true
}
if ($(this._element).find(Selector.NEXT_PREV)[0] &&
Util.supportsTransitionEnd()) {
Util.triggerTransitionEnd(this._element)
this.cycle(true)
}
clearInterval(this._interval)
this._interval = null
}
cycle(event) {
if (!event) {
this._isPaused = false
}
if (this._interval) {
clearInterval(this._interval)
this._interval = null
}
if (this._config.interval && !this._isPaused) {
this._interval = setInterval(
(document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
this._config.interval
)
}
}
to(index) {
this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
const activeIndex = this._getItemIndex(this._activeElement)
if (index > this._items.length - 1 || index < 0) {
return
}
if (this._isSliding) {
$(this._element).one(Event.SLID, () => this.to(index))
return
}
if (activeIndex === index) {
this.pause()
this.cycle()
return
}
const direction = index > activeIndex
? Direction.NEXT
: Direction.PREV
this._slide(direction, this._items[index])
}
dispose() {
$(this._element).off(EVENT_KEY)
$.removeData(this._element, DATA_KEY)
this._items = null
this._config = null
this._element = null
this._interval = null
this._isPaused = null
this._isSliding = null
this._activeElement = null
this._indicatorsElement = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_addEventListeners() {
if (this._config.keyboard) {
$(this._element)
.on(Event.KEYDOWN, (event) => this._keydown(event))
}
if (this._config.pause === 'hover') {
$(this._element)
.on(Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
if ('ontouchstart' in document.documentElement) {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;
// here, we listen for touchend, explicitly pause the carousel
// (as if it's the second time we tap on it, mouseenter compat event
// is NOT fired) and after a timeout (to allow for mouse compatibility
// events to fire) we explicitly restart cycling
$(this._element).on(Event.TOUCHEND, () => {
this.pause()
if (this.touchTimeout) {
clearTimeout(this.touchTimeout)
}
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
})
}
}
}
_keydown(event) {
if (/input|textarea/i.test(event.target.tagName)) {
return
}
switch (event.which) {
case ARROW_LEFT_KEYCODE:
event.preventDefault()
this.prev()
break
case ARROW_RIGHT_KEYCODE:
event.preventDefault()
this.next()
break
default:
}
}
_getItemIndex(element) {
this._items = $.makeArray($(element).parent().find(Selector.ITEM))
return this._items.indexOf(element)
}
_getItemByDirection(direction, activeElement) {
const isNextDirection = direction === Direction.NEXT
const isPrevDirection = direction === Direction.PREV
const activeIndex = this._getItemIndex(activeElement)
const lastItemIndex = this._items.length - 1
const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
isNextDirection && activeIndex === lastItemIndex
if (isGoingToWrap && !this._config.wrap) {
return activeElement
}
const delta = direction === Direction.PREV ? -1 : 1
const itemIndex = (activeIndex + delta) % this._items.length
return itemIndex === -1
? this._items[this._items.length - 1] : this._items[itemIndex]
}
_triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex($(this._element).find(Selector.ACTIVE_ITEM)[0])
const slideEvent = $.Event(Event.SLIDE, {
relatedTarget,
direction: eventDirectionName,
from: fromIndex,
to: targetIndex
})
$(this._element).trigger(slideEvent)
return slideEvent
}
_setActiveIndicatorElement(element) {
if (this._indicatorsElement) {
$(this._indicatorsElement)
.find(Selector.ACTIVE)
.removeClass(ClassName.ACTIVE)
const nextIndicator = this._indicatorsElement.children[
this._getItemIndex(element)
]
if (nextIndicator) {
$(nextIndicator).addClass(ClassName.ACTIVE)
}
}
}
_slide(direction, element) {
const activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval)
let directionalClassName
let orderClassName
let eventDirectionName
if (direction === Direction.NEXT) {
directionalClassName = ClassName.LEFT
orderClassName = ClassName.NEXT
eventDirectionName = Direction.LEFT
} else {
directionalClassName = ClassName.RIGHT
orderClassName = ClassName.PREV
eventDirectionName = Direction.RIGHT
}
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
this._isSliding = false
return
}
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
if (slideEvent.isDefaultPrevented()) {
return
}
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
return
}
this._isSliding = true
if (isCycling) {
this.pause()
}
this._setActiveIndicatorElement(nextElement)
const slidEvent = $.Event(Event.SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
})
if (Util.supportsTransitionEnd() &&
$(this._element).hasClass(ClassName.SLIDE)) {
$(nextElement).addClass(orderClassName)
Util.reflow(nextElement)
$(activeElement).addClass(directionalClassName)
$(nextElement).addClass(directionalClassName)
$(activeElement)
.one(Util.TRANSITION_END, () => {
$(nextElement)
.removeClass(`${directionalClassName} ${orderClassName}`)
.addClass(ClassName.ACTIVE)
$(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
this._isSliding = false
setTimeout(() => $(this._element).trigger(slidEvent), 0)
})
.emulateTransitionEnd(TRANSITION_DURATION)
} else {
$(activeElement).removeClass(ClassName.ACTIVE)
$(nextElement).addClass(ClassName.ACTIVE)
this._isSliding = false
$(this._element).trigger(slidEvent)
}
if (isCycling) {
this.cycle()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
let _config = {
...Default,
...$(this).data()
}
if (typeof config === 'object') {
_config = {
..._config,
...config
}
}
const action = typeof config === 'string' ? config : _config.slide
if (!data) {
data = new Carousel(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'number') {
data.to(config)
} else if (typeof action === 'string') {
if (typeof data[action] === 'undefined') {
throw new TypeError(`No method named "${action}"`)
}
data[action]()
} else if (_config.interval) {
data.pause()
data.cycle()
}
})
}
static _dataApiClickHandler(event) {
const selector = Util.getSelectorFromElement(this)
if (!selector) {
return
}
const target = $(selector)[0]
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
return
}
const config = {
...$(target).data(),
...$(this).data()
}
const slideIndex = this.getAttribute('data-slide-to')
if (slideIndex) {
config.interval = false
}
Carousel._jQueryInterface.call($(target), config)
if (slideIndex) {
$(target).data(DATA_KEY).to(slideIndex)
}
event.preventDefault()
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
$(window).on(Event.LOAD_DATA_API, () => {
$(Selector.DATA_RIDE).each(function () {
const $carousel = $(this)
Carousel._jQueryInterface.call($carousel, $carousel.data())
})
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Carousel._jQueryInterface
$.fn[NAME].Constructor = Carousel
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Carousel._jQueryInterface
}
return Carousel
})($)
export default Carousel

View File

@ -0,0 +1,403 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Collapse = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'collapse'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 600
const Default = {
toggle : true,
parent : ''
}
const DefaultType = {
toggle : 'boolean',
parent : '(string|element)'
}
const Event = {
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
SHOW : 'show',
COLLAPSE : 'collapse',
COLLAPSING : 'collapsing',
COLLAPSED : 'collapsed'
}
const Dimension = {
WIDTH : 'width',
HEIGHT : 'height'
}
const Selector = {
ACTIVES : '.show, .collapsing',
DATA_TOGGLE : '[data-toggle="collapse"]'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Collapse {
constructor(element, config) {
this._isTransitioning = false
this._element = element
this._config = this._getConfig(config)
this._triggerArray = $.makeArray($(
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
))
const tabToggles = $(Selector.DATA_TOGGLE)
for (let i = 0; i < tabToggles.length; i++) {
const elem = tabToggles[i]
const selector = Util.getSelectorFromElement(elem)
if (selector !== null && $(selector).filter(element).length > 0) {
this._selector = selector
this._triggerArray.push(elem)
}
}
this._parent = this._config.parent ? this._getParent() : null
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
}
if (this._config.toggle) {
this.toggle()
}
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
toggle() {
if ($(this._element).hasClass(ClassName.SHOW)) {
this.hide()
} else {
this.show()
}
}
show() {
if (this._isTransitioning ||
$(this._element).hasClass(ClassName.SHOW)) {
return
}
let actives
let activesData
if (this._parent) {
actives = $.makeArray(
$(this._parent)
.find(Selector.ACTIVES)
.filter(`[data-parent="${this._config.parent}"]`)
)
if (actives.length === 0) {
actives = null
}
}
if (actives) {
activesData = $(actives).not(this._selector).data(DATA_KEY)
if (activesData && activesData._isTransitioning) {
return
}
}
const startEvent = $.Event(Event.SHOW)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
if (actives) {
Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')
if (!activesData) {
$(actives).data(DATA_KEY, null)
}
}
const dimension = this._getDimension()
$(this._element)
.removeClass(ClassName.COLLAPSE)
.addClass(ClassName.COLLAPSING)
this._element.style[dimension] = 0
if (this._triggerArray.length > 0) {
$(this._triggerArray)
.removeClass(ClassName.COLLAPSED)
.attr('aria-expanded', true)
}
this.setTransitioning(true)
const complete = () => {
$(this._element)
.removeClass(ClassName.COLLAPSING)
.addClass(ClassName.COLLAPSE)
.addClass(ClassName.SHOW)
this._element.style[dimension] = ''
this.setTransitioning(false)
$(this._element).trigger(Event.SHOWN)
}
if (!Util.supportsTransitionEnd()) {
complete()
return
}
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
const scrollSize = `scroll${capitalizedDimension}`
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(TRANSITION_DURATION)
this._element.style[dimension] = `${this._element[scrollSize]}px`
}
hide() {
if (this._isTransitioning ||
!$(this._element).hasClass(ClassName.SHOW)) {
return
}
const startEvent = $.Event(Event.HIDE)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
const dimension = this._getDimension()
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
Util.reflow(this._element)
$(this._element)
.addClass(ClassName.COLLAPSING)
.removeClass(ClassName.COLLAPSE)
.removeClass(ClassName.SHOW)
if (this._triggerArray.length > 0) {
for (let i = 0; i < this._triggerArray.length; i++) {
const trigger = this._triggerArray[i]
const selector = Util.getSelectorFromElement(trigger)
if (selector !== null) {
const $elem = $(selector)
if (!$elem.hasClass(ClassName.SHOW)) {
$(trigger).addClass(ClassName.COLLAPSED)
.attr('aria-expanded', false)
}
}
}
}
this.setTransitioning(true)
const complete = () => {
this.setTransitioning(false)
$(this._element)
.removeClass(ClassName.COLLAPSING)
.addClass(ClassName.COLLAPSE)
.trigger(Event.HIDDEN)
}
this._element.style[dimension] = ''
if (!Util.supportsTransitionEnd()) {
complete()
return
}
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(TRANSITION_DURATION)
}
setTransitioning(isTransitioning) {
this._isTransitioning = isTransitioning
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._config = null
this._parent = null
this._element = null
this._triggerArray = null
this._isTransitioning = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
config.toggle = Boolean(config.toggle) // Coerce string values
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getDimension() {
const hasWidth = $(this._element).hasClass(Dimension.WIDTH)
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
}
_getParent() {
let parent = null
if (Util.isElement(this._config.parent)) {
parent = this._config.parent
// It's a jQuery object
if (typeof this._config.parent.jquery !== 'undefined') {
parent = this._config.parent[0]
}
} else {
parent = $(this._config.parent)[0]
}
const selector =
`[data-toggle="collapse"][data-parent="${this._config.parent}"]`
$(parent).find(selector).each((i, element) => {
this._addAriaAndCollapsedClass(
Collapse._getTargetFromElement(element),
[element]
)
})
return parent
}
_addAriaAndCollapsedClass(element, triggerArray) {
if (element) {
const isOpen = $(element).hasClass(ClassName.SHOW)
if (triggerArray.length > 0) {
$(triggerArray)
.toggleClass(ClassName.COLLAPSED, !isOpen)
.attr('aria-expanded', isOpen)
}
}
}
// Static
static _getTargetFromElement(element) {
const selector = Util.getSelectorFromElement(element)
return selector ? $(selector)[0] : null
}
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
const _config = {
...Default,
...$this.data(),
...typeof config === 'object' && config
}
if (!data && _config.toggle && /show|hide/.test(config)) {
_config.toggle = false
}
if (!data) {
data = new Collapse(this, _config)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
if (event.currentTarget.tagName === 'A') {
event.preventDefault()
}
const $trigger = $(this)
const selector = Util.getSelectorFromElement(this)
$(selector).each(function () {
const $target = $(this)
const data = $target.data(DATA_KEY)
const config = data ? 'toggle' : $trigger.data()
Collapse._jQueryInterface.call($target, config)
})
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Collapse._jQueryInterface
$.fn[NAME].Constructor = Collapse
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Collapse._jQueryInterface
}
return Collapse
})($)
export default Collapse

View File

@ -0,0 +1,470 @@
import $ from 'jquery'
import Popper from 'popper.js'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Dropdown = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'dropdown'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,
KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,
KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DISABLED : 'disabled',
SHOW : 'show',
DROPUP : 'dropup',
DROPRIGHT : 'dropright',
DROPLEFT : 'dropleft',
MENURIGHT : 'dropdown-menu-right',
MENULEFT : 'dropdown-menu-left',
POSITION_STATIC : 'position-static'
}
const Selector = {
DATA_TOGGLE : '[data-toggle="dropdown"]',
FORM_CHILD : '.dropdown form',
MENU : '.dropdown-menu',
NAVBAR_NAV : '.navbar-nav',
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)'
}
const AttachmentMap = {
TOP : 'top-start',
TOPEND : 'top-end',
BOTTOM : 'bottom-start',
BOTTOMEND : 'bottom-end',
RIGHT : 'right-start',
RIGHTEND : 'right-end',
LEFT : 'left-start',
LEFTEND : 'left-end'
}
const Default = {
offset : 0,
flip : true,
boundary : 'scrollParent'
}
const DefaultType = {
offset : '(number|string|function)',
flip : 'boolean',
boundary : '(string|element)'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Dropdown {
constructor(element, config) {
this._element = element
this._popper = null
this._config = this._getConfig(config)
this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar()
this._addEventListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DefaultType() {
return DefaultType
}
// Public
toggle() {
if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {
return
}
const parent = Dropdown._getParentFromElement(this._element)
const isActive = $(this._menu).hasClass(ClassName.SHOW)
Dropdown._clearMenus()
if (isActive) {
return
}
const relatedTarget = {
relatedTarget: this._element
}
const showEvent = $.Event(Event.SHOW, relatedTarget)
$(parent).trigger(showEvent)
if (showEvent.isDefaultPrevented()) {
return
}
// Disable totally Popper.js for Dropdown in Navbar
if (!this._inNavbar) {
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')
}
let element = this._element
// For dropup with alignment we use the parent as popper container
if ($(parent).hasClass(ClassName.DROPUP)) {
if ($(this._menu).hasClass(ClassName.MENULEFT) || $(this._menu).hasClass(ClassName.MENURIGHT)) {
element = parent
}
}
// If boundary is not `scrollParent`, then set position to `static`
// to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') {
$(parent).addClass(ClassName.POSITION_STATIC)
}
this._popper = new Popper(element, this._menu, this._getPopperConfig())
}
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement &&
$(parent).closest(Selector.NAVBAR_NAV).length === 0) {
$('body').children().on('mouseover', null, $.noop)
}
this._element.focus()
this._element.setAttribute('aria-expanded', true)
$(this._menu).toggleClass(ClassName.SHOW)
$(parent)
.toggleClass(ClassName.SHOW)
.trigger($.Event(Event.SHOWN, relatedTarget))
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._element).off(EVENT_KEY)
this._element = null
this._menu = null
if (this._popper !== null) {
this._popper.destroy()
this._popper = null
}
}
update() {
this._inNavbar = this._detectNavbar()
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Private
_addEventListeners() {
$(this._element).on(Event.CLICK, (event) => {
event.preventDefault()
event.stopPropagation()
this.toggle()
})
}
_getConfig(config) {
config = {
...this.constructor.Default,
...$(this._element).data(),
...config
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_getMenuElement() {
if (!this._menu) {
const parent = Dropdown._getParentFromElement(this._element)
this._menu = $(parent).find(Selector.MENU)[0]
}
return this._menu
}
_getPlacement() {
const $parentDropdown = $(this._element).parent()
let placement = AttachmentMap.BOTTOM
// Handle dropup
if ($parentDropdown.hasClass(ClassName.DROPUP)) {
placement = AttachmentMap.TOP
if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.TOPEND
}
} else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
placement = AttachmentMap.RIGHT
} else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
placement = AttachmentMap.LEFT
} else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND
}
return placement
}
_detectNavbar() {
return $(this._element).closest('.navbar').length > 0
}
_getPopperConfig() {
const offsetConf = {}
if (typeof this._config.offset === 'function') {
offsetConf.fn = (data) => {
data.offsets = {
...data.offsets,
...this._config.offset(data.offsets) || {}
}
return data
}
} else {
offsetConf.offset = this._config.offset
}
const popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: offsetConf,
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
}
return popperConfig
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data) {
data = new Dropdown(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
static _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return
}
const toggles = $.makeArray($(Selector.DATA_TOGGLE))
for (let i = 0; i < toggles.length; i++) {
const parent = Dropdown._getParentFromElement(toggles[i])
const context = $(toggles[i]).data(DATA_KEY)
const relatedTarget = {
relatedTarget: toggles[i]
}
if (!context) {
continue
}
const dropdownMenu = context._menu
if (!$(parent).hasClass(ClassName.SHOW)) {
continue
}
if (event && (event.type === 'click' &&
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
$.contains(parent, event.target)) {
continue
}
const hideEvent = $.Event(Event.HIDE, relatedTarget)
$(parent).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
continue
}
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$('body').children().off('mouseover', null, $.noop)
}
toggles[i].setAttribute('aria-expanded', 'false')
$(dropdownMenu).removeClass(ClassName.SHOW)
$(parent)
.removeClass(ClassName.SHOW)
.trigger($.Event(Event.HIDDEN, relatedTarget))
}
}
static _getParentFromElement(element) {
let parent
const selector = Util.getSelectorFromElement(element)
if (selector) {
parent = $(selector)[0]
}
return parent || element.parentNode
}
// eslint-disable-next-line complexity
static _dataApiKeydownHandler(event) {
// If not input/textarea:
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
// If input/textarea:
// - If space key => not a dropdown command
// - If key is other than escape
// - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName)
? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
$(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
return
}
event.preventDefault()
event.stopPropagation()
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
return
}
const parent = Dropdown._getParentFromElement(this)
const isActive = $(parent).hasClass(ClassName.SHOW)
if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) ||
isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
const toggle = $(parent).find(Selector.DATA_TOGGLE)[0]
$(toggle).trigger('focus')
}
$(this).trigger('click')
return
}
const items = $(parent).find(Selector.VISIBLE_ITEMS).get()
if (items.length === 0) {
return
}
let index = items.indexOf(event.target)
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
index--
}
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
index++
}
if (index < 0) {
index = 0
}
items[index].focus()
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
.on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)
.on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
event.stopPropagation()
Dropdown._jQueryInterface.call($(this), 'toggle')
})
.on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
e.stopPropagation()
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface
$.fn[NAME].Constructor = Dropdown
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Dropdown._jQueryInterface
}
return Dropdown
})($, Popper)
export default Dropdown

View File

@ -0,0 +1,50 @@
import $ from 'jquery'
import Alert from './alert'
import Button from './button'
import Carousel from './carousel'
import Collapse from './collapse'
import Dropdown from './dropdown'
import Modal from './modal'
import Popover from './popover'
import Scrollspy from './scrollspy'
import Tab from './tab'
import Tooltip from './tooltip'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-alpha.6): index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
(($) => {
if (typeof $ === 'undefined') {
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.')
}
const version = $.fn.jquery.split(' ')[0].split('.')
const minMajor = 1
const ltMajor = 2
const minMinor = 9
const minPatch = 1
const maxMajor = 4
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0')
}
})($)
export {
Util,
Alert,
Button,
Carousel,
Collapse,
Dropdown,
Modal,
Popover,
Scrollspy,
Tab,
Tooltip
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,578 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Modal = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'modal'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.modal'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 300
const BACKDROP_TRANSITION_DURATION = 150
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const Default = {
backdrop : true,
keyboard : true,
focus : true,
show : true
}
const DefaultType = {
backdrop : '(boolean|string)',
keyboard : 'boolean',
focus : 'boolean',
show : 'boolean'
}
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`,
RESIZE : `resize${EVENT_KEY}`,
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,
KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,
MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,
MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
SCROLLBAR_MEASURER : 'modal-scrollbar-measure',
BACKDROP : 'modal-backdrop',
OPEN : 'modal-open',
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
DIALOG : '.modal-dialog',
DATA_TOGGLE : '[data-toggle="modal"]',
DATA_DISMISS : '[data-dismiss="modal"]',
FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
STICKY_CONTENT : '.sticky-top',
NAVBAR_TOGGLER : '.navbar-toggler'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Modal {
constructor(element, config) {
this._config = this._getConfig(config)
this._element = element
this._dialog = $(element).find(Selector.DIALOG)[0]
this._backdrop = null
this._isShown = false
this._isBodyOverflowing = false
this._ignoreBackdropClick = false
this._originalBodyPadding = 0
this._scrollbarWidth = 0
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget)
}
show(relatedTarget) {
if (this._isTransitioning || this._isShown) {
return
}
if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
this._isTransitioning = true
}
const showEvent = $.Event(Event.SHOW, {
relatedTarget
})
$(this._element).trigger(showEvent)
if (this._isShown || showEvent.isDefaultPrevented()) {
return
}
this._isShown = true
this._checkScrollbar()
this._setScrollbar()
this._adjustDialog()
$(document.body).addClass(ClassName.OPEN)
this._setEscapeEvent()
this._setResizeEvent()
$(this._element).on(
Event.CLICK_DISMISS,
Selector.DATA_DISMISS,
(event) => this.hide(event)
)
$(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {
$(this._element).one(Event.MOUSEUP_DISMISS, (event) => {
if ($(event.target).is(this._element)) {
this._ignoreBackdropClick = true
}
})
})
this._showBackdrop(() => this._showElement(relatedTarget))
}
hide(event) {
if (event) {
event.preventDefault()
}
if (this._isTransitioning || !this._isShown) {
return
}
const hideEvent = $.Event(Event.HIDE)
$(this._element).trigger(hideEvent)
if (!this._isShown || hideEvent.isDefaultPrevented()) {
return
}
this._isShown = false
const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)
if (transition) {
this._isTransitioning = true
}
this._setEscapeEvent()
this._setResizeEvent()
$(document).off(Event.FOCUSIN)
$(this._element).removeClass(ClassName.SHOW)
$(this._element).off(Event.CLICK_DISMISS)
$(this._dialog).off(Event.MOUSEDOWN_DISMISS)
if (transition) {
$(this._element)
.one(Util.TRANSITION_END, (event) => this._hideModal(event))
.emulateTransitionEnd(TRANSITION_DURATION)
} else {
this._hideModal()
}
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(window, document, this._element, this._backdrop).off(EVENT_KEY)
this._config = null
this._element = null
this._dialog = null
this._backdrop = null
this._isShown = null
this._isBodyOverflowing = null
this._ignoreBackdropClick = null
this._scrollbarWidth = null
}
handleUpdate() {
this._adjustDialog()
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_showElement(relatedTarget) {
const transition = Util.supportsTransitionEnd() &&
$(this._element).hasClass(ClassName.FADE)
if (!this._element.parentNode ||
this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
// Don't move modal's DOM position
document.body.appendChild(this._element)
}
this._element.style.display = 'block'
this._element.removeAttribute('aria-hidden')
this._element.scrollTop = 0
if (transition) {
Util.reflow(this._element)
}
$(this._element).addClass(ClassName.SHOW)
if (this._config.focus) {
this._enforceFocus()
}
const shownEvent = $.Event(Event.SHOWN, {
relatedTarget
})
const transitionComplete = () => {
if (this._config.focus) {
this._element.focus()
}
this._isTransitioning = false
$(this._element).trigger(shownEvent)
}
if (transition) {
$(this._dialog)
.one(Util.TRANSITION_END, transitionComplete)
.emulateTransitionEnd(TRANSITION_DURATION)
} else {
transitionComplete()
}
}
_enforceFocus() {
$(document)
.off(Event.FOCUSIN) // Guard against infinite focus loop
.on(Event.FOCUSIN, (event) => {
if (document !== event.target &&
this._element !== event.target &&
$(this._element).has(event.target).length === 0) {
this._element.focus()
}
})
}
_setEscapeEvent() {
if (this._isShown && this._config.keyboard) {
$(this._element).on(Event.KEYDOWN_DISMISS, (event) => {
if (event.which === ESCAPE_KEYCODE) {
event.preventDefault()
this.hide()
}
})
} else if (!this._isShown) {
$(this._element).off(Event.KEYDOWN_DISMISS)
}
}
_setResizeEvent() {
if (this._isShown) {
$(window).on(Event.RESIZE, (event) => this.handleUpdate(event))
} else {
$(window).off(Event.RESIZE)
}
}
_hideModal() {
this._element.style.display = 'none'
this._element.setAttribute('aria-hidden', true)
this._isTransitioning = false
this._showBackdrop(() => {
$(document.body).removeClass(ClassName.OPEN)
this._resetAdjustments()
this._resetScrollbar()
$(this._element).trigger(Event.HIDDEN)
})
}
_removeBackdrop() {
if (this._backdrop) {
$(this._backdrop).remove()
this._backdrop = null
}
}
_showBackdrop(callback) {
const animate = $(this._element).hasClass(ClassName.FADE)
? ClassName.FADE : ''
if (this._isShown && this._config.backdrop) {
const doAnimate = Util.supportsTransitionEnd() && animate
this._backdrop = document.createElement('div')
this._backdrop.className = ClassName.BACKDROP
if (animate) {
$(this._backdrop).addClass(animate)
}
$(this._backdrop).appendTo(document.body)
$(this._element).on(Event.CLICK_DISMISS, (event) => {
if (this._ignoreBackdropClick) {
this._ignoreBackdropClick = false
return
}
if (event.target !== event.currentTarget) {
return
}
if (this._config.backdrop === 'static') {
this._element.focus()
} else {
this.hide()
}
})
if (doAnimate) {
Util.reflow(this._backdrop)
}
$(this._backdrop).addClass(ClassName.SHOW)
if (!callback) {
return
}
if (!doAnimate) {
callback()
return
}
$(this._backdrop)
.one(Util.TRANSITION_END, callback)
.emulateTransitionEnd(BACKDROP_TRANSITION_DURATION)
} else if (!this._isShown && this._backdrop) {
$(this._backdrop).removeClass(ClassName.SHOW)
const callbackRemove = () => {
this._removeBackdrop()
if (callback) {
callback()
}
}
if (Util.supportsTransitionEnd() &&
$(this._element).hasClass(ClassName.FADE)) {
$(this._backdrop)
.one(Util.TRANSITION_END, callbackRemove)
.emulateTransitionEnd(BACKDROP_TRANSITION_DURATION)
} else {
callbackRemove()
}
} else if (callback) {
callback()
}
}
// ----------------------------------------------------------------------
// the following methods are used to handle overflowing modals
// todo (fat): these should probably be refactored out of modal.js
// ----------------------------------------------------------------------
_adjustDialog() {
const isModalOverflowing =
this._element.scrollHeight > document.documentElement.clientHeight
if (!this._isBodyOverflowing && isModalOverflowing) {
this._element.style.paddingLeft = `${this._scrollbarWidth}px`
}
if (this._isBodyOverflowing && !isModalOverflowing) {
this._element.style.paddingRight = `${this._scrollbarWidth}px`
}
}
_resetAdjustments() {
this._element.style.paddingLeft = ''
this._element.style.paddingRight = ''
}
_checkScrollbar() {
const rect = document.body.getBoundingClientRect()
this._isBodyOverflowing = rect.left + rect.right < window.innerWidth
this._scrollbarWidth = this._getScrollbarWidth()
}
_setScrollbar() {
if (this._isBodyOverflowing) {
// Note: DOMNode.style.paddingRight returns the actual value or '' if not set
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
// Adjust fixed content padding
$(Selector.FIXED_CONTENT).each((index, element) => {
const actualPadding = $(element)[0].style.paddingRight
const calculatedPadding = $(element).css('padding-right')
$(element).data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
})
// Adjust sticky content margin
$(Selector.STICKY_CONTENT).each((index, element) => {
const actualMargin = $(element)[0].style.marginRight
const calculatedMargin = $(element).css('margin-right')
$(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)
})
// Adjust navbar-toggler margin
$(Selector.NAVBAR_TOGGLER).each((index, element) => {
const actualMargin = $(element)[0].style.marginRight
const calculatedMargin = $(element).css('margin-right')
$(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) + this._scrollbarWidth}px`)
})
// Adjust body padding
const actualPadding = document.body.style.paddingRight
const calculatedPadding = $('body').css('padding-right')
$('body').data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
}
}
_resetScrollbar() {
// Restore fixed content padding
$(Selector.FIXED_CONTENT).each((index, element) => {
const padding = $(element).data('padding-right')
if (typeof padding !== 'undefined') {
$(element).css('padding-right', padding).removeData('padding-right')
}
})
// Restore sticky content and navbar-toggler margin
$(`${Selector.STICKY_CONTENT}, ${Selector.NAVBAR_TOGGLER}`).each((index, element) => {
const margin = $(element).data('margin-right')
if (typeof margin !== 'undefined') {
$(element).css('margin-right', margin).removeData('margin-right')
}
})
// Restore body padding
const padding = $('body').data('padding-right')
if (typeof padding !== 'undefined') {
$('body').css('padding-right', padding).removeData('padding-right')
}
}
_getScrollbarWidth() { // thx d.walsh
const scrollDiv = document.createElement('div')
scrollDiv.className = ClassName.SCROLLBAR_MEASURER
document.body.appendChild(scrollDiv)
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
document.body.removeChild(scrollDiv)
return scrollbarWidth
}
// Static
static _jQueryInterface(config, relatedTarget) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = {
...Modal.Default,
...$(this).data(),
...typeof config === 'object' && config
}
if (!data) {
data = new Modal(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config](relatedTarget)
} else if (_config.show) {
data.show(relatedTarget)
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
let target
const selector = Util.getSelectorFromElement(this)
if (selector) {
target = $(selector)[0]
}
const config = $(target).data(DATA_KEY)
? 'toggle' : {
...$(target).data(),
...$(this).data()
}
if (this.tagName === 'A' || this.tagName === 'AREA') {
event.preventDefault()
}
const $target = $(target).one(Event.SHOW, (showEvent) => {
if (showEvent.isDefaultPrevented()) {
// Only register focus restorer if modal will actually get shown
return
}
$target.one(Event.HIDDEN, () => {
if ($(this).is(':visible')) {
this.focus()
}
})
})
Modal._jQueryInterface.call($(target), config, this)
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Modal._jQueryInterface
$.fn[NAME].Constructor = Modal
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Modal._jQueryInterface
}
return Modal
})($)
export default Modal

View File

@ -0,0 +1,188 @@
import $ from 'jquery'
import Tooltip from './tooltip'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Popover = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'popover'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const CLASS_PREFIX = 'bs-popover'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const Default = {
...Tooltip.Default,
placement : 'right',
trigger : 'click',
content : '',
template : '<div class="popover" role="tooltip">' +
'<div class="arrow"></div>' +
'<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div></div>'
}
const DefaultType = {
...Tooltip.DefaultType,
content : '(string|element|function)'
}
const ClassName = {
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
TITLE : '.popover-header',
CONTENT : '.popover-body'
}
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Popover extends Tooltip {
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get NAME() {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
// Overrides
isWithContent() {
return this.getTitle() || this._getContent()
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const $tip = $(this.getTipElement())
// We use append for html objects to maintain js events
this.setElementContent($tip.find(Selector.TITLE), this.getTitle())
let content = this._getContent()
if (typeof content === 'function') {
content = content.call(this.element)
}
this.setElementContent($tip.find(Selector.CONTENT), content)
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
}
// Private
_getContent() {
return this.element.getAttribute('data-content') ||
this.config.content
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''))
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data && /destroy|hide/.test(config)) {
return
}
if (!data) {
data = new Popover(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Popover._jQueryInterface
$.fn[NAME].Constructor = Popover
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Popover._jQueryInterface
}
return Popover
})($)
export default Popover

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,329 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const ScrollSpy = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'scrollspy'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
offset : 10,
method : 'auto',
target : ''
}
const DefaultType = {
offset : 'number',
method : 'string',
target : '(string|element)'
}
const Event = {
ACTIVATE : `activate${EVENT_KEY}`,
SCROLL : `scroll${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_ITEM : 'dropdown-item',
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active'
}
const Selector = {
DATA_SPY : '[data-spy="scroll"]',
ACTIVE : '.active',
NAV_LIST_GROUP : '.nav, .list-group',
NAV_LINKS : '.nav-link',
NAV_ITEMS : '.nav-item',
LIST_ITEMS : '.list-group-item',
DROPDOWN : '.dropdown',
DROPDOWN_ITEMS : '.dropdown-item',
DROPDOWN_TOGGLE : '.dropdown-toggle'
}
const OffsetMethod = {
OFFSET : 'offset',
POSITION : 'position'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class ScrollSpy {
constructor(element, config) {
this._element = element
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config)
this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +
`${this._config.target} ${Selector.LIST_ITEMS},` +
`${this._config.target} ${Selector.DROPDOWN_ITEMS}`
this._offsets = []
this._targets = []
this._activeTarget = null
this._scrollHeight = 0
$(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))
this.refresh()
this._process()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window
? OffsetMethod.OFFSET : OffsetMethod.POSITION
const offsetMethod = this._config.method === 'auto'
? autoMethod : this._config.method
const offsetBase = offsetMethod === OffsetMethod.POSITION
? this._getScrollTop() : 0
this._offsets = []
this._targets = []
this._scrollHeight = this._getScrollHeight()
const targets = $.makeArray($(this._selector))
targets
.map((element) => {
let target
const targetSelector = Util.getSelectorFromElement(element)
if (targetSelector) {
target = $(targetSelector)[0]
}
if (target) {
const targetBCR = target.getBoundingClientRect()
if (targetBCR.width || targetBCR.height) {
// TODO (fat): remove sketch reliance on jQuery position/offset
return [
$(target)[offsetMethod]().top + offsetBase,
targetSelector
]
}
}
return null
})
.filter((item) => item)
.sort((a, b) => a[0] - b[0])
.forEach((item) => {
this._offsets.push(item[0])
this._targets.push(item[1])
})
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._scrollElement).off(EVENT_KEY)
this._element = null
this._scrollElement = null
this._config = null
this._selector = null
this._offsets = null
this._targets = null
this._activeTarget = null
this._scrollHeight = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
if (typeof config.target !== 'string') {
let id = $(config.target).attr('id')
if (!id) {
id = Util.getUID(NAME)
$(config.target).attr('id', id)
}
config.target = `#${id}`
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getScrollTop() {
return this._scrollElement === window
? this._scrollElement.pageYOffset : this._scrollElement.scrollTop
}
_getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
)
}
_getOffsetHeight() {
return this._scrollElement === window
? window.innerHeight : this._scrollElement.getBoundingClientRect().height
}
_process() {
const scrollTop = this._getScrollTop() + this._config.offset
const scrollHeight = this._getScrollHeight()
const maxScroll = this._config.offset +
scrollHeight -
this._getOffsetHeight()
if (this._scrollHeight !== scrollHeight) {
this.refresh()
}
if (scrollTop >= maxScroll) {
const target = this._targets[this._targets.length - 1]
if (this._activeTarget !== target) {
this._activate(target)
}
return
}
if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
this._activeTarget = null
this._clear()
return
}
for (let i = this._offsets.length; i--;) {
const isActiveTarget = this._activeTarget !== this._targets[i] &&
scrollTop >= this._offsets[i] &&
(typeof this._offsets[i + 1] === 'undefined' ||
scrollTop < this._offsets[i + 1])
if (isActiveTarget) {
this._activate(this._targets[i])
}
}
}
_activate(target) {
this._activeTarget = target
this._clear()
let queries = this._selector.split(',')
// eslint-disable-next-line arrow-body-style
queries = queries.map((selector) => {
return `${selector}[data-target="${target}"],` +
`${selector}[href="${target}"]`
})
const $link = $(queries.join(','))
if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
$link.addClass(ClassName.ACTIVE)
} else {
// Set triggered link as active
$link.addClass(ClassName.ACTIVE)
// Set triggered links parents as active
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
$link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
// Handle special case when .nav-link is inside .nav-item
$link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE)
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
relatedTarget: target
})
}
_clear() {
$(this._selector).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data) {
data = new ScrollSpy(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(window).on(Event.LOAD_DATA_API, () => {
const scrollSpys = $.makeArray($(Selector.DATA_SPY))
for (let i = scrollSpys.length; i--;) {
const $spy = $(scrollSpys[i])
ScrollSpy._jQueryInterface.call($spy, $spy.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface
}
return ScrollSpy
})($)
export default ScrollSpy

View File

@ -0,0 +1,263 @@
import $ from 'jquery'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Tab = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tab'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 150
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active',
DISABLED : 'disabled',
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
DROPDOWN : '.dropdown',
NAV_LIST_GROUP : '.nav, .list-group',
ACTIVE : '.active',
ACTIVE_UL : '> li > .active',
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tab {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
show() {
if (this._element.parentNode &&
this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
$(this._element).hasClass(ClassName.ACTIVE) ||
$(this._element).hasClass(ClassName.DISABLED)) {
return
}
let target
let previous
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
const selector = Util.getSelectorFromElement(this._element)
if (listElement) {
const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
previous = $.makeArray($(listElement).find(itemSelector))
previous = previous[previous.length - 1]
}
const hideEvent = $.Event(Event.HIDE, {
relatedTarget: this._element
})
const showEvent = $.Event(Event.SHOW, {
relatedTarget: previous
})
if (previous) {
$(previous).trigger(hideEvent)
}
$(this._element).trigger(showEvent)
if (showEvent.isDefaultPrevented() ||
hideEvent.isDefaultPrevented()) {
return
}
if (selector) {
target = $(selector)[0]
}
this._activate(
this._element,
listElement
)
const complete = () => {
const hiddenEvent = $.Event(Event.HIDDEN, {
relatedTarget: this._element
})
const shownEvent = $.Event(Event.SHOWN, {
relatedTarget: previous
})
$(previous).trigger(hiddenEvent)
$(this._element).trigger(shownEvent)
}
if (target) {
this._activate(target, target.parentNode, complete)
} else {
complete()
}
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_activate(element, container, callback) {
let activeElements
if (container.nodeName === 'UL') {
activeElements = $(container).find(Selector.ACTIVE_UL)
} else {
activeElements = $(container).children(Selector.ACTIVE)
}
const active = activeElements[0]
const isTransitioning = callback &&
Util.supportsTransitionEnd() &&
(active && $(active).hasClass(ClassName.FADE))
const complete = () => this._transitionComplete(
element,
active,
callback
)
if (active && isTransitioning) {
$(active)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(TRANSITION_DURATION)
} else {
complete()
}
}
_transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
const dropdownChild = $(active.parentNode).find(
Selector.DROPDOWN_ACTIVE_CHILD
)[0]
if (dropdownChild) {
$(dropdownChild).removeClass(ClassName.ACTIVE)
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
}
}
$(element).addClass(ClassName.ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
}
Util.reflow(element)
$(element).addClass(ClassName.SHOW)
if (element.parentNode &&
$(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
if (dropdownElement) {
$(dropdownElement).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
}
element.setAttribute('aria-expanded', true)
}
if (callback) {
callback()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
if (!data) {
data = new Tab(this)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
Tab._jQueryInterface.call($(this), 'show')
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface
}
return Tab
})($)
export default Tab

View File

@ -0,0 +1,721 @@
import $ from 'jquery'
import Popper from 'popper.js'
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Tooltip = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tooltip'
const VERSION = '4.0.0'
const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 150
const CLASS_PREFIX = 'bs-tooltip'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const DefaultType = {
animation : 'boolean',
template : 'string',
title : '(string|element|function)',
trigger : 'string',
delay : '(number|object)',
html : 'boolean',
selector : '(string|boolean)',
placement : '(string|function)',
offset : '(number|string)',
container : '(string|element|boolean)',
fallbackPlacement : '(string|array)',
boundary : '(string|element)'
}
const AttachmentMap = {
AUTO : 'auto',
TOP : 'top',
RIGHT : 'right',
BOTTOM : 'bottom',
LEFT : 'left'
}
const Default = {
animation : true,
template : '<div class="tooltip" role="tooltip">' +
'<div class="arrow"></div>' +
'<div class="tooltip-inner"></div></div>',
trigger : 'hover focus',
title : '',
delay : 0,
html : false,
selector : false,
placement : 'top',
offset : 0,
container : false,
fallbackPlacement : 'flip',
boundary : 'scrollParent'
}
const HoverState = {
SHOW : 'show',
OUT : 'out'
}
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`
}
const ClassName = {
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
TOOLTIP : '.tooltip',
TOOLTIP_INNER : '.tooltip-inner',
ARROW : '.arrow'
}
const Trigger = {
HOVER : 'hover',
FOCUS : 'focus',
CLICK : 'click',
MANUAL : 'manual'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tooltip {
constructor(element, config) {
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)')
}
// private
this._isEnabled = true
this._timeout = 0
this._hoverState = ''
this._activeTrigger = {}
this._popper = null
// Protected
this.element = element
this.config = this._getConfig(config)
this.tip = null
this._setListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get NAME() {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
// Public
enable() {
this._isEnabled = true
}
disable() {
this._isEnabled = false
}
toggleEnabled() {
this._isEnabled = !this._isEnabled
}
toggle(event) {
if (!this._isEnabled) {
return
}
if (event) {
const dataKey = this.constructor.DATA_KEY
let context = $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
context._activeTrigger.click = !context._activeTrigger.click
if (context._isWithActiveTrigger()) {
context._enter(null, context)
} else {
context._leave(null, context)
}
} else {
if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {
this._leave(null, this)
return
}
this._enter(null, this)
}
}
dispose() {
clearTimeout(this._timeout)
$.removeData(this.element, this.constructor.DATA_KEY)
$(this.element).off(this.constructor.EVENT_KEY)
$(this.element).closest('.modal').off('hide.bs.modal')
if (this.tip) {
$(this.tip).remove()
}
this._isEnabled = null
this._timeout = null
this._hoverState = null
this._activeTrigger = null
if (this._popper !== null) {
this._popper.destroy()
}
this._popper = null
this.element = null
this.config = null
this.tip = null
}
show() {
if ($(this.element).css('display') === 'none') {
throw new Error('Please use show on visible elements')
}
const showEvent = $.Event(this.constructor.Event.SHOW)
if (this.isWithContent() && this._isEnabled) {
$(this.element).trigger(showEvent)
const isInTheDom = $.contains(
this.element.ownerDocument.documentElement,
this.element
)
if (showEvent.isDefaultPrevented() || !isInTheDom) {
return
}
const tip = this.getTipElement()
const tipId = Util.getUID(this.constructor.NAME)
tip.setAttribute('id', tipId)
this.element.setAttribute('aria-describedby', tipId)
this.setContent()
if (this.config.animation) {
$(tip).addClass(ClassName.FADE)
}
const placement = typeof this.config.placement === 'function'
? this.config.placement.call(this, tip, this.element)
: this.config.placement
const attachment = this._getAttachment(placement)
this.addAttachmentClass(attachment)
const container = this.config.container === false ? document.body : $(this.config.container)
$(tip).data(this.constructor.DATA_KEY, this)
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
$(tip).appendTo(container)
}
$(this.element).trigger(this.constructor.Event.INSERTED)
this._popper = new Popper(this.element, tip, {
placement: attachment,
modifiers: {
offset: {
offset: this.config.offset
},
flip: {
behavior: this.config.fallbackPlacement
},
arrow: {
element: Selector.ARROW
},
preventOverflow: {
boundariesElement: this.config.boundary
}
},
onCreate: (data) => {
if (data.originalPlacement !== data.placement) {
this._handlePopperPlacementChange(data)
}
},
onUpdate: (data) => {
this._handlePopperPlacementChange(data)
}
})
$(tip).addClass(ClassName.SHOW)
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
$('body').children().on('mouseover', null, $.noop)
}
const complete = () => {
if (this.config.animation) {
this._fixTransition()
}
const prevHoverState = this._hoverState
this._hoverState = null
$(this.element).trigger(this.constructor.Event.SHOWN)
if (prevHoverState === HoverState.OUT) {
this._leave(null, this)
}
}
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
$(this.tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
} else {
complete()
}
}
}
hide(callback) {
const tip = this.getTipElement()
const hideEvent = $.Event(this.constructor.Event.HIDE)
const complete = () => {
if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip)
}
this._cleanTipClass()
this.element.removeAttribute('aria-describedby')
$(this.element).trigger(this.constructor.Event.HIDDEN)
if (this._popper !== null) {
this._popper.destroy()
}
if (callback) {
callback()
}
}
$(this.element).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
return
}
$(tip).removeClass(ClassName.SHOW)
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$('body').children().off('mouseover', null, $.noop)
}
this._activeTrigger[Trigger.CLICK] = false
this._activeTrigger[Trigger.FOCUS] = false
this._activeTrigger[Trigger.HOVER] = false
if (Util.supportsTransitionEnd() &&
$(this.tip).hasClass(ClassName.FADE)) {
$(tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(TRANSITION_DURATION)
} else {
complete()
}
this._hoverState = ''
}
update() {
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Protected
isWithContent() {
return Boolean(this.getTitle())
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const $tip = $(this.getTipElement())
this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle())
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
}
setElementContent($element, content) {
const html = this.config.html
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
// Content is a DOM node or a jQuery
if (html) {
if (!$(content).parent().is($element)) {
$element.empty().append(content)
}
} else {
$element.text($(content).text())
}
} else {
$element[html ? 'html' : 'text'](content)
}
}
getTitle() {
let title = this.element.getAttribute('data-original-title')
if (!title) {
title = typeof this.config.title === 'function'
? this.config.title.call(this.element)
: this.config.title
}
return title
}
// Private
_getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()]
}
_setListeners() {
const triggers = this.config.trigger.split(' ')
triggers.forEach((trigger) => {
if (trigger === 'click') {
$(this.element).on(
this.constructor.Event.CLICK,
this.config.selector,
(event) => this.toggle(event)
)
} else if (trigger !== Trigger.MANUAL) {
const eventIn = trigger === Trigger.HOVER
? this.constructor.Event.MOUSEENTER
: this.constructor.Event.FOCUSIN
const eventOut = trigger === Trigger.HOVER
? this.constructor.Event.MOUSELEAVE
: this.constructor.Event.FOCUSOUT
$(this.element)
.on(
eventIn,
this.config.selector,
(event) => this._enter(event)
)
.on(
eventOut,
this.config.selector,
(event) => this._leave(event)
)
}
$(this.element).closest('.modal').on(
'hide.bs.modal',
() => this.hide()
)
})
if (this.config.selector) {
this.config = {
...this.config,
trigger: 'manual',
selector: ''
}
} else {
this._fixTitle()
}
}
_fixTitle() {
const titleType = typeof this.element.getAttribute('data-original-title')
if (this.element.getAttribute('title') ||
titleType !== 'string') {
this.element.setAttribute(
'data-original-title',
this.element.getAttribute('title') || ''
)
this.element.setAttribute('title', '')
}
}
_enter(event, context) {
const dataKey = this.constructor.DATA_KEY
context = context || $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
if (event) {
context._activeTrigger[
event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER
] = true
}
if ($(context.getTipElement()).hasClass(ClassName.SHOW) ||
context._hoverState === HoverState.SHOW) {
context._hoverState = HoverState.SHOW
return
}
clearTimeout(context._timeout)
context._hoverState = HoverState.SHOW
if (!context.config.delay || !context.config.delay.show) {
context.show()
return
}
context._timeout = setTimeout(() => {
if (context._hoverState === HoverState.SHOW) {
context.show()
}
}, context.config.delay.show)
}
_leave(event, context) {
const dataKey = this.constructor.DATA_KEY
context = context || $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
if (event) {
context._activeTrigger[
event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER
] = false
}
if (context._isWithActiveTrigger()) {
return
}
clearTimeout(context._timeout)
context._hoverState = HoverState.OUT
if (!context.config.delay || !context.config.delay.hide) {
context.hide()
return
}
context._timeout = setTimeout(() => {
if (context._hoverState === HoverState.OUT) {
context.hide()
}
}, context.config.delay.hide)
}
_isWithActiveTrigger() {
for (const trigger in this._activeTrigger) {
if (this._activeTrigger[trigger]) {
return true
}
}
return false
}
_getConfig(config) {
config = {
...this.constructor.Default,
...$(this.element).data(),
...config
}
if (typeof config.delay === 'number') {
config.delay = {
show: config.delay,
hide: config.delay
}
}
if (typeof config.title === 'number') {
config.title = config.title.toString()
}
if (typeof config.content === 'number') {
config.content = config.content.toString()
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_getDelegateConfig() {
const config = {}
if (this.config) {
for (const key in this.config) {
if (this.constructor.Default[key] !== this.config[key]) {
config[key] = this.config[key]
}
}
}
return config
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''))
}
}
_handlePopperPlacementChange(data) {
this._cleanTipClass()
this.addAttachmentClass(this._getAttachment(data.placement))
}
_fixTransition() {
const tip = this.getTipElement()
const initConfigAnimation = this.config.animation
if (tip.getAttribute('x-placement') !== null) {
return
}
$(tip).removeClass(ClassName.FADE)
this.config.animation = false
this.hide()
this.show()
this.config.animation = initConfigAnimation
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data && /dispose|hide/.test(config)) {
return
}
if (!data) {
data = new Tooltip(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tooltip._jQueryInterface
$.fn[NAME].Constructor = Tooltip
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tooltip._jQueryInterface
}
return Tooltip
})($, Popper)
export default Tooltip

View File

@ -0,0 +1,161 @@
import $ from 'jquery'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): util.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Util = (($) => {
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
let transition = false
const MAX_UID = 1000000
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
function toType(obj) {
return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function getSpecialTransitionEndEvent() {
return {
bindType: transition.end,
delegateType: transition.end,
handle(event) {
if ($(event.target).is(this)) {
return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params
}
return undefined // eslint-disable-line no-undefined
}
}
}
function transitionEndTest() {
if (typeof window !== 'undefined' && window.QUnit) {
return false
}
return {
end: 'transitionend'
}
}
function transitionEndEmulator(duration) {
let called = false
$(this).one(Util.TRANSITION_END, () => {
called = true
})
setTimeout(() => {
if (!called) {
Util.triggerTransitionEnd(this)
}
}, duration)
return this
}
function setTransitionEndSupport() {
transition = transitionEndTest()
$.fn.emulateTransitionEnd = transitionEndEmulator
if (Util.supportsTransitionEnd()) {
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()
}
}
function escapeId(selector) {
// We escape IDs in case of special selectors (selector = '#myId:something')
// $.escapeSelector does not exist in jQuery < 3
selector = typeof $.escapeSelector === 'function' ? $.escapeSelector(selector).substr(1)
: selector.replace(/(:|\.|\[|\]|,|=|@)/g, '\\$1')
return selector
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
const Util = {
TRANSITION_END: 'bsTransitionEnd',
getUID(prefix) {
do {
// eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix))
return prefix
},
getSelectorFromElement(element) {
let selector = element.getAttribute('data-target')
if (!selector || selector === '#') {
selector = element.getAttribute('href') || ''
}
// If it's an ID
if (selector.charAt(0) === '#') {
selector = escapeId(selector)
}
try {
const $selector = $(document).find(selector)
return $selector.length > 0 ? selector : null
} catch (err) {
return null
}
},
reflow(element) {
return element.offsetHeight
},
triggerTransitionEnd(element) {
$(element).trigger(transition.end)
},
supportsTransitionEnd() {
return Boolean(transition)
},
isElement(obj) {
return (obj[0] || obj).nodeType
},
typeCheckConfig(componentName, config, configTypes) {
for (const property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
const expectedTypes = configTypes[property]
const value = config[property]
const valueType = value && Util.isElement(value)
? 'element' : toType(value)
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error(
`${componentName.toUpperCase()}: ` +
`Option "${property}" provided type "${valueType}" ` +
`but expected type "${expectedTypes}".`)
}
}
}
}
}
setTransitionEndSupport()
return Util
})($)
export default Util

View File

@ -0,0 +1,11 @@
[Theme]
engine = mako
parent = bootstrap3
author = The Nikola Contributors
author_url = https://getnikola.com/
license = MIT
based_on = Bootstrap 4 <http://v4-alpha.getbootstrap.com/>
tags = bootstrap
[Family]
family = bootstrap4

View File

@ -0,0 +1,4 @@
assets/css/all-nocdn.css=bootstrap.css,rst.css,code.css,baguetteBox.min.css,theme.css,custom.css
assets/css/all.css=rst.css,code.css,baguetteBox.min.css,theme.css,custom.css
assets/js/all-nocdn.js=jquery.min.js,popper.min.js,bootstrap.min.js,baguetteBox.min.js,moment-with-locales.min.js,fancydates.js
assets/js/all.js=baguetteBox.min.js,moment-with-locales.min.js,fancydates.js

1
themes/bootstrap4/engine Normal file
View File

@ -0,0 +1 @@
mako

1
themes/bootstrap4/parent Normal file
View File

@ -0,0 +1 @@
bootstrap3

View File

@ -0,0 +1,108 @@
## -*- coding: utf-8 -*-
<%namespace name="base" file="base_helper.tmpl" import="*" />
<%namespace name="notes" file="annotation_helper.tmpl" import="*" />
${set_locale(lang)}
${base.html_headstart()}
<%block name="extra_head">
### Leave this block alone.
</%block>
${template_hooks['extra_head']()}
</head>
<body>
<a href="#content" class="sr-only sr-only-focusable">${messages("Skip to main content")}</a>
<!-- Menubar -->
<nav class="navbar navbar-expand-md navbar-dark bg-dark static-top mb-4">
<div class="container"><!-- This keeps the margins nice -->
<a class="navbar-brand" href="${abs_link(_link("root", None, lang))}">
%if logo_url:
<img src="${logo_url}" alt="${blog_title|h}" id="logo" class="d-inline-block align-top">
%endif
% if show_blog_title:
<span id="blog-title">${blog_title|h}</span>
% endif
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#bs-navbar" aria-controls="bs-navbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="bs-navbar">
<ul class="navbar-nav mr-auto">
${base.html_navigation_links()}
${template_hooks['menu']()}
<li class="nav-item"><a class="nav-link" href="https://impactstory.org/u/0000-0003-4298-168X">ImpactStory <i class="fa fa-external-link" aria-hidden="true"></i></a></li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Data & Code <i class="fa fa-external-link" aria-hidden="true"></i></a>
<div class="dropdown-menu">
<a class="dropdown-item" href="https://osf.io/7mj2q/">Open Science Framework</a>
<a class="dropdown-item" href="https://gitlab.com/VickySteeves">GitLab</a>
<a class="dropdown-item" href="https://github.com/VickySteeves">GitHub</a>
</div>
</li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Social <i class="fa fa-external-link" aria-hidden="true"></i></a>
<div class="dropdown-menu">
<a class="dropdown-item" href="https://bookwitty.social/@VickySteeves">Mastodon: Bookwitty.Social</a>
<a class="dropdown-item" href="https://twitter.com/VickySteeves" >Twitter</a>
<a class="dropdown-item" href="https://www.instagram.com/vickysteeves/" >Instagram</a></li>
<a class="dropdown-item" href="https://www.linkedin.com/in/victoriaisteeves" >LinkedIn</a></li>
</div>
</li>
%if search_form:
${search_form}
%endif
<ul class="navbar-nav navbar-right">
<%block name="belowtitle">
%if len(translations) > 1:
<li>${base.html_translations()}</li>
%endif
</%block>
% if show_sourcelink:
<%block name="sourcelink"></%block>
%endif
${template_hooks['menu_alt']()}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container -->
</nav>
<!-- End of Menubar -->
<div class="container" id="content" role="main">
<div class="body-content">
<!--Body content-->
${template_hooks['page_header']()}
<%block name="content"></%block>
<!--End of body content-->
<footer id="footer">
${content_footer}
${template_hooks['page_footer']()}
</footer>
</div>
</div>
${base.late_load_js()}
<!-- fancy dates -->
<script>
moment.locale("${momentjs_locales[lang]}");
fancydates(${date_fanciness}, ${js_date_format});
</script>
<!-- end fancy dates -->
<%block name="extra_js"></%block>
<script>
baguetteBox.run('a.reference:not(.islink)', {
captions: function(element) {
return element.getElementsByTagName('img')[0].alt;
}});
baguetteBox.run('img:not(.islink)', {
captions: function(element) {
return element.alt;
}});
</script>
${body_end}
${template_hooks['body_end']()}
</body>
</html>

View File

@ -0,0 +1,194 @@
## -*- coding: utf-8 -*-
<%namespace name="notes" file="annotation_helper.tmpl" import="*" />
<%def name="html_headstart()">
<!DOCTYPE html>
<html
\
% if use_open_graph or (twitter_card and twitter_card['use_twitter_cards']) or (comment_system == 'facebook'):
prefix='\
%if use_open_graph or (twitter_card and twitter_card['use_twitter_cards']):
og: http://ogp.me/ns# \
%endif
%if use_open_graph:
article: http://ogp.me/ns/article# \
%endif
%if comment_system == 'facebook':
fb: http://ogp.me/ns/fb# \
%endif
'\
%endif
\
% if is_rtl:
dir="rtl" \
% endif
\
lang="${lang}">
<head>
<meta charset="utf-8">
% if use_base_tag:
<base href="${abs_link(permalink)}">
% endif
%if description:
<meta name="description" content="${description|h}">
%endif
<meta name="viewport" content="width=device-width, initial-scale=1">
%if title == blog_title:
<title>${blog_title|h}</title>
%else:
<title>${title|h} | ${blog_title|h}</title>
%endif
${html_stylesheets()}
<meta name="theme-color" content="${theme_color}">
% if meta_generator_tag:
<meta name="generator" content="Nikola (getnikola.com)">
% endif
${html_feedlinks()}
<link rel="canonical" href="${abs_link(permalink)}">
%if favicons:
%for name, file, size in favicons:
<link rel="${name}" href="${file}" sizes="${size}"/>
%endfor
%endif
% if comment_system == 'facebook':
<meta property="fb:app_id" content="${comment_system_id}">
% endif
%if prevlink:
<link rel="prev" href="${prevlink}" type="text/html">
%endif
%if nextlink:
<link rel="next" href="${nextlink}" type="text/html">
%endif
${mathjax_config}
%if use_cdn:
<!--[if lt IE 9]><script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
%else:
<!--[if lt IE 9]><script src="${url_replacer(permalink, '/assets/js/html5.js', lang, url_type)}"></script><![endif]-->
%endif
${extra_head_data}
</%def>
<%def name="late_load_js()">
%if use_bundles:
%if use_cdn:
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.8.2/baguetteBox.min.js"></script>
<script src="/assets/js/all.js"></script>
%else:
<script src="/assets/js/all-nocdn.js"></script>
%endif
%else:
%if use_cdn:
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.8.2/baguetteBox.min.js"></script>
%else:
<script src="/assets/js/jquery.min.js"></script>
<script src="/assets/js/popper.min.js"></script>
<script src="/assets/js/bootstrap.min.js"></script>
<script src="/assets/js/baguetteBox.min.js"></script>
<script src="/assets/js/moment-with-locales.min.js"></script>
<script src="/assets/js/fancydates.js"></script>
%endif
%endif
${social_buttons_code}
</%def>
<%def name="html_stylesheets()">
%if use_bundles:
%if use_cdn:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.8.2/baguetteBox.min.css" rel="stylesheet" type="text/css">
<link href="/assets/css/all.css" rel="stylesheet" type="text/css">
%else:
<link href="/assets/css/all-nocdn.css" rel="stylesheet" type="text/css">
%endif
%else:
%if use_cdn:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.8.2/baguetteBox.min.css" rel="stylesheet" type="text/css">
%else:
<link href="/assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="/assets/css/baguetteBox.min.css" rel="stylesheet" type="text/css">
%endif
<link href="/assets/css/rst.css" rel="stylesheet" type="text/css">
<link href="/assets/css/code.css" rel="stylesheet" type="text/css">
<link href="/assets/css/theme.css" rel="stylesheet" type="text/css">
%if has_custom_css:
<link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
%endif
%endif
% if needs_ipython_css:
<link href="/assets/css/ipython.min.css" rel="stylesheet" type="text/css">
<link href="/assets/css/nikola_ipython.css" rel="stylesheet" type="text/css">
% endif
% if annotations and post and not post.meta('noannotations'):
${notes.css()}
% elif not annotations and post and post.meta('annotations'):
${notes.css()}
% endif
</%def>
<%def name="html_navigation_links()">
%for url, text in navigation_links[lang]:
% if isinstance(url, tuple):
<li class="nav-item dropdown"><a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">${text}</a>
<div class="dropdown-menu">
%for suburl, text in url:
% if rel_link(permalink, suburl) == "#":
<a href="${permalink}" class="dropdown-item active">${text} <span class="sr-only">${messages("(active)", lang)}</span></a>
%else:
<a href="${suburl}" class="dropdown-item">${text}</a>
%endif
%endfor
</div>
% else:
% if rel_link(permalink, url) == "#":
<li class="nav-item active"><a href="${permalink}" class="nav-link">${text} <span class="sr-only">${messages("(active)", lang)}</span></a>
%else:
<li class="nav-item"><a href="${url}" class="nav-link">${text}</a>
%endif
% endif
%endfor
</%def>
<%def name="html_feedlinks()">
%if rss_link:
${rss_link}
%elif generate_rss:
%if len(translations) > 1:
%for language in sorted(translations):
<link rel="alternate" type="application/rss+xml" title="RSS (${language})" href="${_link('rss', None, language)}">
%endfor
%else:
<link rel="alternate" type="application/rss+xml" title="RSS" href="${_link('rss', None)}">
%endif
%endif
%if generate_atom:
%if len(translations) > 1:
%for language in sorted(translations):
<link rel="alternate" type="application/atom+xml" title="Atom (${language})" href="${_link('index_atom', None, language)}">
%endfor
%else:
<link rel="alternate" type="application/atom+xml" title="Atom" href="${_link('index_atom', None)}">
%endif
%endif
</%def>
<%def name="html_translations()">
%for langname in sorted(translations):
%if langname != lang:
<li><a href="${abs_link(_link("root", None, langname))}" rel="alternate" hreflang="${langname}">${messages("LANGUAGE", langname)}</a></li>
%endif
%endfor
</%def>

View File

@ -0,0 +1,19 @@
## -*- coding: utf-8 -*-
<%def name="bar(crumbs)">
%if crumbs:
<nav class="breadcrumbs">
<ul class="breadcrumb">
% for link, text in crumbs:
% if text != index_file:
% if link == '#':
<li class="breadcrumb-item active">${text.rsplit('.html', 1)[0]}</li>
% else:
<li class="breadcrumb-item"><a href="${link}">${text}</a></li>
% endif
% endif
% endfor
</ul>
</nav>
%endif
</%def>

View File

@ -0,0 +1,13 @@
## -*- coding: utf-8 -*-
<%def name="html_pager()">
%if prevlink or nextlink:
<ul class="pager postindexpager clearfix">
%if prevlink:
<li class="previous"><a href="${prevlink}" rel="prev">${messages("Newer posts")}</a></li>
%endif
%if nextlink:
<li class="next"><a href="${nextlink}" rel="next">${messages("Older posts")}</a></li>
%endif
</ul>
%endif
</%def>

View File

@ -0,0 +1,40 @@
## -*- coding: utf-8 -*-
<%def name="page_navigation(current_page, page_links, prevlink, nextlink, prev_next_links_reversed, surrounding=5)">
<nav aria-label="Page navigation">
<ul class="pagination">
% if prev_next_links_reversed:
% if nextlink:
<li class="page-item"><a href="${nextlink}" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">&laquo;</span></a></li>
% else:
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">&laquo;</span></a></li>
% endif
% else:
% if prevlink:
<li class="page-item"><a href="${prevlink}" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">&laquo;</span></a></li>
% else:
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">&laquo;</span></a></li>
% endif
% endif
% for i, link in enumerate(page_links):
% if abs(i - current_page) <= surrounding or i == 0 or i == len(page_links) - 1:
<li class="page-item ${'active' if i == current_page else ''}"><a href="${link}" class="page-link">${i + 1}${' <span class="sr-only">(current)</span>' if i == current_page else ''}</a></li>
% elif i == current_page - surrounding - 1 or i == current_page + surrounding + 1:
<li class="page-item disabled"><a href="#" class="page-link" aria-label="…"><span aria-hidden="true">…</span></a></li>
% endif
% endfor
% if prev_next_links_reversed:
% if prevlink:
<li class="page-item"><a href="${prevlink}" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">&raquo;</span></a></li>
% else:
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">&raquo;</span></a></li>
% endif
% else:
% if nextlink:
<li class="page-item"><a href="${nextlink}" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">&raquo;</span></a></li>
% else:
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">&raquo;</span></a></li>
% endif
% endif
</ul>
</nav>
</%def>

View File

@ -0,0 +1,60 @@
## -*- coding: utf-8 -*-
<%namespace name="helper" file="post_helper.tmpl"/>
<%namespace name="pheader" file="post_header.tmpl"/>
<%namespace name="comments" file="comments_helper.tmpl"/>
<%namespace name="math" file="math_helper.tmpl"/>
<%inherit file="base.tmpl"/>
<%block name="extra_head">
${parent.extra_head()}
% if post.meta('keywords'):
<meta name="keywords" content="${post.meta('keywords')|h}">
% endif
%if post.description():
<meta name="description" itemprop="description" content="${post.description()|h}">
%endif
<meta name="author" content="${post.author()|h}">
%if post.prev_post:
<link rel="prev" href="${post.prev_post.permalink()}" title="${post.prev_post.title()|h}" type="text/html">
%endif
%if post.next_post:
<link rel="next" href="${post.next_post.permalink()}" title="${post.next_post.title()|h}" type="text/html">
%endif
% if post.is_draft:
<meta name="robots" content="noindex">
% endif
${helper.open_graph_metadata(post)}
${helper.twitter_card_information(post)}
${helper.meta_translations(post)}
</%block>
<%block name="content">
<article class="post-${post.meta('type')} h-entry hentry postpage" itemscope="itemscope" itemtype="http://schema.org/Article">
${pheader.html_post_header()}
<div class="e-content entry-content" itemprop="articleBody text">
${post.text()}
</div>
<aside class="postpromonav">
<nav>
${helper.html_tags(post)}
${helper.html_pager(post)}
</nav>
</aside>
% if not post.meta('nocomments') and site_has_comments:
<section class="comments hidden-print">
<h2>${messages("Comments")}</h2>
${comments.comment_form(post.permalink(absolute=True), post.title(), post._base_path)}
</section>
% endif
${math.math_scripts_ifpost(post)}
</article>
${comments.comment_link_script()}
</%block>
<%block name="sourcelink">
% if show_sourcelink:
<li class="nav-item">
<a href="${post.source_link()}" id="sourcelink" class="nav-link">${messages("Source")}</a>
</li>
% endif
</%block>

View File

@ -0,0 +1,38 @@
## -*- coding: utf-8 -*-
<%inherit file="base.tmpl"/>
<%block name="content">
<h1>${title|h}</h1>
% if cat_items:
% if items:
<h2>${messages("Categories")}</h2>
% endif
% for text, full_name, path, link, indent_levels, indent_change_before, indent_change_after in cat_hierarchy:
% for i in range(indent_change_before):
<ul class="list-inline">
% endfor
<li class="list-inline-item"><a class="reference badge badge-secondary" href="${link}">${text|h}</a>
% if indent_change_after <= 0:
</li>
% endif
% for i in range(-indent_change_after):
</ul>
% if i + 1 < len(indent_levels):
</li>
% endif
% endfor
% endfor
% if items:
<h2>${messages("Tags")}</h2>
% endif
%endif
% if items:
<ul class="list-inline">
% for text, link in items:
% if text not in hidden_tags:
<li class="list-inline-item"><a class="reference badge badge-secondary" href="${link}">${text|h}</a></li>
% endif
% endfor
</ul>
% endif
</%block>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long