started upgrade to bootstrap4
This commit is contained in:
parent
8d5c3dabfb
commit
c8ab741026
4
conf.py
4
conf.py
|
@ -144,7 +144,7 @@ NAVIGATION_LINKS = {
|
|||
}
|
||||
|
||||
# Name of the theme to use.
|
||||
THEME = "custom"
|
||||
THEME = "bootstrap4"
|
||||
|
||||
# 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.
|
||||
|
@ -973,7 +973,7 @@ DEPLOY_DRAFTS = False
|
|||
# it's faster and the output looks better.
|
||||
# If you set USE_KATEX to True, you also need to add an extra CSS file
|
||||
# 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
|
||||
|
||||
# Do you want to customize the nbconversion of your IPython notebook?
|
||||
|
|
|
@ -10,67 +10,76 @@
|
|||
-->
|
||||
<html>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#int" data-toggle="tab" aria-expanded="false">Interests & Skills</a></li>
|
||||
<li><a href="#edu" data-toggle="tab" aria-expanded="true">Education</a></li>
|
||||
<li><a href="#emp" data-toggle="tab" aria-expanded="true">Employment</a></li>
|
||||
<li><a href="#pub" data-toggle="tab" aria-expanded="true">Publications</a></li>
|
||||
<li><a href="#pre" data-toggle="tab" aria-expanded="true">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 active show" data-toggle="tab" href="#int">Interests & Skills</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#edu">Education</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#emp">Employment</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#pub">Publications</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#pre">Presentations</a></li>
|
||||
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#mis">Creative Works</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- INTERESTS -->
|
||||
<div class="tab-pane fade active in" id="int">
|
||||
<h4>Interests</h4>
|
||||
<!- -----------------TAB CONTENT--------------------- -->
|
||||
<div id="myTabContent" class="tab-content">
|
||||
|
||||
<!- -----------------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>
|
||||
|
||||
<h4>Skills</h4>
|
||||
<ul>
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<!- -----------------END--------------------- -->
|
||||
|
||||
<!-- EDUCATION -->
|
||||
<!- -----------------BEGIN EDUCATION--------------------- -->
|
||||
<div class="tab-pane fade" id="edu">
|
||||
<ul style="margin-top: 1em;">
|
||||
<li><strong>Simmons College</strong>, Boston, MA, USA:</li>
|
||||
<ul>
|
||||
<li><em>Master of Library and Information Science</em>, August 2014
|
||||
<h3 style="margin-top: 1em;">Simmons College <small class="text-muted">Boston, MA, USA</small></h3>
|
||||
|
||||
<h4>Master of Library and Information Science <small class="text-muted">August 2014</small></h4>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<p><strong>Research Opportunities</strong></p>
|
||||
<ul>
|
||||
<li>Study Abroad:</li>
|
||||
<ul><li>Celtic Studies, University College Cork, Cork, Ireland, Summer 2012</li></ul>
|
||||
<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>Research Opportunities</li>
|
||||
<ul><li>"A Computational Linguistics Approach to Inflection and Difference in Meaning"</li>
|
||||
<ul><li>Research completed accompanying Dr. Nanette Veilleux, August 2012-August 2013.</li></ul></ul>
|
||||
<h4>Bachelor of Science in Computer Science and Information Technology <small class="text-muted">May 2013</small></h4>
|
||||
<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>
|
||||
<ul><li>Research completed as a part of the Simmons World Challenge, January 2011</li></ul></ul>
|
||||
<p><strong>Study Abroad:</strong></p>
|
||||
<ul><li><li>Celtic Studies, University College Cork, Cork, Ireland, Summer 2012</li></ul>
|
||||
|
||||
<ul><li>"The Harm (or lack thereof) of Marijuana"</li>
|
||||
<ul><li>Research completed as a part of an introductory research methods course.</li></ul></ul></ul>
|
||||
<p><strong>Research Opportunities:</strong></p>
|
||||
<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>
|
||||
<ul><li>Honours Program: Participant 2010-2013</li>
|
||||
<p><strong>Activities:</strong></p>
|
||||
<ul>
|
||||
<li>Honours Program: Participant 2010-2013</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 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>
|
||||
<ul><li>College of Arts and Sciences Dean's Fellow Award; 2013-2014</li>
|
||||
<p><strong>Honours and Achievements:</strong></p>
|
||||
<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>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>
|
||||
|
@ -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>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>United Pilgrimage for Youth, sponsored participant; Summer 2009</li></ul></ul></ul>
|
||||
<li>United Pilgrimage for Youth, sponsored participant; Summer 2009</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!- -----------------END EDUCATION--------------------- -->
|
||||
|
||||
<!-- EMPLOYMENT -->
|
||||
<!- -----------------BEGIN EMPLOYMENT--------------------- -->
|
||||
<div class="tab-pane fade" id="emp">
|
||||
<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>
|
||||
<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>
|
||||
|
@ -95,59 +105,89 @@
|
|||
<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>
|
||||
</ul>
|
||||
<li><strong>Metropolitan New York Library Council, Interim Program Coordinator, June 2015-July 2015; December 2015-May 2016</strong></li>
|
||||
<ul><li>Day-to-day coordinator of METRO’s National Digital Stewardship Residency in New York <a href="http://ndsr.nycdigital.org/">(NDSR-NY)</a> program</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 METRO’s 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 program’s web presence and online platforms.</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><strong>American Museum of Natural History, National Digital Stewardship Resident, September 2014-May 2015</strong></li>
|
||||
<ul><li>See my NDSR application video <a href="https://youtu.be/3oS4boUD9ms">here!</a></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>
|
||||
<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>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><strong>Sasaki Associates, Archives Intern; January 2014-April 2014</strong></li>
|
||||
<ul><li>Process historical architectural material and write the accompanying finding aid.</li>
|
||||
<li>Create records for each collection processed and catalogue them in Koha ILS.</li></ul>
|
||||
<li><strong>Simmons College, Dean's Fellow for Technology; September 2013-June 2014</strong></li>
|
||||
<ul><li>Manage social media technology for undergraduate science departments.</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><strong>Simmons College, Technical Resource Assistant; September 2013-June 2014</strong></li>
|
||||
<ul><li>Provide technical instruction to students, staff, and faculty at the Graduate School of Library and Information Science (GSLIS).</li>
|
||||
<li>Troubleshoot hard/software issues within GSLIS for students, staff, and faculty.</li></ul>
|
||||
<li><strong>IES Technical Sales, Contracted Web Developer; May 2013-September 2013</strong></li>
|
||||
<ul><li>Update the company website, developing and implementing design and operational upgrades.</li>
|
||||
<li>Assess and adapt to changing client needs through the development and deployment phase.</li></ul>
|
||||
<li><strong>Simmons College, Tutor in Computer Science; September 2012-2013</strong></li>
|
||||
<ul><li>Provide one-on-one tutoring for students in computer science classes.</li>
|
||||
<li>Lead group study sessions for upcoming evaluations, tests, and projects.</li></ul>
|
||||
<li><strong>Tutors For All, Campus Representative; January 2012-May 2012</strong></li>
|
||||
<ul><li>Enhance Tutors for All's social media pages through graphic design work and constant updating.</li>
|
||||
<li>Generate new leads among college campus populations through social media outreach.</li></ul>
|
||||
<li><strong>Simmons College, Teacher's Assistant in Computer Science; 2011-2013</strong></li>
|
||||
<ul><li>Create classwork, homework, and quizzes for students to complete.</li>
|
||||
<li>Grade students' work and report these grades to the instructor.</li></ul>
|
||||
<li><strong>Simmons College, Lab Monitor for Computer Science Laboratory; 2011-2013</strong></li>
|
||||
<ul><li>Assist students with troubleshooting soft/hardware issues.</li>
|
||||
<li>Repair and maintain computers in the computer science laboratory.</li></ul>
|
||||
<li><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>
|
||||
<li><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>
|
||||
<li><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>
|
||||
<li><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></li></ul>
|
||||
<li><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>
|
||||
<li>Recommend strategies to digitally preserve the scientific research at the AMNH.</li>
|
||||
</ul>
|
||||
<p><strong>Sasaki Associates, Archives Intern; January 2014-April 2014</strong></p>
|
||||
<ul>
|
||||
<li>Process historical architectural material and write the accompanying finding aid.</li>
|
||||
<li>Create records for each collection processed and catalogue them in Koha ILS.</li>
|
||||
</ul>
|
||||
<p><strong>Simmons College, Dean's Fellow for Technology; September 2013-June 2014</strong></p>
|
||||
<ul>
|
||||
<li>Manage social media technology for undergraduate science departments.</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>
|
||||
<p><strong>Simmons College, Technical Resource Assistant; 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>
|
||||
<li>Troubleshoot hard/software issues within GSLIS for students, staff, and faculty.</li>
|
||||
</ul>
|
||||
<p><strong>IES Technical Sales, Contracted Web Developer; May 2013-September 2013</strong></li>
|
||||
<ul>
|
||||
<li>Update the company website, developing and implementing design and operational upgrades.</li>
|
||||
<li>Assess and adapt to changing client needs through the development and deployment phase.</li>
|
||||
</ul>
|
||||
<p><strong>Simmons College, Tutor in Computer Science; September 2012-2013</strong></li>
|
||||
<ul>
|
||||
<li>Provide one-on-one tutoring for students in computer science classes.</li>
|
||||
<li>Lead group study sessions for upcoming evaluations, tests, and projects.</li>
|
||||
</ul>
|
||||
<p><strong>Tutors For All, Campus Representative; January 2012-May 2012</strong></li>
|
||||
<ul>
|
||||
<li>Enhance Tutors for All's social media pages through graphic design work and constant updating.</li>
|
||||
<li>Generate new leads among college campus populations through social media outreach.</li>
|
||||
</ul>
|
||||
<p><strong>Simmons College, Teacher's Assistant in Computer Science; 2011-2013</strong></li>
|
||||
<ul>
|
||||
<li>Create classwork, homework, and quizzes for students to complete.</li>
|
||||
<li>Grade students' work and report these grades to the instructor.</li>
|
||||
</ul>
|
||||
<p><strong>Simmons College, Lab Monitor for Computer Science Laboratory; 2011-2013</strong></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>
|
||||
</div>
|
||||
<!- -----------------END EMPLOYMENT--------------------- -->
|
||||
|
||||
<!-- PUB -->
|
||||
<!- -----------------BEGIN PUBLICATIONS--------------------- -->
|
||||
<div class="tab-pane fade" id="pub">
|
||||
<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>
|
||||
|
@ -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>
|
||||
</ul>
|
||||
</div>
|
||||
<!- -----------------END PUBLICATIONS--------------------- -->
|
||||
|
||||
<!-- PRESENT -->
|
||||
<!- -----------------BEGIN PRESENTATIONS--------------------- -->
|
||||
<div class="tab-pane fade" id="pre">
|
||||
<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://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://osf.io/s6fw9/">ReproZip for Reproducible Research</a>, Steeves, V., & Rampin, R., PresQT Workshop One, May 2017. Notre Dame University.</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/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>
|
||||
<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://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.</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="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="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>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>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><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>"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>"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>"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>"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>"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>"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>
|
||||
</ul>
|
||||
</div>
|
||||
<!- -----------------END PRESENTATIONS--------------------- -->
|
||||
|
||||
<!-- PROFESSIONAL OUTPUT -->
|
||||
<!- -----------BEGIN OTHER OUTPUT----------------- -->
|
||||
<div class="tab-pane fade" id="mis">
|
||||
<h4>Professional Memberships</h4>
|
||||
<ul>
|
||||
<li>American Library Association; September 2014-present</li>
|
||||
<li>Association for Computing Machinery; September 2012-present</li>
|
||||
</ul>
|
||||
|
||||
<h4>Professional Output</h4>
|
||||
<ul>
|
||||
<ul style="margin-top: 1em;">
|
||||
<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>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>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>
|
||||
<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>:</li>
|
||||
<ul><li>Serve as an Open Science Framework Ambassador</li>
|
||||
<li>Participant in the 2016 SHARE Curation Associates program.</li></ul>
|
||||
<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>
|
||||
<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>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>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>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>
|
||||
<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>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>
|
||||
<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>
|
||||
</div>
|
||||
<!- -----------END OTHER OUTPUT----------------- -->
|
||||
|
||||
</div>
|
||||
<!- -----------END TAB CONTENT----------------- -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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
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
|
@ -0,0 +1,11 @@
|
|||
.bg-dark{
|
||||
background-color: #6695c4 !important;
|
||||
}
|
||||
|
||||
.navbar{
|
||||
font-size: 1.07em;
|
||||
}
|
||||
|
||||
.navbar-brand{
|
||||
font-size: 1.07em;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
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
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
mako
|
|
@ -0,0 +1 @@
|
|||
bootstrap3
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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">«</span></a></li>
|
||||
% else:
|
||||
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">«</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">«</span></a></li>
|
||||
% else:
|
||||
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">«</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">»</span></a></li>
|
||||
% else:
|
||||
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Newer posts")}"><span aria-hidden="true">»</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">»</span></a></li>
|
||||
% else:
|
||||
<li class="page-item disabled"><a href="#" class="page-link" aria-label="${messages("Older posts")}"><span aria-hidden="true">»</span></a></li>
|
||||
% endif
|
||||
% endif
|
||||
</ul>
|
||||
</nav>
|
||||
</%def>
|
|
@ -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>
|
|
@ -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
Loading…
Reference in New Issue