commit
fa34415146
405 changed files with 131547 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||
<?php |
|||
|
|||
$DATAhst="localhost"; |
|||
$DATAusr="ordine"; |
|||
$DATApwd="ordine"; |
|||
$DATAdtb="ordine"; |
|||
|
|||
|
|||
?> |
@ -0,0 +1,79 @@ |
|||
<?php |
|||
|
|||
function conv_data($od,$lang){ |
|||
$tmp=explode("-",substr($od,0,10)); |
|||
switch($lang){ |
|||
case "IT": |
|||
case "FR": |
|||
case "ES": |
|||
$stres = $tmp[2]."/".$tmp[1]."/".$tmp[0].""; |
|||
break; |
|||
case "DE": |
|||
$stres = $tmp[2].".".$tmp[1].".".$tmp[0].""; |
|||
break; |
|||
default: |
|||
$stres=$od; |
|||
} |
|||
return $stres; |
|||
} |
|||
|
|||
|
|||
|
|||
function convert($stringa){ |
|||
$strres = $stringa; |
|||
$strres = str_replace("'","'",$strres); |
|||
return stripslashes(nl2br($strres)); |
|||
} |
|||
|
|||
|
|||
function convert_mce($stringa){ |
|||
$strres = $stringa; |
|||
$strres = str_replace("'","'",$strres); |
|||
return stripslashes($strres); |
|||
} |
|||
|
|||
function conv_url($text){ |
|||
$entities = array('+','%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%2B', '%24', '%2C', '%2F', '%3F', '%25', '%23', '%5B', '%5D'); |
|||
$replacements = array('-','_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',); |
|||
return str_replace($entities, $replacements, urlencode($text)); |
|||
} |
|||
|
|||
|
|||
function save_photo($foto,$basepath,$id){ |
|||
|
|||
$filename = $foto; |
|||
// Get new sizes |
|||
list($width, $height) = getimagesize($filename); |
|||
$percent=800/$width; |
|||
$newwidth = $width * $percent; |
|||
$newheight = $height * $percent; |
|||
|
|||
// Load |
|||
$thumb = imagecreatetruecolor($newwidth, $newheight); |
|||
|
|||
switch(exif_imagetype($filename)){ |
|||
case 1: |
|||
$source = imagecreatefromgif($filename); |
|||
break; |
|||
|
|||
case 2: |
|||
$source = imagecreatefromjpeg($filename); |
|||
break; |
|||
|
|||
case 3: |
|||
$source = imagecreatefrompng($filename); |
|||
break; |
|||
|
|||
} |
|||
// Resize |
|||
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); |
|||
|
|||
// Output |
|||
imagejpeg($thumb, $basepath."$id.jpg"); |
|||
// echo $basepath."$id.jpg"; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
?> |
@ -0,0 +1,5 @@ |
|||
<?php |
|||
|
|||
$BASE_URL="http://".$_SERVER['HTTP_HOST'].""; |
|||
|
|||
?> |
File diff suppressed because it is too large
@ -0,0 +1,74 @@ |
|||
<?php |
|||
|
|||
session_start(); |
|||
|
|||
@include 'cgi-bin/conn.conn'; |
|||
@include 'cgi-bin/functions.inc'; |
|||
@include 'cgi-bin/params.inc'; |
|||
|
|||
$GLOBALS['getQ'][0]="home"; |
|||
$GLOBALS['conn']; |
|||
|
|||
$conn=@mysqli_connect($DATAhst,$DATAusr,$DATApwd,$DATAdtb)or die("CONNECTION ERROR"); |
|||
|
|||
if(@$_GET['q']){ |
|||
$GLOBALS['getQ']=explode("/",$_GET['q']); |
|||
} |
|||
|
|||
$description = " "; |
|||
/* |
|||
if($getQ[1]){ |
|||
$q=mysqli_query($conn,"SELECT * FROM `contents` WHERE id=".$getQ[1].""); |
|||
$r=mysqli_fetch_array($q); |
|||
} |
|||
|
|||
if(!$r['title']){$r['title']="New Media Arts";} |
|||
*/ |
|||
?> |
|||
|
|||
|
|||
|
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title><?php echo conv_url($r['title']);?> | Ordine dei Farmacisti della Provincia di Salerno</title> |
|||
|
|||
<meta name="description" content="<?php echo $description;?> | Ordine dei Farmacisti della Provincia di Salerno"> |
|||
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'> |
|||
|
|||
|
|||
<meta property="og:url" content="<?php echo $BASE_URL;?>" /> |
|||
<meta property="og:description" content="<?php echo $description;?> | Ordine dei Farmacisti della Provincia di Salerno" /> |
|||
<meta property="og:image" content="<?php echo $BASE_URL."/images/logo.png";?>"> |
|||
<meta property="og:title" content="<?php echo $description;?> | Ordine dei Farmacisti della Provincia di Salerno" /> |
|||
|
|||
|
|||
<base href="<?php echo $BASE_URL;?>/"> |
|||
|
|||
<link href="<?php echo $BASE_URL;?>/css/styles.css" rel="stylesheet" type="text/css"/> |
|||
<link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> |
|||
<link rel="shortcut icon" type="image/png" href="<?php echo $BASE_URL;?>/images/favicon.png"/> |
|||
|
|||
</head> |
|||
<body itemscope itemtype="http://schema.org/WebPage"> |
|||
|
|||
<?php |
|||
|
|||
@include 'header.php'; |
|||
@include $getQ[0].'.php'; |
|||
|
|||
?> |
|||
|
|||
|
|||
<script type="text/javascript" src="<?php echo $BASE_URL;?>/node_modules/jquery/dist/jquery.min.js"></script> |
|||
<script type="text/javascript" src="<?php echo $BASE_URL;?>/js/scripts.js"></script> |
|||
|
|||
|
|||
</body> |
|||
</html> |
|||
|
|||
|
|||
<?php |
|||
mysqli_close($conn); |
|||
?> |
@ -0,0 +1 @@ |
|||
|
@ -0,0 +1,5 @@ |
|||
Bootstrap uses [GitHub's Releases feature](https://github.com/blog/1547-release-your-software) for its changelogs. |
|||
|
|||
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. |
|||
|
|||
Release announcement posts on [the official Bootstrap blog](https://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release. |
@ -0,0 +1,355 @@ |
|||
/*! |
|||
* Bootstrap's Gruntfile |
|||
* https://getbootstrap.com
|
|||
* Copyright 2013-2017 The Bootstrap Authors |
|||
* Copyright 2013-2017 Twitter, Inc. |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
*/ |
|||
|
|||
module.exports = function (grunt) { |
|||
'use strict' |
|||
|
|||
// Force use of Unix newlines
|
|||
grunt.util.linefeed = '\n' |
|||
|
|||
RegExp.quote = function (string) { |
|||
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&') |
|||
} |
|||
|
|||
var path = require('path') |
|||
var isTravis = require('is-travis') |
|||
|
|||
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' }) |
|||
|
|||
Object.keys(configBridge.paths).forEach(function (key) { |
|||
configBridge.paths[key].forEach(function (val, i, arr) { |
|||
arr[i] = path.join('./docs', val) |
|||
}) |
|||
}) |
|||
|
|||
// Project configuration.
|
|||
grunt.initConfig({ |
|||
|
|||
// Metadata.
|
|||
pkg: grunt.file.readJSON('package.json'), |
|||
banner: '/*!\n' + |
|||
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' + |
|||
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' + |
|||
' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' + |
|||
' */\n', |
|||
jqueryCheck: 'if (typeof jQuery === \'undefined\') {\n' + |
|||
' throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\\\'s JavaScript.\')\n' + |
|||
'}\n', |
|||
jqueryVersionCheck: '+function ($) {\n' + |
|||
' var version = $.fn.jquery.split(\' \')[0].split(\'.\')\n' + |
|||
' if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 4)) {\n' + |
|||
' throw new Error(\'Bootstrap\\\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0\')\n' + |
|||
' }\n' + |
|||
'}(jQuery);\n\n', |
|||
|
|||
// Task configuration.
|
|||
clean: { |
|||
dist: 'dist', |
|||
docs: 'docs/dist' |
|||
}, |
|||
|
|||
// JS build configuration
|
|||
babel: { |
|||
dev: { |
|||
options: { |
|||
sourceMap: true |
|||
}, |
|||
files: { |
|||
'js/dist/util.js' : 'js/src/util.js', |
|||
'js/dist/alert.js' : 'js/src/alert.js', |
|||
'js/dist/button.js' : 'js/src/button.js', |
|||
'js/dist/carousel.js' : 'js/src/carousel.js', |
|||
'js/dist/collapse.js' : 'js/src/collapse.js', |
|||
'js/dist/dropdown.js' : 'js/src/dropdown.js', |
|||
'js/dist/modal.js' : 'js/src/modal.js', |
|||
'js/dist/scrollspy.js' : 'js/src/scrollspy.js', |
|||
'js/dist/tab.js' : 'js/src/tab.js', |
|||
'js/dist/tooltip.js' : 'js/src/tooltip.js', |
|||
'js/dist/popover.js' : 'js/src/popover.js' |
|||
} |
|||
}, |
|||
dist: { |
|||
options: { |
|||
extends: '../../js/.babelrc' |
|||
}, |
|||
files: { |
|||
'<%= concat.bootstrap.dest %>' : '<%= concat.bootstrap.dest %>' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
stamp: { |
|||
options: { |
|||
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>\n+function () {\n', |
|||
footer: '\n}();' |
|||
}, |
|||
bootstrap: { |
|||
files: { |
|||
src: '<%= concat.bootstrap.dest %>' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
concat: { |
|||
options: { |
|||
// Custom function to remove all export and import statements
|
|||
process: function (src) { |
|||
return src.replace(/^(export|import).*/gm, '') |
|||
} |
|||
}, |
|||
bootstrap: { |
|||
src: [ |
|||
'js/src/util.js', |
|||
'js/src/alert.js', |
|||
'js/src/button.js', |
|||
'js/src/carousel.js', |
|||
'js/src/collapse.js', |
|||
'js/src/dropdown.js', |
|||
'js/src/modal.js', |
|||
'js/src/scrollspy.js', |
|||
'js/src/tab.js', |
|||
'js/src/tooltip.js', |
|||
'js/src/popover.js' |
|||
], |
|||
dest: 'dist/js/<%= pkg.name %>.js' |
|||
} |
|||
}, |
|||
|
|||
qunit: { |
|||
options: { |
|||
inject: 'js/tests/unit/phantom.js' |
|||
}, |
|||
files: 'js/tests/index.html' |
|||
}, |
|||
|
|||
// CSS build configuration
|
|||
copy: { |
|||
docs: { |
|||
expand: true, |
|||
cwd: 'dist/', |
|||
src: [ |
|||
'**/*' |
|||
], |
|||
dest: 'docs/dist/' |
|||
} |
|||
}, |
|||
|
|||
connect: { |
|||
server: { |
|||
options: { |
|||
port: 3000, |
|||
base: '.' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
jekyll: { |
|||
options: { |
|||
bundleExec: true, |
|||
config: '_config.yml', |
|||
incremental: false |
|||
}, |
|||
docs: {}, |
|||
github: { |
|||
options: { |
|||
raw: 'github: true' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
htmllint: { |
|||
options: { |
|||
ignore: [ |
|||
'Attribute “autocomplete” is only allowed when the input type is “color”, “date”, “datetime”, “datetime-local”, “email”, “hidden”, “month”, “number”, “password”, “range”, “search”, “tel”, “text”, “time”, “url”, or “week”.', |
|||
'Attribute “autocomplete” not allowed on element “button” at this point.', |
|||
'Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).', |
|||
'Element “div” not allowed as child of element “progress” in this context. (Suppressing further errors from this subtree.)', |
|||
'Element “img” is missing required attribute “src”.', |
|||
'The “color” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “date” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “datetime-local” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “month” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “time” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.', |
|||
'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.' |
|||
] |
|||
}, |
|||
src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html'] |
|||
}, |
|||
|
|||
watch: { |
|||
src: { |
|||
files: '<%= concat.bootstrap.src %>', |
|||
tasks: ['babel:dev'] |
|||
}, |
|||
sass: { |
|||
files: 'scss/**/*.scss', |
|||
tasks: ['dist-css', 'docs'] |
|||
}, |
|||
docs: { |
|||
files: 'docs/assets/scss/**/*.scss', |
|||
tasks: ['dist-css', 'docs'] |
|||
} |
|||
}, |
|||
|
|||
'saucelabs-qunit': { |
|||
all: { |
|||
options: { |
|||
build: process.env.TRAVIS_JOB_ID, |
|||
concurrency: 10, |
|||
maxRetries: 3, |
|||
maxPollRetries: 4, |
|||
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'], |
|||
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml') |
|||
} |
|||
} |
|||
}, |
|||
|
|||
exec: { |
|||
'clean-css': { |
|||
command: 'npm run clean-css' |
|||
}, |
|||
'clean-css-docs': { |
|||
command: 'npm run clean-css-docs' |
|||
}, |
|||
postcss: { |
|||
command: 'npm run postcss' |
|||
}, |
|||
'postcss-docs': { |
|||
command: 'npm run postcss-docs' |
|||
}, |
|||
htmlhint: { |
|||
command: 'npm run htmlhint' |
|||
}, |
|||
sass: { |
|||
command: 'npm run sass' |
|||
}, |
|||
'sass-docs': { |
|||
command: 'npm run sass-docs' |
|||
}, |
|||
'scss-lint': { |
|||
command: 'npm run scss-lint' |
|||
}, |
|||
'scss-lint-docs': { |
|||
command: 'npm run scss-lint-docs' |
|||
}, |
|||
uglify: { |
|||
command: 'npm run uglify' |
|||
}, |
|||
'uglify-docs': { |
|||
command: 'npm run uglify-docs' |
|||
} |
|||
}, |
|||
|
|||
buildcontrol: { |
|||
options: { |
|||
dir: '_gh_pages', |
|||
commit: true, |
|||
push: true, |
|||
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' |
|||
}, |
|||
pages: { |
|||
options: { |
|||
remote: 'git@github.com:twbs/derpstrap.git', |
|||
branch: 'gh-pages' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
compress: { |
|||
main: { |
|||
options: { |
|||
archive: 'bootstrap-<%= pkg.version %>-dist.zip', |
|||
mode: 'zip', |
|||
level: 9, |
|||
pretty: true |
|||
}, |
|||
files: [ |
|||
{ |
|||
expand: true, |
|||
cwd: 'dist/', |
|||
src: ['**'], |
|||
dest: 'bootstrap-<%= pkg.version %>-dist' |
|||
} |
|||
] |
|||
} |
|||
} |
|||
|
|||
}) |
|||
|
|||
|
|||
// These plugins provide necessary tasks.
|
|||
require('load-grunt-tasks')(grunt) |
|||
require('time-grunt')(grunt) |
|||
|
|||
// Docs HTML validation task
|
|||
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint', 'exec:htmlhint']) |
|||
|
|||
var runSubset = function (subset) { |
|||
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset |
|||
} |
|||
var isUndefOrNonZero = function (val) { |
|||
return val === undefined || val !== '0' |
|||
} |
|||
|
|||
// Test task.
|
|||
var testSubtasks = [] |
|||
// Skip core tests if running a different subset of the test suite
|
|||
if (runSubset('core') && |
|||
// Skip core tests if this is a Savage build
|
|||
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') { |
|||
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'qunit', 'docs']) |
|||
} |
|||
// Skip HTML validation if running a different subset of the test suite
|
|||
if (runSubset('validate-html') && |
|||
isTravis && |
|||
// Skip HTML5 validator when [skip validator] is in the commit message
|
|||
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) { |
|||
testSubtasks.push('validate-html') |
|||
} |
|||
// Only run Sauce Labs tests if there's a Sauce access key
|
|||
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' && |
|||
// Skip Sauce if running a different subset of the test suite
|
|||
runSubset('sauce-js-unit')) { |
|||
testSubtasks = testSubtasks.concat(['dist', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs']) |
|||
// Skip Sauce on Travis when [skip sauce] is in the commit message
|
|||
if (isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) { |
|||
testSubtasks.push('connect') |
|||
testSubtasks.push('saucelabs-qunit') |
|||
} |
|||
} |
|||
grunt.registerTask('test', testSubtasks) |
|||
|
|||
// JS distribution task.
|
|||
grunt.registerTask('dist-js', ['babel:dev', 'concat', 'babel:dist', 'stamp', 'exec:uglify']) |
|||
|
|||
grunt.registerTask('test-scss', ['exec:scss-lint']) |
|||
|
|||
// CSS distribution task.
|
|||
grunt.registerTask('sass-compile', ['exec:sass', 'exec:sass-docs']) |
|||
|
|||
grunt.registerTask('dist-css', ['sass-compile', 'exec:postcss', 'exec:clean-css', 'exec:clean-css-docs']) |
|||
|
|||
// Full distribution task.
|
|||
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js']) |
|||
|
|||
// Default task.
|
|||
grunt.registerTask('default', ['clean:dist', 'test']) |
|||
|
|||
// Docs task.
|
|||
grunt.registerTask('docs-css', ['exec:clean-css-docs', 'exec:postcss-docs']) |
|||
grunt.registerTask('lint-docs-css', ['exec:scss-lint-docs']) |
|||
grunt.registerTask('docs-js', ['exec:uglify-docs']) |
|||
grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs']) |
|||
grunt.registerTask('docs-github', ['jekyll:github']) |
|||
|
|||
grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']) |
|||
|
|||
// Publish to GitHub
|
|||
grunt.registerTask('publish', ['buildcontrol:pages']) |
|||
} |
@ -0,0 +1,22 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2011-2017 Twitter, Inc. |
|||
Copyright (c) 2011-2017 The Bootstrap Authors |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
@ -0,0 +1,135 @@ |
|||
# [Bootstrap](https://getbootstrap.com) |
|||
|
|||
[](https://bootstrap-slack.herokuapp.com) |
|||
 |
|||
[](https://www.npmjs.com/package/bootstrap) |
|||
[](https://rubygems.org/gems/bootstrap) |
|||
[](https://travis-ci.org/twbs/bootstrap) |
|||
[](https://david-dm.org/twbs/bootstrap?type=dev) |
|||
[](https://atmospherejs.com/twbs/bootstrap) |
|||
[](https://packagist.org/packages/twbs/bootstrap) |
|||
[](https://www.nuget.org/packages/bootstrap/4.0.0-alpha5) |
|||
|
|||
[](https://saucelabs.com/u/bootstrap) |
|||
|
|||
Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community. |
|||
|
|||
To get started, check out <https://getbootstrap.com>! |
|||
|
|||
## Table of contents |
|||
|
|||
- [Quick start](#quick-start) |
|||
- [Bugs and feature requests](#bugs-and-feature-requests) |
|||
- [Documentation](#documentation) |
|||
- [Contributing](#contributing) |
|||
- [Community](#community) |
|||
- [Versioning](#versioning) |
|||
- [Creators](#creators) |
|||
- [Copyright and license](#copyright-and-license) |
|||
|
|||
## Quick start |
|||
|
|||
Several quick start options are available: |
|||
|
|||
- [Download the latest release.](https://github.com/twbs/bootstrap/archive/v4.0.0-alpha.6.zip) |
|||
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git` |
|||
- Install with [npm](https://www.npmjs.com): `npm install bootstrap@4.0.0-alpha.6` |
|||
- Install with [yarn](https://github.com/yarnpkg/yarn): `yarn add bootstrap@4.0.0-alpha.6` |
|||
- Install with [Composer](https://getcomposer.org): `composer require twbs/bootstrap:4.0.0-alpha.6` |
|||
- Install with [Bower](https://bower.io): `bower install bootstrap#v4.0.0-alpha.6` |
|||
- Install with [NuGet](https://www.nuget.org): CSS: `Install-Package bootstrap -Pre` Sass: `Install-Package bootstrap.sass -Pre` (`-Pre` is only required until Bootstrap v4 has a stable release). |
|||
|
|||
Read the [Getting started page](https://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more. |
|||
|
|||
### What's included |
|||
|
|||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: |
|||
|
|||
``` |
|||
bootstrap/ |
|||
├── css/ |
|||
│ ├── bootstrap.css |
|||
│ ├── bootstrap.css.map |
|||
│ ├── bootstrap.min.css |
|||
│ └── bootstrap.min.css.map |
|||
└── js/ |
|||
├── bootstrap.js |
|||
└── bootstrap.min.js |
|||
``` |
|||
|
|||
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developer.chrome.com/devtools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. |
|||
|
|||
|
|||
## Bugs and feature requests |
|||
|
|||
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new). |
|||
|
|||
|
|||
## Documentation |
|||
|
|||
Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](https://jekyllrb.com) and publicly hosted on GitHub Pages at <https://getbootstrap.com>. The docs may also be run locally. |
|||
|
|||
### Running documentation locally |
|||
|
|||
1. Run through the [tooling setup](https://github.com/twbs/bootstrap/blob/v4-dev/docs/getting-started/build-tools.md#tooling-setup) to install Jekyll (the site builder) and other Ruby dependencies with `bundle install`. |
|||
2. Run `grunt` (or a specific set of Grunt tasks) to rebuild distributed CSS and JavaScript files, as well as our docs assets. |
|||
3. From the root `/bootstrap` directory, run `bundle exec jekyll serve` in the command line. |
|||
4. Open <http://localhost:9001> in your browser, and voilà. |
|||
|
|||
Learn more about using Jekyll by reading its [documentation](https://jekyllrb.com/docs/home/). |
|||
|
|||
### Documentation for previous releases |
|||
|
|||
Documentation for v2.3.2 has been made available for the time being at <https://getbootstrap.com/2.3.2/> while folks transition to Bootstrap 3. |
|||
|
|||
[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. |
|||
|
|||
|
|||
|
|||
## Contributing |
|||
|
|||
Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. |
|||
|
|||
Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). |
|||
|
|||
Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>. |
|||
|
|||
|
|||
|
|||
## Community |
|||
|
|||
Get updates on Bootstrap's development and chat with the project maintainers and community members. |
|||
|
|||
- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap). |
|||
- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com). |
|||
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com). |
|||
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel. |
|||
- Implementation help may be found at Stack Overflow (tagged [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4)). |
|||
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability. |
|||
|
|||
|
|||
|
|||
## Versioning |
|||
|
|||
For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible. |
|||
|
|||
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](https://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release. |
|||
|
|||
|
|||
## Creators |
|||
|
|||
**Mark Otto** |
|||
|
|||
- <https://twitter.com/mdo> |
|||
- <https://github.com/mdo> |
|||
|
|||
**Jacob Thornton** |
|||
|
|||
- <https://twitter.com/fat> |
|||
- <https://github.com/fat> |
|||
|
|||
|
|||
|
|||
## Copyright and license |
|||
|
|||
Code and documentation copyright 2011-2017 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE). |
File diff suppressed because it is too large
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,459 @@ |
|||
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ |
|||
html { |
|||
font-family: sans-serif; |
|||
line-height: 1.15; |
|||
-ms-text-size-adjust: 100%; |
|||
-webkit-text-size-adjust: 100%; |
|||
} |
|||
|
|||
body { |
|||
margin: 0; |
|||
} |
|||
|
|||
article, |
|||
aside, |
|||
footer, |
|||
header, |
|||
nav, |
|||
section { |
|||
display: block; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 2em; |
|||
margin: 0.67em 0; |
|||
} |
|||
|
|||
figcaption, |
|||
figure, |
|||
main { |
|||
display: block; |
|||
} |
|||
|
|||
figure { |
|||
margin: 1em 40px; |
|||
} |
|||
|
|||
hr { |
|||
-webkit-box-sizing: content-box; |
|||
box-sizing: content-box; |
|||
height: 0; |
|||
overflow: visible; |
|||
} |
|||
|
|||
pre { |
|||
font-family: monospace, monospace; |
|||
font-size: 1em; |
|||
} |
|||
|
|||
a { |
|||
background-color: transparent; |
|||
-webkit-text-decoration-skip: objects; |
|||
} |
|||
|
|||
a:active, |
|||
a:hover { |
|||
outline-width: 0; |
|||
} |
|||
|
|||
abbr[title] { |
|||
border-bottom: none; |
|||
text-decoration: underline; |
|||
text-decoration: underline dotted; |
|||
} |
|||
|
|||
b, |
|||
strong { |
|||
font-weight: inherit; |
|||
} |
|||
|
|||
b, |
|||
strong { |
|||
font-weight: bolder; |
|||
} |
|||
|
|||
code, |
|||
kbd, |
|||
samp { |
|||
font-family: monospace, monospace; |
|||
font-size: 1em; |
|||
} |
|||
|
|||
dfn { |
|||
font-style: italic; |
|||
} |
|||
|
|||
mark { |
|||
background-color: #ff0; |
|||
color: #000; |
|||
} |
|||
|
|||
small { |
|||
font-size: 80%; |
|||
} |
|||
|
|||
sub, |
|||
sup { |
|||
font-size: 75%; |
|||
line-height: 0; |
|||
position: relative; |
|||
vertical-align: baseline; |
|||
} |
|||
|
|||
sub { |
|||
bottom: -0.25em; |
|||
} |
|||
|
|||
sup { |
|||
top: -0.5em; |
|||
} |
|||
|
|||
audio, |
|||
video { |
|||
display: inline-block; |
|||
} |
|||
|
|||
audio:not([controls]) { |
|||
display: none; |
|||
height: 0; |
|||
} |
|||
|
|||
img { |
|||
border-style: none; |
|||
} |
|||
|
|||
svg:not(:root) { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
button, |
|||
input, |
|||
optgroup, |
|||
select, |
|||
textarea { |
|||
font-family: sans-serif; |
|||
font-size: 100%; |
|||
line-height: 1.15; |
|||
margin: 0; |
|||
} |
|||
|
|||
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 { |
|||
border-style: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
button:-moz-focusring, |
|||
[type="button"]:-moz-focusring, |
|||
[type="reset"]:-moz-focusring, |
|||
[type="submit"]:-moz-focusring { |
|||
outline: 1px dotted ButtonText; |
|||
} |
|||
|
|||
fieldset { |
|||
border: 1px solid #c0c0c0; |
|||
margin: 0 2px; |
|||
padding: 0.35em 0.625em 0.75em; |
|||
} |
|||
|
|||
legend { |
|||
-webkit-box-sizing: border-box; |
|||
box-sizing: border-box; |
|||
color: inherit; |
|||
display: table; |
|||
max-width: 100%; |
|||
padding: 0; |
|||
white-space: normal; |
|||
} |
|||
|
|||
progress { |
|||
display: inline-block; |
|||
vertical-align: baseline; |
|||
} |
|||
|
|||
textarea { |
|||
overflow: auto; |
|||
} |
|||
|
|||
[type="checkbox"], |
|||
[type="radio"] { |
|||
-webkit-box-sizing: border-box; |
|||
box-sizing: border-box; |
|||
padding: 0; |
|||
} |
|||
|
|||
[type="number"]::-webkit-inner-spin-button, |
|||
[type="number"]::-webkit-outer-spin-button { |
|||
height: auto; |
|||
} |
|||
|
|||
[type="search"] { |
|||
-webkit-appearance: textfield; |
|||
outline-offset: -2px; |
|||
} |
|||
|
|||
[type="search"]::-webkit-search-cancel-button, |
|||
[type="search"]::-webkit-search-decoration { |
|||
-webkit-appearance: none; |
|||
} |
|||
|
|||
::-webkit-file-upload-button { |
|||
-webkit-appearance: button; |
|||
font: inherit; |
|||
} |
|||
|
|||
details, |
|||
menu { |
|||
display: block; |
|||
} |
|||
|
|||
summary { |
|||
display: list-item; |
|||
} |
|||
|
|||
canvas { |
|||
display: inline-block; |
|||
} |
|||
|
|||
template { |
|||
display: none; |
|||
} |
|||
|
|||
[hidden] { |
|||
display: none; |
|||
} |
|||
|
|||
html { |
|||
-webkit-box-sizing: border-box; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
*, |
|||
*::before, |
|||
*::after { |
|||
-webkit-box-sizing: inherit; |
|||
box-sizing: inherit; |
|||
} |
|||
|
|||
@-ms-viewport { |
|||
width: device-width; |
|||
} |
|||
|
|||
html { |
|||
-ms-overflow-style: scrollbar; |
|||
-webkit-tap-highlight-color: transparent; |
|||
} |
|||
|
|||
body { |
|||
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
|||
font-size: 1rem; |
|||
font-weight: normal; |
|||
line-height: 1.5; |
|||
color: #292b2c; |
|||
background-color: #fff; |
|||
} |
|||
|
|||
[tabindex="-1"]:focus { |
|||
outline: none !important; |
|||
} |
|||
|
|||
h1, h2, h3, h4, h5, h6 { |
|||
margin-top: 0; |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
p { |
|||
margin-top: 0; |
|||
margin-bottom: 1rem; |
|||
} |
|||
|
|||
abbr[title], |
|||
abbr[data-original-title] { |
|||
cursor: help; |
|||
} |
|||
|
|||
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: bold; |
|||
} |
|||
|
|||
dd { |
|||
margin-bottom: .5rem; |
|||
margin-left: 0; |
|||
} |
|||
|
|||
blockquote { |
|||
margin: 0 0 1rem; |
|||
} |
|||
|
|||
a { |
|||
color: #0275d8; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
a:focus, a:hover { |
|||
color: #014c8c; |
|||
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; |
|||
} |
|||
|
|||
pre { |
|||
margin-top: 0; |
|||
margin-bottom: 1rem; |
|||
overflow: auto; |
|||
} |
|||
|
|||
figure { |
|||
margin: 0 0 1rem; |
|||
} |
|||
|
|||
img { |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
[role="button"] { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
a, |
|||
area, |
|||
button, |
|||
[role="button"], |
|||
input, |
|||
label, |
|||
select, |
|||
summary, |
|||
textarea { |
|||
-ms-touch-action: manipulation; |
|||
touch-action: manipulation; |
|||
} |
|||
|
|||
table { |
|||
border-collapse: collapse; |
|||
background-color: transparent; |
|||
} |
|||
|
|||
caption { |
|||
padding-top: 0.75rem; |
|||
padding-bottom: 0.75rem; |
|||
color: #636c72; |
|||
text-align: left; |
|||
caption-side: bottom; |
|||
} |
|||
|
|||
th { |
|||
text-align: left; |
|||
} |
|||
|
|||
label { |
|||
display: inline-block; |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
button:focus { |
|||
outline: 1px dotted; |
|||
outline: 5px auto -webkit-focus-ring-color; |
|||
} |
|||
|
|||
input, |
|||
button, |
|||
select, |
|||
textarea { |
|||
line-height: inherit; |
|||
} |
|||
|
|||
input[type="radio"]:disabled, |
|||
input[type="checkbox"]:disabled { |
|||
cursor: not-allowed; |
|||
} |
|||
|
|||
input[type="date"], |
|||
input[type="time"], |
|||
input[type="datetime-local"], |
|||
input[type="month"] { |
|||
-webkit-appearance: listbox; |
|||
} |
|||
|
|||
textarea { |
|||
resize: vertical; |
|||
} |
|||
|
|||
fieldset { |
|||
min-width: 0; |
|||
padding: 0; |
|||
margin: 0; |
|||
border: 0; |
|||
} |
|||
|
|||
legend { |
|||
display: block; |
|||
width: 100%; |
|||
padding: 0; |
|||
margin-bottom: .5rem; |
|||
font-size: 1.5rem; |
|||
line-height: inherit; |
|||
} |
|||
|
|||
input[type="search"] { |
|||
-webkit-appearance: none; |
|||
} |
|||
|
|||
output { |
|||
display: inline-block; |
|||
} |
|||
|
|||
[hidden] { |
|||
display: none !important; |
|||
} |
|||
/*# sourceMappingURL=bootstrap-reboot.css.map */ |
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}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{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#292b2c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}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]{cursor:help}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}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;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}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#636c72;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}/*# sourceMappingURL=bootstrap-reboot.min.css.map */ |
@ -0,0 +1 @@ |
|||
{"version":3,"sources":["../../scss/_normalize.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"4EAYA,KACE,YAAA,WACA,YAAA,KACA,qBAAA,KACA,yBAAA,KAUF,KACE,OAAA,EAOF,QAAA,MAAA,OAAA,OAAA,IAAA,QAME,QAAA,MAQF,GACE,UAAA,IACA,OAAA,MAAA,EAWF,WAAA,OAAA,KAGE,QAAA,MAOF,OACE,OAAA,IAAA,KAQF,GACE,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QAQF,IACE,YAAA,UAAA,UACA,UAAA,IAWF,EACE,iBAAA,YACA,6BAAA,QAQF,SAAA,QAEE,cAAA,EAQF,YACE,cAAA,KACA,gBAAA,UACA,gBAAA,UAAA,OAOF,EAAA,OAEE,YAAA,QAOF,EAAA,OAEE,YAAA,OAQF,KAAA,IAAA,KAGE,YAAA,UAAA,UACA,UAAA,IAOF,IACE,WAAA,OAOF,KACE,iBAAA,KACA,MAAA,KAOF,MACE,UAAA,IAQF,IAAA,IAEE,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SAGF,IACE,OAAA,OAGF,IACE,IAAA,MAUF,MAAA,MAEE,QAAA,aAOF,sBACE,QAAA,KACA,OAAA,EAOF,IACE,aAAA,KAOF,eACE,SAAA,OAWF,OAAA,MAAA,SAAA,OAAA,SAKE,YAAA,WACA,UAAA,KACA,YAAA,KACA,OAAA,EAQF,OAAA,MAEE,SAAA,QAQF,OAAA,OAEE,eAAA,KASF,aAAA,cAAA,OAAA,mBAIE,mBAAA,OAOF,gCAAA,+BAAA,gCAAA,yBAIE,aAAA,KACA,QAAA,EAOF,6BAAA,4BAAA,6BAAA,sBAIE,QAAA,IAAA,OAAA,WAOF,SACE,OAAA,IAAA,MAAA,OACA,OAAA,EAAA,IACA,QAAA,MAAA,OAAA,MAUF,OACE,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OAQF,SACE,QAAA,aACA,eAAA,SAOF,SACE,SAAA,KCrKF,gBAAA,aD+KE,mBAAA,WAAA,WAAA,WACA,QAAA,EC1KF,yCAAA,yCDmLE,OAAA,KC9KF,cDuLE,mBAAA,UACA,eAAA,KCnLF,4CAAA,yCD4LE,mBAAA,KAQF,6BACE,mBAAA,OACA,KAAA,QAWF,QAAA,KAEE,QAAA,MAOF,QACE,QAAA,UAUF,OACE,QAAA,aAOF,SACE,QAAA,KCnNF,SD8NE,QAAA,KEtbF,KACE,mBAAA,WAAA,WAAA,WAGF,EAAA,QAAA,SAGE,mBAAA,QAAA,WAAA,QAoBA,cAAgB,MAAA,aAQlB,KAYE,mBAAA,UAGA,4BAAA,YAGF,KACE,YAAA,cAAA,UAAA,mBAAA,WAAA,OC2K4H,iBD3K5H,MAAA,WACA,UAAA,KACA,YAAA,IACA,YAAA,IAEA,MAAA,QAEA,iBAAA,KD2LF,sBClLE,QAAA,YAYF,GAAI,GAAI,GAAI,GAAI,GAAI,GAClB,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KAIF,0BAAA,YAGE,OAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAGF,GAAA,GAAA,GAGE,WAAA,EACA,cAAA,KAGF,MAAA,MAAA,MAAA,MAIE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAQF,EACE,MAAA,QACA,gBAAA,KEhJE,QAAA,QFmJA,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KEhKE,oCAAA,oCFmKA,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EASJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAGE,OAAA,EAAA,EAAA,KAQF,IAGE,eAAA,ODsIF,cCzHE,OAAA,QAcF,cAAA,EAAA,KAAA,OAAA,MAAA,MAAA,OAAA,QAAA,SASE,iBAAA,aAAA,aAAA,aAQF,MAEE,gBAAA,SAEA,iBAAA,YAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAEE,WAAA,KAQF,MAEE,QAAA,aACA,cAAA,MAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBAGF,OAAA,MAAA,OAAA,SAME,YAAA,QAGF,8BAAA,2BAMI,OAAA,YAKJ,iBAAA,iBAAA,2BAAA,kBASE,mBAAA,QAGF,SAEE,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAGF,OAEE,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QAGF,mBAKE,mBAAA,KAIF,OACE,QAAA,aDsEF,SC9DE,QAAA"} |
File diff suppressed because it is too large
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
File diff suppressed because one or more lines are too long
@ -0,0 +1,110 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
'use strict' |
|||
|
|||
/*! |
|||
* Script to update version number references in the project. |
|||
* Copyright 2017 The Bootstrap Authors |
|||
* Copyright 2017 Twitter, Inc. |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
*/ |
|||
|
|||
/* global Set */ |
|||
|
|||
var fs = require('fs') |
|||
var path = require('path') |
|||
var sh = require('shelljs') |
|||
sh.config.fatal = true |
|||
var sed = sh.sed |
|||
|
|||
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
|
|||
RegExp.quote = function (string) { |
|||
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&') |
|||
} |
|||
RegExp.quoteReplacement = function (string) { |
|||
return string.replace(/[$]/g, '$$') |
|||
} |
|||
|
|||
var DRY_RUN = false |
|||
|
|||
function walkAsync(directory, excludedDirectories, fileCallback, errback) { |
|||
if (excludedDirectories.has(path.parse(directory).base)) { |
|||
return |
|||
} |
|||
fs.readdir(directory, function (err, names) { |
|||
if (err) { |
|||
errback(err) |
|||
return |
|||
} |
|||
names.forEach(function (name) { |
|||
var filepath = path.join(directory, name) |
|||
fs.lstat(filepath, function (err, stats) { |
|||
if (err) { |
|||
process.nextTick(errback, err) |
|||
return |
|||
} |
|||
if (stats.isSymbolicLink()) { |
|||
return |
|||
} |
|||
else if (stats.isDirectory()) { |
|||
process.nextTick(walkAsync, filepath, excludedDirectories, fileCallback, errback) |
|||
} |
|||
else if (stats.isFile()) { |
|||
process.nextTick(fileCallback, filepath) |
|||
} |
|||
}) |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
function replaceRecursively(directory, excludedDirectories, allowedExtensions, original, replacement) { |
|||
original = new RegExp(RegExp.quote(original), 'g') |
|||
replacement = RegExp.quoteReplacement(replacement) |
|||
var updateFile = !DRY_RUN ? function (filepath) { |
|||
if (allowedExtensions.has(path.parse(filepath).ext)) { |
|||
sed('-i', original, replacement, filepath) |
|||
} |
|||
} : function (filepath) { |
|||
if (allowedExtensions.has(path.parse(filepath).ext)) { |
|||
console.log('FILE: ' + filepath) |
|||
} |
|||
else { |
|||
console.log('EXCLUDED:' + filepath) |
|||
} |
|||
} |
|||
walkAsync(directory, excludedDirectories, updateFile, function (err) { |
|||
console.error('ERROR while traversing directory!:') |
|||
console.error(err) |
|||
process.exit(1) |
|||
}) |
|||
} |
|||
|
|||
function main(args) { |
|||
if (args.length !== 2) { |
|||
console.error('USAGE: change-version old_version new_version') |
|||
console.error('Got arguments:', args) |
|||
process.exit(1) |
|||
} |
|||
var oldVersion = args[0] |
|||
var newVersion = args[1] |
|||
var EXCLUDED_DIRS = new Set([ |
|||
'.git', |
|||
'node_modules', |
|||
'vendor' |
|||
]) |
|||
var INCLUDED_EXTENSIONS = new Set([ |
|||
// This extension whitelist is how we avoid modifying binary files
|
|||
'', |
|||
'.css', |
|||
'.html', |
|||
'.js', |
|||
'.json', |
|||
'.md', |
|||
'.scss', |
|||
'.txt', |
|||
'.yml' |
|||
]) |
|||
replaceRecursively('.', EXCLUDED_DIRS, INCLUDED_EXTENSIONS, oldVersion, newVersion) |
|||
} |
|||
|
|||
main(process.argv.slice(2)) |
@ -0,0 +1,10 @@ |
|||
{ |
|||
"paths": { |
|||
"docsJs": [ |
|||
"assets/js/vendor/anchor.min.js", |
|||
"assets/js/vendor/clipboard.min.js", |
|||
"assets/js/vendor/holder.min.js", |
|||
"assets/js/src/application.js" |
|||
] |
|||
} |
|||
} |
Binary file not shown.
File diff suppressed because it is too large
@ -0,0 +1,42 @@ |
|||
module.exports = { |
|||
use: [ |
|||
'postcss-flexbugs-fixes', |
|||
'autoprefixer' |
|||
], |
|||
map: { |
|||
inline: false, |
|||
annotation: true, |
|||
sourcesContent: true |
|||
}, |
|||
autoprefixer: { |
|||
browsers: [ |
|||
//
|
|||
// Official browser support policy:
|
|||
// https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#supported-browsers
|
|||
//
|
|||
'Chrome >= 35', // Exact version number here is kinda arbitrary
|
|||
// Rather than using Autoprefixer's native "Firefox ESR" version specifier string,
|
|||
// we deliberately hardcode the number. This is to avoid unwittingly severely breaking the previous ESR in the event that:
|
|||
// (a) we happen to ship a new Bootstrap release soon after the release of a new ESR,
|
|||
// such that folks haven't yet had a reasonable amount of time to upgrade; and
|
|||
// (b) the new ESR has unprefixed CSS properties/values whose absence would severely break webpages
|
|||
// (e.g. `box-sizing`, as opposed to `background: linear-gradient(...)`).
|
|||
// Since they've been unprefixed, Autoprefixer will stop prefixing them,
|
|||
// thus causing them to not work in the previous ESR (where the prefixes were required).
|
|||
'Firefox >= 38', // Current Firefox Extended Support Release (ESR); https://www.mozilla.org/en-US/firefox/organizations/faq/
|
|||
// Note: Edge versions in Autoprefixer & Can I Use refer to the EdgeHTML rendering engine version,
|
|||
// NOT the Edge app version shown in Edge's "About" screen.
|
|||
// For example, at the time of writing, Edge 20 on an up-to-date system uses EdgeHTML 12.
|
|||
// See also https://github.com/Fyrd/caniuse/issues/1928
|
|||
'Edge >= 12', |
|||
'Explorer >= 10', |
|||
// Out of leniency, we prefix these 1 version further back than the official policy.
|
|||
'iOS >= 8', |
|||
'Safari >= 8', |
|||
// The following remain NOT officially supported, but we're lenient and include their prefixes to avoid severely breaking in them.
|
|||
'Android 2.3', |
|||
'Android >= 4', |
|||
'Opera >= 12' |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,84 @@ |
|||
[ |
|||
# Docs: https://wiki.saucelabs.com/display/DOCS/Platform+Configurator |
|||
|
|||
{ |
|||
browserName: "safari", |
|||
platform: "OS X 10.11" |
|||
}, |
|||
{ |
|||
browserName: "chrome", |
|||
platform: "OS X 10.11", |
|||
version: "latest" |
|||
}, |
|||
{ |
|||
browserName: "firefox", |
|||
platform: "OS X 10.11", |
|||
version: "latest" |
|||
}, |
|||
|
|||
# Mac Opera not currently supported by Sauce Labs |
|||
|
|||
{ |
|||
browserName: "MicrosoftEdge", |
|||
platform: "Windows 10", |
|||
version: "latest" |
|||
}, |
|||
{ |
|||
browserName: "internet explorer", |
|||
version: "11", |
|||
platform: "Windows 8.1" |
|||
}, |
|||
{ |
|||
browserName: "internet explorer", |
|||
version: "10", |
|||
platform: "Windows 8" |
|||
}, |
|||
{ |
|||
browserName: "internet explorer", |
|||
version: "9", |
|||
platform: "Windows 7" |
|||
}, |
|||
|
|||
{ |
|||
browserName: "chrome", |
|||
platform: "Windows 10", |
|||
version: "latest" |
|||
}, |
|||
{ |
|||
browserName: "firefox", |
|||
platform: "Windows 10", |
|||
version: "latest" |
|||
}, |
|||
|
|||
# Win Opera 15+ not currently supported by Sauce Labs |
|||
|
|||
{ |
|||
browserName: "Safari", |
|||
deviceName: "iPhone Simulator", |
|||
platformVersion: "9.3", |
|||
platformName: "iOS" |
|||
}, |
|||
|
|||
# iOS Chrome not currently supported by Sauce Labs |
|||
|
|||
# Linux (unofficial) |
|||
{ |
|||
browserName: "chrome", |
|||
platform: "Linux", |
|||
version: "latest" |
|||
}, |
|||
{ |
|||
browserName: "firefox", |
|||
platform: "Linux", |
|||
version: "latest" |
|||
}, |
|||
|
|||
# Android |
|||
{ |
|||
platform: "Linux", |
|||
browserName: "android", |
|||
deviceName: "Android Emulator", |
|||
version: "latest", |
|||
deviceType: "phone" |
|||
} |
|||
] |
@ -0,0 +1,28 @@ |
|||
#!/bin/bash |
|||
# Upload built docs to preview.twbsapps.com |
|||
|
|||
if [ "$TRAVIS_REPO_SLUG" != twbs-savage/bootstrap ]; then exit 0; fi |
|||
|
|||
# Add build metadata to version |
|||
sed -i "/^current_version:/ s/\$/+pr.${TRAVIS_COMMIT}/" _config.yml |
|||
# Fix URLs since the site's root is now a subdirectory |
|||
echo "baseurl: /c/${TRAVIS_COMMIT}" >> _config.yml |
|||
bundle exec jekyll build --destination "$TRAVIS_COMMIT" |
|||
|
|||
# Install gcloud & gsutil |
|||
GSUTIL_VERSION=$(gsutil version | cut -d ' ' -f 3) |
|||
if [ ! -d "${HOME}/google-cloud-sdk" ] || [ "${GSUTIL_VERSION}" != '4.19' ]; then |
|||
rm -rf "${HOME}/google-cloud-sdk" # Kill Travis' outdated non-updateable preinstalled version |
|||
echo 'Installing google-cloud-sdk...' |
|||
export CLOUDSDK_CORE_DISABLE_PROMPTS=1 |
|||
time (curl -S -s https://sdk.cloud.google.com | bash &>/dev/null) |
|||
echo 'Done.' |
|||
fi |
|||
source "${HOME}/google-cloud-sdk/path.bash.inc" |
|||
|
|||
openssl aes-256-cbc -K $encrypted_2b749c8e6327_key -iv $encrypted_2b749c8e6327_iv -in grunt/gcp-key.json.enc -out grunt/gcp-key.json -d |
|||
gcloud auth activate-service-account "$GCP_SERVICE_ACCOUNT" --key-file grunt/gcp-key.json &> /dev/null || (echo 'GCP login failed!'; exit 1) |
|||
|
|||
echo "Uploading to http://preview.twbsapps.com/c/${TRAVIS_COMMIT} ..." |
|||
time gsutil -q -m cp -z html,css,js,svg -r "./${TRAVIS_COMMIT}" gs://preview.twbsapps.com/c/ |
|||
echo 'Done.' |
@ -0,0 +1,183 @@ |
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): alert.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Alert = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'alert'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.alert'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 150; |
|||
|
|||
var Selector = { |
|||
DISMISS: '[data-dismiss="alert"]' |
|||
}; |
|||
|
|||
var Event = { |
|||
CLOSE: 'close' + EVENT_KEY, |
|||
CLOSED: 'closed' + EVENT_KEY, |
|||
CLICK_DATA_API: 'click' + EVENT_KEY + DATA_API_KEY |
|||
}; |
|||
|
|||
var ClassName = { |
|||
ALERT: 'alert', |
|||
FADE: 'fade', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Alert = function () { |
|||
function Alert(element) { |
|||
_classCallCheck(this, Alert); |
|||
|
|||
this._element = element; |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Alert.prototype.close = function close(element) { |
|||
element = element || this._element; |
|||
|
|||
var rootElement = this._getRootElement(element); |
|||
var customEvent = this._triggerCloseEvent(rootElement); |
|||
|
|||
if (customEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
this._removeElement(rootElement); |
|||
}; |
|||
|
|||
Alert.prototype.dispose = function dispose() { |
|||
$.removeData(this._element, DATA_KEY); |
|||
this._element = null; |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Alert.prototype._getRootElement = function _getRootElement(element) { |
|||
var selector = Util.getSelectorFromElement(element); |
|||
var parent = false; |
|||
|
|||
if (selector) { |
|||
parent = $(selector)[0]; |
|||
} |
|||
|
|||
if (!parent) { |
|||
parent = $(element).closest('.' + ClassName.ALERT)[0]; |
|||
} |
|||
|
|||
return parent; |
|||
}; |
|||
|
|||
Alert.prototype._triggerCloseEvent = function _triggerCloseEvent(element) { |
|||
var closeEvent = $.Event(Event.CLOSE); |
|||
|
|||
$(element).trigger(closeEvent); |
|||
return closeEvent; |
|||
}; |
|||
|
|||
Alert.prototype._removeElement = function _removeElement(element) { |
|||
var _this = this; |
|||
|
|||
$(element).removeClass(ClassName.SHOW); |
|||
|
|||
if (!Util.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) { |
|||
this._destroyElement(element); |
|||
return; |
|||
} |
|||
|
|||
$(element).one(Util.TRANSITION_END, function (event) { |
|||
return _this._destroyElement(element, event); |
|||
}).emulateTransitionEnd(TRANSITION_DURATION); |
|||
}; |
|||
|
|||
Alert.prototype._destroyElement = function _destroyElement(element) { |
|||
$(element).detach().trigger(Event.CLOSED).remove(); |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Alert._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var $element = $(this); |
|||
var data = $element.data(DATA_KEY); |
|||
|
|||
if (!data) { |
|||
data = new Alert(this); |
|||
$element.data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (config === 'close') { |
|||
data[config](this); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
Alert._handleDismiss = function _handleDismiss(alertInstance) { |
|||
return function (event) { |
|||
if (event) { |
|||
event.preventDefault(); |
|||
} |
|||
|
|||
alertInstance.close(this); |
|||
}; |
|||
}; |
|||
|
|||
_createClass(Alert, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}]); |
|||
|
|||
return Alert; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* 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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=alert.js.map
|
@ -0,0 +1,167 @@ |
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): button.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Button = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'button'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.button'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
|
|||
var ClassName = { |
|||
ACTIVE: 'active', |
|||
BUTTON: 'btn', |
|||
FOCUS: 'focus' |
|||
}; |
|||
|
|||
var Selector = { |
|||
DATA_TOGGLE_CARROT: '[data-toggle^="button"]', |
|||
DATA_TOGGLE: '[data-toggle="buttons"]', |
|||
INPUT: 'input', |
|||
ACTIVE: '.active', |
|||
BUTTON: '.btn' |
|||
}; |
|||
|
|||
var 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 |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Button = function () { |
|||
function Button(element) { |
|||
_classCallCheck(this, Button); |
|||
|
|||
this._element = element; |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Button.prototype.toggle = function toggle() { |
|||
var triggerChangeEvent = true; |
|||
var rootElement = $(this._element).closest(Selector.DATA_TOGGLE)[0]; |
|||
|
|||
if (rootElement) { |
|||
var input = $(this._element).find(Selector.INPUT)[0]; |
|||
|
|||
if (input) { |
|||
if (input.type === 'radio') { |
|||
if (input.checked && $(this._element).hasClass(ClassName.ACTIVE)) { |
|||
triggerChangeEvent = false; |
|||
} else { |
|||
var activeElement = $(rootElement).find(Selector.ACTIVE)[0]; |
|||
|
|||
if (activeElement) { |
|||
$(activeElement).removeClass(ClassName.ACTIVE); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (triggerChangeEvent) { |
|||
input.checked = !$(this._element).hasClass(ClassName.ACTIVE); |
|||
$(input).trigger('change'); |
|||
} |
|||
|
|||
input.focus(); |
|||
} |
|||
} |
|||
|
|||
this._element.setAttribute('aria-pressed', !$(this._element).hasClass(ClassName.ACTIVE)); |
|||
|
|||
if (triggerChangeEvent) { |
|||
$(this._element).toggleClass(ClassName.ACTIVE); |
|||
} |
|||
}; |
|||
|
|||
Button.prototype.dispose = function dispose() { |
|||
$.removeData(this._element, DATA_KEY); |
|||
this._element = null; |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Button._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
|
|||
if (!data) { |
|||
data = new Button(this); |
|||
$(this).data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (config === 'toggle') { |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Button, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}]); |
|||
|
|||
return Button; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, function (event) { |
|||
event.preventDefault(); |
|||
|
|||
var 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, function (event) { |
|||
var 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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=button.js.map
|
@ -0,0 +1,486 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): carousel.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Carousel = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'carousel'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.carousel'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 600; |
|||
var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key
|
|||
var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key
|
|||
|
|||
var Default = { |
|||
interval: 5000, |
|||
keyboard: true, |
|||
slide: false, |
|||
pause: 'hover', |
|||
wrap: true |
|||
}; |
|||
|
|||
var DefaultType = { |
|||
interval: '(number|boolean)', |
|||
keyboard: 'boolean', |
|||
slide: '(boolean|string)', |
|||
pause: '(string|boolean)', |
|||
wrap: 'boolean' |
|||
}; |
|||
|
|||
var Direction = { |
|||
NEXT: 'next', |
|||
PREV: 'prev', |
|||
LEFT: 'left', |
|||
RIGHT: 'right' |
|||
}; |
|||
|
|||
var Event = { |
|||
SLIDE: 'slide' + EVENT_KEY, |
|||
SLID: 'slid' + EVENT_KEY, |
|||
KEYDOWN: 'keydown' + EVENT_KEY, |
|||
MOUSEENTER: 'mouseenter' + EVENT_KEY, |
|||
MOUSELEAVE: 'mouseleave' + EVENT_KEY, |
|||
LOAD_DATA_API: 'load' + EVENT_KEY + DATA_API_KEY, |
|||
CLICK_DATA_API: 'click' + EVENT_KEY + DATA_API_KEY |
|||
}; |
|||
|
|||
var 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' |
|||
}; |
|||
|
|||
var 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 |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Carousel = function () { |
|||
function Carousel(element, config) { |
|||
_classCallCheck(this, Carousel); |
|||
|
|||
this._items = null; |
|||
this._interval = null; |
|||
this._activeElement = null; |
|||
|
|||
this._isPaused = false; |
|||
this._isSliding = false; |
|||
|
|||
this._config = this._getConfig(config); |
|||
this._element = $(element)[0]; |
|||
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]; |
|||
|
|||
this._addEventListeners(); |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Carousel.prototype.next = function next() { |
|||
if (this._isSliding) { |
|||
throw new Error('Carousel is sliding'); |
|||
} |
|||
this._slide(Direction.NEXT); |
|||
}; |
|||
|
|||
Carousel.prototype.nextWhenVisible = function nextWhenVisible() { |
|||
// Don't call next when the page isn't visible
|
|||
if (!document.hidden) { |
|||
this.next(); |
|||
} |
|||
}; |
|||
|
|||
Carousel.prototype.prev = function prev() { |
|||
if (this._isSliding) { |
|||
throw new Error('Carousel is sliding'); |
|||
} |
|||
this._slide(Direction.PREVIOUS); |
|||
}; |
|||
|
|||
Carousel.prototype.pause = function 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; |
|||
}; |
|||
|
|||
Carousel.prototype.cycle = function 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); |
|||
} |
|||
}; |
|||
|
|||
Carousel.prototype.to = function to(index) { |
|||
var _this = this; |
|||
|
|||
this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]; |
|||
|
|||
var activeIndex = this._getItemIndex(this._activeElement); |
|||
|
|||
if (index > this._items.length - 1 || index < 0) { |
|||
return; |
|||
} |
|||
|
|||
if (this._isSliding) { |
|||
$(this._element).one(Event.SLID, function () { |
|||
return _this.to(index); |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
if (activeIndex === index) { |
|||
this.pause(); |
|||
this.cycle(); |
|||
return; |
|||
} |
|||
|
|||
var direction = index > activeIndex ? Direction.NEXT : Direction.PREVIOUS; |
|||
|
|||
this._slide(direction, this._items[index]); |
|||
}; |
|||
|
|||
Carousel.prototype.dispose = function 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
|
|||
|
|||
Carousel.prototype._getConfig = function _getConfig(config) { |
|||
config = $.extend({}, Default, config); |
|||
Util.typeCheckConfig(NAME, config, DefaultType); |
|||
return config; |
|||
}; |
|||
|
|||
Carousel.prototype._addEventListeners = function _addEventListeners() { |
|||
var _this2 = this; |
|||
|
|||
if (this._config.keyboard) { |
|||
$(this._element).on(Event.KEYDOWN, function (event) { |
|||
return _this2._keydown(event); |
|||
}); |
|||
} |
|||
|
|||
if (this._config.pause === 'hover' && !('ontouchstart' in document.documentElement)) { |
|||
$(this._element).on(Event.MOUSEENTER, function (event) { |
|||
return _this2.pause(event); |
|||
}).on(Event.MOUSELEAVE, function (event) { |
|||
return _this2.cycle(event); |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
Carousel.prototype._keydown = function _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: |
|||
return; |
|||
} |
|||
}; |
|||
|
|||
Carousel.prototype._getItemIndex = function _getItemIndex(element) { |
|||
this._items = $.makeArray($(element).parent().find(Selector.ITEM)); |
|||
return this._items.indexOf(element); |
|||
}; |
|||
|
|||
Carousel.prototype._getItemByDirection = function _getItemByDirection(direction, activeElement) { |
|||
var isNextDirection = direction === Direction.NEXT; |
|||
var isPrevDirection = direction === Direction.PREVIOUS; |
|||
var activeIndex = this._getItemIndex(activeElement); |
|||
var lastItemIndex = this._items.length - 1; |
|||
var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex; |
|||
|
|||
if (isGoingToWrap && !this._config.wrap) { |
|||
return activeElement; |
|||
} |
|||
|
|||
var delta = direction === Direction.PREVIOUS ? -1 : 1; |
|||
var itemIndex = (activeIndex + delta) % this._items.length; |
|||
|
|||
return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]; |
|||
}; |
|||
|
|||
Carousel.prototype._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) { |
|||
var slideEvent = $.Event(Event.SLIDE, { |
|||
relatedTarget: relatedTarget, |
|||
direction: eventDirectionName |
|||
}); |
|||
|
|||
$(this._element).trigger(slideEvent); |
|||
|
|||
return slideEvent; |
|||
}; |
|||
|
|||
Carousel.prototype._setActiveIndicatorElement = function _setActiveIndicatorElement(element) { |
|||
if (this._indicatorsElement) { |
|||
$(this._indicatorsElement).find(Selector.ACTIVE).removeClass(ClassName.ACTIVE); |
|||
|
|||
var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; |
|||
|
|||
if (nextIndicator) { |
|||
$(nextIndicator).addClass(ClassName.ACTIVE); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
Carousel.prototype._slide = function _slide(direction, element) { |
|||
var _this3 = this; |
|||
|
|||
var activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]; |
|||
var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement); |
|||
|
|||
var isCycling = Boolean(this._interval); |
|||
|
|||
var directionalClassName = void 0; |
|||
var orderClassName = void 0; |
|||
var eventDirectionName = void 0; |
|||
|
|||
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; |
|||
} |
|||
|
|||
var 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); |
|||
|
|||
var slidEvent = $.Event(Event.SLID, { |
|||
relatedTarget: nextElement, |
|||
direction: eventDirectionName |
|||
}); |
|||
|
|||
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, function () { |
|||
$(nextElement).removeClass(directionalClassName + ' ' + orderClassName).addClass(ClassName.ACTIVE); |
|||
|
|||
$(activeElement).removeClass(ClassName.ACTIVE + ' ' + orderClassName + ' ' + directionalClassName); |
|||
|
|||
_this3._isSliding = false; |
|||
|
|||
setTimeout(function () { |
|||
return $(_this3._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
|
|||
|
|||
Carousel._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
var _config = $.extend({}, Default, $(this).data()); |
|||
|
|||
if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { |
|||
$.extend(_config, config); |
|||
} |
|||
|
|||
var 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 (data[action] === undefined) { |
|||
throw new Error('No method named "' + action + '"'); |
|||
} |
|||
data[action](); |
|||
} else if (_config.interval) { |
|||
data.pause(); |
|||
data.cycle(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
Carousel._dataApiClickHandler = function _dataApiClickHandler(event) { |
|||
var selector = Util.getSelectorFromElement(this); |
|||
|
|||
if (!selector) { |
|||
return; |
|||
} |
|||
|
|||
var target = $(selector)[0]; |
|||
|
|||
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) { |
|||
return; |
|||
} |
|||
|
|||
var config = $.extend({}, $(target).data(), $(this).data()); |
|||
var 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(); |
|||
}; |
|||
|
|||
_createClass(Carousel, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}]); |
|||
|
|||
return Carousel; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler); |
|||
|
|||
$(window).on(Event.LOAD_DATA_API, function () { |
|||
$(Selector.DATA_RIDE).each(function () { |
|||
var $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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=carousel.js.map
|
@ -0,0 +1,358 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): collapse.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Collapse = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'collapse'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.collapse'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 600; |
|||
|
|||
var Default = { |
|||
toggle: true, |
|||
parent: '' |
|||
}; |
|||
|
|||
var DefaultType = { |
|||
toggle: 'boolean', |
|||
parent: 'string' |
|||
}; |
|||
|
|||
var 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 |
|||
}; |
|||
|
|||
var ClassName = { |
|||
SHOW: 'show', |
|||
COLLAPSE: 'collapse', |
|||
COLLAPSING: 'collapsing', |
|||
COLLAPSED: 'collapsed' |
|||
}; |
|||
|
|||
var Dimension = { |
|||
WIDTH: 'width', |
|||
HEIGHT: 'height' |
|||
}; |
|||
|
|||
var Selector = { |
|||
ACTIVES: '.card > .show, .card > .collapsing', |
|||
DATA_TOGGLE: '[data-toggle="collapse"]' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Collapse = function () { |
|||
function Collapse(element, config) { |
|||
_classCallCheck(this, Collapse); |
|||
|
|||
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 + '"]'))); |
|||
|
|||
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
|
|||
|
|||
// public
|
|||
|
|||
Collapse.prototype.toggle = function toggle() { |
|||
if ($(this._element).hasClass(ClassName.SHOW)) { |
|||
this.hide(); |
|||
} else { |
|||
this.show(); |
|||
} |
|||
}; |
|||
|
|||
Collapse.prototype.show = function show() { |
|||
var _this = this; |
|||
|
|||
if (this._isTransitioning) { |
|||
throw new Error('Collapse is transitioning'); |
|||
} |
|||
|
|||
if ($(this._element).hasClass(ClassName.SHOW)) { |
|||
return; |
|||
} |
|||
|
|||
var actives = void 0; |
|||
var activesData = void 0; |
|||
|
|||
if (this._parent) { |
|||
actives = $.makeArray($(this._parent).find(Selector.ACTIVES)); |
|||
if (!actives.length) { |
|||
actives = null; |
|||
} |
|||
} |
|||
|
|||
if (actives) { |
|||
activesData = $(actives).data(DATA_KEY); |
|||
if (activesData && activesData._isTransitioning) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
var startEvent = $.Event(Event.SHOW); |
|||
$(this._element).trigger(startEvent); |
|||
if (startEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
if (actives) { |
|||
Collapse._jQueryInterface.call($(actives), 'hide'); |
|||
if (!activesData) { |
|||
$(actives).data(DATA_KEY, null); |
|||
} |
|||
} |
|||
|
|||
var dimension = this._getDimension(); |
|||
|
|||
$(this._element).removeClass(ClassName.COLLAPSE).addClass(ClassName.COLLAPSING); |
|||
|
|||
this._element.style[dimension] = 0; |
|||
this._element.setAttribute('aria-expanded', true); |
|||
|
|||
if (this._triggerArray.length) { |
|||
$(this._triggerArray).removeClass(ClassName.COLLAPSED).attr('aria-expanded', true); |
|||
} |
|||
|
|||
this.setTransitioning(true); |
|||
|
|||
var complete = function 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; |
|||
} |
|||
|
|||
var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); |
|||
var scrollSize = 'scroll' + capitalizedDimension; |
|||
|
|||
$(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION); |
|||
|
|||
this._element.style[dimension] = this._element[scrollSize] + 'px'; |
|||
}; |
|||
|
|||
Collapse.prototype.hide = function hide() { |
|||
var _this2 = this; |
|||
|
|||
if (this._isTransitioning) { |
|||
throw new Error('Collapse is transitioning'); |
|||
} |
|||
|
|||
if (!$(this._element).hasClass(ClassName.SHOW)) { |
|||
return; |
|||
} |
|||
|
|||
var startEvent = $.Event(Event.HIDE); |
|||
$(this._element).trigger(startEvent); |
|||
if (startEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
var dimension = this._getDimension(); |
|||
var offsetDimension = dimension === Dimension.WIDTH ? 'offsetWidth' : 'offsetHeight'; |
|||
|
|||
this._element.style[dimension] = this._element[offsetDimension] + 'px'; |
|||
|
|||
Util.reflow(this._element); |
|||
|
|||
$(this._element).addClass(ClassName.COLLAPSING).removeClass(ClassName.COLLAPSE).removeClass(ClassName.SHOW); |
|||
|
|||
this._element.setAttribute('aria-expanded', false); |
|||
|
|||
if (this._triggerArray.length) { |
|||
$(this._triggerArray).addClass(ClassName.COLLAPSED).attr('aria-expanded', false); |
|||
} |
|||
|
|||
this.setTransitioning(true); |
|||
|
|||
var complete = function complete() { |
|||
_this2.setTransitioning(false); |
|||
$(_this2._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); |
|||
}; |
|||
|
|||
Collapse.prototype.setTransitioning = function setTransitioning(isTransitioning) { |
|||
this._isTransitioning = isTransitioning; |
|||
}; |
|||
|
|||
Collapse.prototype.dispose = function dispose() { |
|||
$.removeData(this._element, DATA_KEY); |
|||
|
|||
this._config = null; |
|||
this._parent = null; |
|||
this._element = null; |
|||
this._triggerArray = null; |
|||
this._isTransitioning = null; |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Collapse.prototype._getConfig = function _getConfig(config) { |
|||
config = $.extend({}, Default, config); |
|||
config.toggle = Boolean(config.toggle); // coerce string values
|
|||
Util.typeCheckConfig(NAME, config, DefaultType); |
|||
return config; |
|||
}; |
|||
|
|||
Collapse.prototype._getDimension = function _getDimension() { |
|||
var hasWidth = $(this._element).hasClass(Dimension.WIDTH); |
|||
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT; |
|||
}; |
|||
|
|||
Collapse.prototype._getParent = function _getParent() { |
|||
var _this3 = this; |
|||
|
|||
var parent = $(this._config.parent)[0]; |
|||
var selector = '[data-toggle="collapse"][data-parent="' + this._config.parent + '"]'; |
|||
|
|||
$(parent).find(selector).each(function (i, element) { |
|||
_this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); |
|||
}); |
|||
|
|||
return parent; |
|||
}; |
|||
|
|||
Collapse.prototype._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { |
|||
if (element) { |
|||
var isOpen = $(element).hasClass(ClassName.SHOW); |
|||
element.setAttribute('aria-expanded', isOpen); |
|||
|
|||
if (triggerArray.length) { |
|||
$(triggerArray).toggleClass(ClassName.COLLAPSED, !isOpen).attr('aria-expanded', isOpen); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Collapse._getTargetFromElement = function _getTargetFromElement(element) { |
|||
var selector = Util.getSelectorFromElement(element); |
|||
return selector ? $(selector)[0] : null; |
|||
}; |
|||
|
|||
Collapse._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var $this = $(this); |
|||
var data = $this.data(DATA_KEY); |
|||
var _config = $.extend({}, Default, $this.data(), (typeof config === 'undefined' ? 'undefined' : _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 (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Collapse, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}]); |
|||
|
|||
return Collapse; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { |
|||
event.preventDefault(); |
|||
|
|||
var target = Collapse._getTargetFromElement(this); |
|||
var data = $(target).data(DATA_KEY); |
|||
var config = data ? 'toggle' : $(this).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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=collapse.js.map
|
@ -0,0 +1,287 @@ |
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): dropdown.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Dropdown = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'dropdown'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.dropdown'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
|
|||
var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
|
|||
var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
|
|||
var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
|
|||
|
|||
var 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, |
|||
FOCUSIN_DATA_API: 'focusin' + EVENT_KEY + DATA_API_KEY, |
|||
KEYDOWN_DATA_API: 'keydown' + EVENT_KEY + DATA_API_KEY |
|||
}; |
|||
|
|||
var ClassName = { |
|||
BACKDROP: 'dropdown-backdrop', |
|||
DISABLED: 'disabled', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
var Selector = { |
|||
BACKDROP: '.dropdown-backdrop', |
|||
DATA_TOGGLE: '[data-toggle="dropdown"]', |
|||
FORM_CHILD: '.dropdown form', |
|||
ROLE_MENU: '[role="menu"]', |
|||
ROLE_LISTBOX: '[role="listbox"]', |
|||
NAVBAR_NAV: '.navbar-nav', |
|||
VISIBLE_ITEMS: '[role="menu"] li:not(.disabled) a, ' + '[role="listbox"] li:not(.disabled) a' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Dropdown = function () { |
|||
function Dropdown(element) { |
|||
_classCallCheck(this, Dropdown); |
|||
|
|||
this._element = element; |
|||
|
|||
this._addEventListeners(); |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Dropdown.prototype.toggle = function toggle() { |
|||
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { |
|||
return false; |
|||
} |
|||
|
|||
var parent = Dropdown._getParentFromElement(this); |
|||
var isActive = $(parent).hasClass(ClassName.SHOW); |
|||
|
|||
Dropdown._clearMenus(); |
|||
|
|||
if (isActive) { |
|||
return false; |
|||
} |
|||
|
|||
if ('ontouchstart' in document.documentElement && !$(parent).closest(Selector.NAVBAR_NAV).length) { |
|||
|
|||
// if mobile we use a backdrop because click events don't delegate
|
|||
var dropdown = document.createElement('div'); |
|||
dropdown.className = ClassName.BACKDROP; |
|||
$(dropdown).insertBefore(this); |
|||
$(dropdown).on('click', Dropdown._clearMenus); |
|||
} |
|||
|
|||
var relatedTarget = { |
|||
relatedTarget: this |
|||
}; |
|||
var showEvent = $.Event(Event.SHOW, relatedTarget); |
|||
|
|||
$(parent).trigger(showEvent); |
|||
|
|||
if (showEvent.isDefaultPrevented()) { |
|||
return false; |
|||
} |
|||
|
|||
this.focus(); |
|||
this.setAttribute('aria-expanded', true); |
|||
|
|||
$(parent).toggleClass(ClassName.SHOW); |
|||
$(parent).trigger($.Event(Event.SHOWN, relatedTarget)); |
|||
|
|||
return false; |
|||
}; |
|||
|
|||
Dropdown.prototype.dispose = function dispose() { |
|||
$.removeData(this._element, DATA_KEY); |
|||
$(this._element).off(EVENT_KEY); |
|||
this._element = null; |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Dropdown.prototype._addEventListeners = function _addEventListeners() { |
|||
$(this._element).on(Event.CLICK, this.toggle); |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Dropdown._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
|
|||
if (!data) { |
|||
data = new Dropdown(this); |
|||
$(this).data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (typeof config === 'string') { |
|||
if (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config].call(this); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
Dropdown._clearMenus = function _clearMenus(event) { |
|||
if (event && event.which === RIGHT_MOUSE_BUTTON_WHICH) { |
|||
return; |
|||
} |
|||
|
|||
var backdrop = $(Selector.BACKDROP)[0]; |
|||
if (backdrop) { |
|||
backdrop.parentNode.removeChild(backdrop); |
|||
} |
|||
|
|||
var toggles = $.makeArray($(Selector.DATA_TOGGLE)); |
|||
|
|||
for (var i = 0; i < toggles.length; i++) { |
|||
var parent = Dropdown._getParentFromElement(toggles[i]); |
|||
var relatedTarget = { |
|||
relatedTarget: toggles[i] |
|||
}; |
|||
|
|||
if (!$(parent).hasClass(ClassName.SHOW)) { |
|||
continue; |
|||
} |
|||
|
|||
if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'focusin') && $.contains(parent, event.target)) { |
|||
continue; |
|||
} |
|||
|
|||
var hideEvent = $.Event(Event.HIDE, relatedTarget); |
|||
$(parent).trigger(hideEvent); |
|||
if (hideEvent.isDefaultPrevented()) { |
|||
continue; |
|||
} |
|||
|
|||
toggles[i].setAttribute('aria-expanded', 'false'); |
|||
|
|||
$(parent).removeClass(ClassName.SHOW).trigger($.Event(Event.HIDDEN, relatedTarget)); |
|||
} |
|||
}; |
|||
|
|||
Dropdown._getParentFromElement = function _getParentFromElement(element) { |
|||
var parent = void 0; |
|||
var selector = Util.getSelectorFromElement(element); |
|||
|
|||
if (selector) { |
|||
parent = $(selector)[0]; |
|||
} |
|||
|
|||
return parent || element.parentNode; |
|||
}; |
|||
|
|||
Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) { |
|||
if (!/(38|40|27|32)/.test(event.which) || /input|textarea/i.test(event.target.tagName)) { |
|||
return; |
|||
} |
|||
|
|||
event.preventDefault(); |
|||
event.stopPropagation(); |
|||
|
|||
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { |
|||
return; |
|||
} |
|||
|
|||
var parent = Dropdown._getParentFromElement(this); |
|||
var isActive = $(parent).hasClass(ClassName.SHOW); |
|||
|
|||
if (!isActive && event.which !== ESCAPE_KEYCODE || isActive && event.which === ESCAPE_KEYCODE) { |
|||
|
|||
if (event.which === ESCAPE_KEYCODE) { |
|||
var toggle = $(parent).find(Selector.DATA_TOGGLE)[0]; |
|||
$(toggle).trigger('focus'); |
|||
} |
|||
|
|||
$(this).trigger('click'); |
|||
return; |
|||
} |
|||
|
|||
var items = $(parent).find(Selector.VISIBLE_ITEMS).get(); |
|||
|
|||
if (!items.length) { |
|||
return; |
|||
} |
|||
|
|||
var 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(); |
|||
}; |
|||
|
|||
_createClass(Dropdown, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}]); |
|||
|
|||
return Dropdown; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler).on(Event.CLICK_DATA_API + ' ' + Event.FOCUSIN_DATA_API, Dropdown._clearMenus).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle).on(Event.CLICK_DATA_API, Selector.FORM_CHILD, function (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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=dropdown.js.map
|
@ -0,0 +1,535 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): modal.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Modal = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'modal'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.modal'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 300; |
|||
var BACKDROP_TRANSITION_DURATION = 150; |
|||
var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
|
|||
|
|||
var Default = { |
|||
backdrop: true, |
|||
keyboard: true, |
|||
focus: true, |
|||
show: true |
|||
}; |
|||
|
|||
var DefaultType = { |
|||
backdrop: '(boolean|string)', |
|||
keyboard: 'boolean', |
|||
focus: 'boolean', |
|||
show: 'boolean' |
|||
}; |
|||
|
|||
var 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 |
|||
}; |
|||
|
|||
var ClassName = { |
|||
SCROLLBAR_MEASURER: 'modal-scrollbar-measure', |
|||
BACKDROP: 'modal-backdrop', |
|||
OPEN: 'modal-open', |
|||
FADE: 'fade', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
var Selector = { |
|||
DIALOG: '.modal-dialog', |
|||
DATA_TOGGLE: '[data-toggle="modal"]', |
|||
DATA_DISMISS: '[data-dismiss="modal"]', |
|||
FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Modal = function () { |
|||
function Modal(element, config) { |
|||
_classCallCheck(this, Modal); |
|||
|
|||
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._isTransitioning = false; |
|||
this._originalBodyPadding = 0; |
|||
this._scrollbarWidth = 0; |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Modal.prototype.toggle = function toggle(relatedTarget) { |
|||
return this._isShown ? this.hide() : this.show(relatedTarget); |
|||
}; |
|||
|
|||
Modal.prototype.show = function show(relatedTarget) { |
|||
var _this = this; |
|||
|
|||
if (this._isTransitioning) { |
|||
throw new Error('Modal is transitioning'); |
|||
} |
|||
|
|||
if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) { |
|||
this._isTransitioning = true; |
|||
} |
|||
var showEvent = $.Event(Event.SHOW, { |
|||
relatedTarget: relatedTarget |
|||
}); |
|||
|
|||
$(this._element).trigger(showEvent); |
|||
|
|||
if (this._isShown || showEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
this._isShown = true; |
|||
|
|||
this._checkScrollbar(); |
|||
this._setScrollbar(); |
|||
|
|||
$(document.body).addClass(ClassName.OPEN); |
|||
|
|||
this._setEscapeEvent(); |
|||
this._setResizeEvent(); |
|||
|
|||
$(this._element).on(Event.CLICK_DISMISS, Selector.DATA_DISMISS, function (event) { |
|||
return _this.hide(event); |
|||
}); |
|||
|
|||
$(this._dialog).on(Event.MOUSEDOWN_DISMISS, function () { |
|||
$(_this._element).one(Event.MOUSEUP_DISMISS, function (event) { |
|||
if ($(event.target).is(_this._element)) { |
|||
_this._ignoreBackdropClick = true; |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
this._showBackdrop(function () { |
|||
return _this._showElement(relatedTarget); |
|||
}); |
|||
}; |
|||
|
|||
Modal.prototype.hide = function hide(event) { |
|||
var _this2 = this; |
|||
|
|||
if (event) { |
|||
event.preventDefault(); |
|||
} |
|||
|
|||
if (this._isTransitioning) { |
|||
throw new Error('Modal is transitioning'); |
|||
} |
|||
|
|||
var transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE); |
|||
if (transition) { |
|||
this._isTransitioning = true; |
|||
} |
|||
|
|||
var hideEvent = $.Event(Event.HIDE); |
|||
$(this._element).trigger(hideEvent); |
|||
|
|||
if (!this._isShown || hideEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
this._isShown = false; |
|||
|
|||
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, function (event) { |
|||
return _this2._hideModal(event); |
|||
}).emulateTransitionEnd(TRANSITION_DURATION); |
|||
} else { |
|||
this._hideModal(); |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype.dispose = function 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._originalBodyPadding = null; |
|||
this._scrollbarWidth = null; |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Modal.prototype._getConfig = function _getConfig(config) { |
|||
config = $.extend({}, Default, config); |
|||
Util.typeCheckConfig(NAME, config, DefaultType); |
|||
return config; |
|||
}; |
|||
|
|||
Modal.prototype._showElement = function _showElement(relatedTarget) { |
|||
var _this3 = this; |
|||
|
|||
var transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE); |
|||
|
|||
if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { |
|||
// don't move modals 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(); |
|||
} |
|||
|
|||
var shownEvent = $.Event(Event.SHOWN, { |
|||
relatedTarget: relatedTarget |
|||
}); |
|||
|
|||
var transitionComplete = function transitionComplete() { |
|||
if (_this3._config.focus) { |
|||
_this3._element.focus(); |
|||
} |
|||
_this3._isTransitioning = false; |
|||
$(_this3._element).trigger(shownEvent); |
|||
}; |
|||
|
|||
if (transition) { |
|||
$(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(TRANSITION_DURATION); |
|||
} else { |
|||
transitionComplete(); |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._enforceFocus = function _enforceFocus() { |
|||
var _this4 = this; |
|||
|
|||
$(document).off(Event.FOCUSIN) // guard against infinite focus loop
|
|||
.on(Event.FOCUSIN, function (event) { |
|||
if (document !== event.target && _this4._element !== event.target && !$(_this4._element).has(event.target).length) { |
|||
_this4._element.focus(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
Modal.prototype._setEscapeEvent = function _setEscapeEvent() { |
|||
var _this5 = this; |
|||
|
|||
if (this._isShown && this._config.keyboard) { |
|||
$(this._element).on(Event.KEYDOWN_DISMISS, function (event) { |
|||
if (event.which === ESCAPE_KEYCODE) { |
|||
_this5.hide(); |
|||
} |
|||
}); |
|||
} else if (!this._isShown) { |
|||
$(this._element).off(Event.KEYDOWN_DISMISS); |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._setResizeEvent = function _setResizeEvent() { |
|||
var _this6 = this; |
|||
|
|||
if (this._isShown) { |
|||
$(window).on(Event.RESIZE, function (event) { |
|||
return _this6._handleUpdate(event); |
|||
}); |
|||
} else { |
|||
$(window).off(Event.RESIZE); |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._hideModal = function _hideModal() { |
|||
var _this7 = this; |
|||
|
|||
this._element.style.display = 'none'; |
|||
this._element.setAttribute('aria-hidden', 'true'); |
|||
this._isTransitioning = false; |
|||
this._showBackdrop(function () { |
|||
$(document.body).removeClass(ClassName.OPEN); |
|||
_this7._resetAdjustments(); |
|||
_this7._resetScrollbar(); |
|||
$(_this7._element).trigger(Event.HIDDEN); |
|||
}); |
|||
}; |
|||
|
|||
Modal.prototype._removeBackdrop = function _removeBackdrop() { |
|||
if (this._backdrop) { |
|||
$(this._backdrop).remove(); |
|||
this._backdrop = null; |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._showBackdrop = function _showBackdrop(callback) { |
|||
var _this8 = this; |
|||
|
|||
var animate = $(this._element).hasClass(ClassName.FADE) ? ClassName.FADE : ''; |
|||
|
|||
if (this._isShown && this._config.backdrop) { |
|||
var 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, function (event) { |
|||
if (_this8._ignoreBackdropClick) { |
|||
_this8._ignoreBackdropClick = false; |
|||
return; |
|||
} |
|||
if (event.target !== event.currentTarget) { |
|||
return; |
|||
} |
|||
if (_this8._config.backdrop === 'static') { |
|||
_this8._element.focus(); |
|||
} else { |
|||
_this8.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); |
|||
|
|||
var callbackRemove = function callbackRemove() { |
|||
_this8._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
|
|||
// ----------------------------------------------------------------------
|
|||
|
|||
Modal.prototype._handleUpdate = function _handleUpdate() { |
|||
this._adjustDialog(); |
|||
}; |
|||
|
|||
Modal.prototype._adjustDialog = function _adjustDialog() { |
|||
var 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'; |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._resetAdjustments = function _resetAdjustments() { |
|||
this._element.style.paddingLeft = ''; |
|||
this._element.style.paddingRight = ''; |
|||
}; |
|||
|
|||
Modal.prototype._checkScrollbar = function _checkScrollbar() { |
|||
this._isBodyOverflowing = document.body.clientWidth < window.innerWidth; |
|||
this._scrollbarWidth = this._getScrollbarWidth(); |
|||
}; |
|||
|
|||
Modal.prototype._setScrollbar = function _setScrollbar() { |
|||
var bodyPadding = parseInt($(Selector.FIXED_CONTENT).css('padding-right') || 0, 10); |
|||
|
|||
this._originalBodyPadding = document.body.style.paddingRight || ''; |
|||
|
|||
if (this._isBodyOverflowing) { |
|||
document.body.style.paddingRight = bodyPadding + this._scrollbarWidth + 'px'; |
|||
} |
|||
}; |
|||
|
|||
Modal.prototype._resetScrollbar = function _resetScrollbar() { |
|||
document.body.style.paddingRight = this._originalBodyPadding; |
|||
}; |
|||
|
|||
Modal.prototype._getScrollbarWidth = function _getScrollbarWidth() { |
|||
// thx d.walsh
|
|||
var scrollDiv = document.createElement('div'); |
|||
scrollDiv.className = ClassName.SCROLLBAR_MEASURER; |
|||
document.body.appendChild(scrollDiv); |
|||
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; |
|||
document.body.removeChild(scrollDiv); |
|||
return scrollbarWidth; |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
var _config = $.extend({}, Modal.Default, $(this).data(), (typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object' && config); |
|||
|
|||
if (!data) { |
|||
data = new Modal(this, _config); |
|||
$(this).data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (typeof config === 'string') { |
|||
if (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](relatedTarget); |
|||
} else if (_config.show) { |
|||
data.show(relatedTarget); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Modal, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}]); |
|||
|
|||
return Modal; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { |
|||
var _this9 = this; |
|||
|
|||
var target = void 0; |
|||
var selector = Util.getSelectorFromElement(this); |
|||
|
|||
if (selector) { |
|||
target = $(selector)[0]; |
|||
} |
|||
|
|||
var config = $(target).data(DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data()); |
|||
|
|||
if (this.tagName === 'A' || this.tagName === 'AREA') { |
|||
event.preventDefault(); |
|||
} |
|||
|
|||
var $target = $(target).one(Event.SHOW, function (showEvent) { |
|||
if (showEvent.isDefaultPrevented()) { |
|||
// only register focus restorer if modal will actually get shown
|
|||
return; |
|||
} |
|||
|
|||
$target.one(Event.HIDDEN, function () { |
|||
if ($(_this9).is(':visible')) { |
|||
_this9.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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=modal.js.map
|
@ -0,0 +1,193 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } |
|||
|
|||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): popover.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Popover = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'popover'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.popover'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
|
|||
var Default = $.extend({}, Tooltip.Default, { |
|||
placement: 'right', |
|||
trigger: 'click', |
|||
content: '', |
|||
template: '<div class="popover" role="tooltip">' + '<h3 class="popover-title"></h3>' + '<div class="popover-content"></div></div>' |
|||
}); |
|||
|
|||
var DefaultType = $.extend({}, Tooltip.DefaultType, { |
|||
content: '(string|element|function)' |
|||
}); |
|||
|
|||
var ClassName = { |
|||
FADE: 'fade', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
var Selector = { |
|||
TITLE: '.popover-title', |
|||
CONTENT: '.popover-content' |
|||
}; |
|||
|
|||
var 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 |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Popover = function (_Tooltip) { |
|||
_inherits(Popover, _Tooltip); |
|||
|
|||
function Popover() { |
|||
_classCallCheck(this, Popover); |
|||
|
|||
return _possibleConstructorReturn(this, _Tooltip.apply(this, arguments)); |
|||
} |
|||
|
|||
// overrides
|
|||
|
|||
Popover.prototype.isWithContent = function isWithContent() { |
|||
return this.getTitle() || this._getContent(); |
|||
}; |
|||
|
|||
Popover.prototype.getTipElement = function getTipElement() { |
|||
return this.tip = this.tip || $(this.config.template)[0]; |
|||
}; |
|||
|
|||
Popover.prototype.setContent = function setContent() { |
|||
var $tip = $(this.getTipElement()); |
|||
|
|||
// we use append for html objects to maintain js events
|
|||
this.setElementContent($tip.find(Selector.TITLE), this.getTitle()); |
|||
this.setElementContent($tip.find(Selector.CONTENT), this._getContent()); |
|||
|
|||
$tip.removeClass(ClassName.FADE + ' ' + ClassName.SHOW); |
|||
|
|||
this.cleanupTether(); |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Popover.prototype._getContent = function _getContent() { |
|||
return this.element.getAttribute('data-content') || (typeof this.config.content === 'function' ? this.config.content.call(this.element) : this.config.content); |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Popover._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
var _config = (typeof config === 'undefined' ? 'undefined' : _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 (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Popover, null, [{ |
|||
key: 'VERSION', |
|||
|
|||
|
|||
// getters
|
|||
|
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}, { |
|||
key: 'NAME', |
|||
get: function get() { |
|||
return NAME; |
|||
} |
|||
}, { |
|||
key: 'DATA_KEY', |
|||
get: function get() { |
|||
return DATA_KEY; |
|||
} |
|||
}, { |
|||
key: 'Event', |
|||
get: function get() { |
|||
return Event; |
|||
} |
|||
}, { |
|||
key: 'EVENT_KEY', |
|||
get: function get() { |
|||
return EVENT_KEY; |
|||
} |
|||
}, { |
|||
key: 'DefaultType', |
|||
get: function get() { |
|||
return DefaultType; |
|||
} |
|||
}]); |
|||
|
|||
return Popover; |
|||
}(Tooltip); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* jQuery |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$.fn[NAME] = Popover._jQueryInterface; |
|||
$.fn[NAME].Constructor = Popover; |
|||
$.fn[NAME].noConflict = function () { |
|||
$.fn[NAME] = JQUERY_NO_CONFLICT; |
|||
return Popover._jQueryInterface; |
|||
}; |
|||
|
|||
return Popover; |
|||
}(jQuery); |
|||
//# sourceMappingURL=popover.js.map
|
@ -0,0 +1,318 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): scrollspy.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var ScrollSpy = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'scrollspy'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.scrollspy'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
|
|||
var Default = { |
|||
offset: 10, |
|||
method: 'auto', |
|||
target: '' |
|||
}; |
|||
|
|||
var DefaultType = { |
|||
offset: 'number', |
|||
method: 'string', |
|||
target: '(string|element)' |
|||
}; |
|||
|
|||
var Event = { |
|||
ACTIVATE: 'activate' + EVENT_KEY, |
|||
SCROLL: 'scroll' + EVENT_KEY, |
|||
LOAD_DATA_API: 'load' + EVENT_KEY + DATA_API_KEY |
|||
}; |
|||
|
|||
var ClassName = { |
|||
DROPDOWN_ITEM: 'dropdown-item', |
|||
DROPDOWN_MENU: 'dropdown-menu', |
|||
NAV_LINK: 'nav-link', |
|||
NAV: 'nav', |
|||
ACTIVE: 'active' |
|||
}; |
|||
|
|||
var Selector = { |
|||
DATA_SPY: '[data-spy="scroll"]', |
|||
ACTIVE: '.active', |
|||
LIST_ITEM: '.list-item', |
|||
LI: 'li', |
|||
LI_DROPDOWN: 'li.dropdown', |
|||
NAV_LINKS: '.nav-link', |
|||
DROPDOWN: '.dropdown', |
|||
DROPDOWN_ITEMS: '.dropdown-item', |
|||
DROPDOWN_TOGGLE: '.dropdown-toggle' |
|||
}; |
|||
|
|||
var OffsetMethod = { |
|||
OFFSET: 'offset', |
|||
POSITION: 'position' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var ScrollSpy = function () { |
|||
function ScrollSpy(element, config) { |
|||
var _this = this; |
|||
|
|||
_classCallCheck(this, ScrollSpy); |
|||
|
|||
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.DROPDOWN_ITEMS); |
|||
this._offsets = []; |
|||
this._targets = []; |
|||
this._activeTarget = null; |
|||
this._scrollHeight = 0; |
|||
|
|||
$(this._scrollElement).on(Event.SCROLL, function (event) { |
|||
return _this._process(event); |
|||
}); |
|||
|
|||
this.refresh(); |
|||
this._process(); |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
ScrollSpy.prototype.refresh = function refresh() { |
|||
var _this2 = this; |
|||
|
|||
var autoMethod = this._scrollElement !== this._scrollElement.window ? OffsetMethod.POSITION : OffsetMethod.OFFSET; |
|||
|
|||
var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; |
|||
|
|||
var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0; |
|||
|
|||
this._offsets = []; |
|||
this._targets = []; |
|||
|
|||
this._scrollHeight = this._getScrollHeight(); |
|||
|
|||
var targets = $.makeArray($(this._selector)); |
|||
|
|||
targets.map(function (element) { |
|||
var target = void 0; |
|||
var targetSelector = Util.getSelectorFromElement(element); |
|||
|
|||
if (targetSelector) { |
|||
target = $(targetSelector)[0]; |
|||
} |
|||
|
|||
if (target && (target.offsetWidth || target.offsetHeight)) { |
|||
// todo (fat): remove sketch reliance on jQuery position/offset
|
|||
return [$(target)[offsetMethod]().top + offsetBase, targetSelector]; |
|||
} |
|||
return null; |
|||
}).filter(function (item) { |
|||
return item; |
|||
}).sort(function (a, b) { |
|||
return a[0] - b[0]; |
|||
}).forEach(function (item) { |
|||
_this2._offsets.push(item[0]); |
|||
_this2._targets.push(item[1]); |
|||
}); |
|||
}; |
|||
|
|||
ScrollSpy.prototype.dispose = function 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
|
|||
|
|||
ScrollSpy.prototype._getConfig = function _getConfig(config) { |
|||
config = $.extend({}, Default, config); |
|||
|
|||
if (typeof config.target !== 'string') { |
|||
var 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; |
|||
}; |
|||
|
|||
ScrollSpy.prototype._getScrollTop = function _getScrollTop() { |
|||
return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop; |
|||
}; |
|||
|
|||
ScrollSpy.prototype._getScrollHeight = function _getScrollHeight() { |
|||
return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); |
|||
}; |
|||
|
|||
ScrollSpy.prototype._getOffsetHeight = function _getOffsetHeight() { |
|||
return this._scrollElement === window ? window.innerHeight : this._scrollElement.offsetHeight; |
|||
}; |
|||
|
|||
ScrollSpy.prototype._process = function _process() { |
|||
var scrollTop = this._getScrollTop() + this._config.offset; |
|||
var scrollHeight = this._getScrollHeight(); |
|||
var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight(); |
|||
|
|||
if (this._scrollHeight !== scrollHeight) { |
|||
this.refresh(); |
|||
} |
|||
|
|||
if (scrollTop >= maxScroll) { |
|||
var 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 (var i = this._offsets.length; i--;) { |
|||
var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (this._offsets[i + 1] === undefined || scrollTop < this._offsets[i + 1]); |
|||
|
|||
if (isActiveTarget) { |
|||
this._activate(this._targets[i]); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
ScrollSpy.prototype._activate = function _activate(target) { |
|||
this._activeTarget = target; |
|||
|
|||
this._clear(); |
|||
|
|||
var queries = this._selector.split(','); |
|||
queries = queries.map(function (selector) { |
|||
return selector + '[data-target="' + target + '"],' + (selector + '[href="' + target + '"]'); |
|||
}); |
|||
|
|||
var $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 { |
|||
// todo (fat) this is kinda sus...
|
|||
// recursively add actives to tested nav-links
|
|||
$link.parents(Selector.LI).find('> ' + Selector.NAV_LINKS).addClass(ClassName.ACTIVE); |
|||
} |
|||
|
|||
$(this._scrollElement).trigger(Event.ACTIVATE, { |
|||
relatedTarget: target |
|||
}); |
|||
}; |
|||
|
|||
ScrollSpy.prototype._clear = function _clear() { |
|||
$(this._selector).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE); |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
ScrollSpy._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
var _config = (typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object' && config; |
|||
|
|||
if (!data) { |
|||
data = new ScrollSpy(this, _config); |
|||
$(this).data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (typeof config === 'string') { |
|||
if (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(ScrollSpy, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}]); |
|||
|
|||
return ScrollSpy; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(window).on(Event.LOAD_DATA_API, function () { |
|||
var scrollSpys = $.makeArray($(Selector.DATA_SPY)); |
|||
|
|||
for (var i = scrollSpys.length; i--;) { |
|||
var $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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=scrollspy.js.map
|
@ -0,0 +1,258 @@ |
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): tab.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Tab = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'tab'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.tab'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var DATA_API_KEY = '.data-api'; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 150; |
|||
|
|||
var 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 |
|||
}; |
|||
|
|||
var ClassName = { |
|||
DROPDOWN_MENU: 'dropdown-menu', |
|||
ACTIVE: 'active', |
|||
DISABLED: 'disabled', |
|||
FADE: 'fade', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
var Selector = { |
|||
A: 'a', |
|||
LI: 'li', |
|||
DROPDOWN: '.dropdown', |
|||
LIST: 'ul:not(.dropdown-menu), ol:not(.dropdown-menu), nav:not(.dropdown-menu)', |
|||
FADE_CHILD: '> .nav-item .fade, > .fade', |
|||
ACTIVE: '.active', |
|||
ACTIVE_CHILD: '> .nav-item > .active, > .active', |
|||
DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"]', |
|||
DROPDOWN_TOGGLE: '.dropdown-toggle', |
|||
DROPDOWN_ACTIVE_CHILD: '> .dropdown-menu .active' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Tab = function () { |
|||
function Tab(element) { |
|||
_classCallCheck(this, Tab); |
|||
|
|||
this._element = element; |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Tab.prototype.show = function show() { |
|||
var _this = this; |
|||
|
|||
if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && $(this._element).hasClass(ClassName.ACTIVE) || $(this._element).hasClass(ClassName.DISABLED)) { |
|||
return; |
|||
} |
|||
|
|||
var target = void 0; |
|||
var previous = void 0; |
|||
var listElement = $(this._element).closest(Selector.LIST)[0]; |
|||
var selector = Util.getSelectorFromElement(this._element); |
|||
|
|||
if (listElement) { |
|||
previous = $.makeArray($(listElement).find(Selector.ACTIVE)); |
|||
previous = previous[previous.length - 1]; |
|||
} |
|||
|
|||
var hideEvent = $.Event(Event.HIDE, { |
|||
relatedTarget: this._element |
|||
}); |
|||
|
|||
var 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); |
|||
|
|||
var complete = function complete() { |
|||
var hiddenEvent = $.Event(Event.HIDDEN, { |
|||
relatedTarget: _this._element |
|||
}); |
|||
|
|||
var shownEvent = $.Event(Event.SHOWN, { |
|||
relatedTarget: previous |
|||
}); |
|||
|
|||
$(previous).trigger(hiddenEvent); |
|||
$(_this._element).trigger(shownEvent); |
|||
}; |
|||
|
|||
if (target) { |
|||
this._activate(target, target.parentNode, complete); |
|||
} else { |
|||
complete(); |
|||
} |
|||
}; |
|||
|
|||
Tab.prototype.dispose = function dispose() { |
|||
$.removeClass(this._element, DATA_KEY); |
|||
this._element = null; |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Tab.prototype._activate = function _activate(element, container, callback) { |
|||
var _this2 = this; |
|||
|
|||
var active = $(container).find(Selector.ACTIVE_CHILD)[0]; |
|||
var isTransitioning = callback && Util.supportsTransitionEnd() && (active && $(active).hasClass(ClassName.FADE) || Boolean($(container).find(Selector.FADE_CHILD)[0])); |
|||
|
|||
var complete = function complete() { |
|||
return _this2._transitionComplete(element, active, isTransitioning, callback); |
|||
}; |
|||
|
|||
if (active && isTransitioning) { |
|||
$(active).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION); |
|||
} else { |
|||
complete(); |
|||
} |
|||
|
|||
if (active) { |
|||
$(active).removeClass(ClassName.SHOW); |
|||
} |
|||
}; |
|||
|
|||
Tab.prototype._transitionComplete = function _transitionComplete(element, active, isTransitioning, callback) { |
|||
if (active) { |
|||
$(active).removeClass(ClassName.ACTIVE); |
|||
|
|||
var dropdownChild = $(active.parentNode).find(Selector.DROPDOWN_ACTIVE_CHILD)[0]; |
|||
|
|||
if (dropdownChild) { |
|||
$(dropdownChild).removeClass(ClassName.ACTIVE); |
|||
} |
|||
|
|||
active.setAttribute('aria-expanded', false); |
|||
} |
|||
|
|||
$(element).addClass(ClassName.ACTIVE); |
|||
element.setAttribute('aria-expanded', true); |
|||
|
|||
if (isTransitioning) { |
|||
Util.reflow(element); |
|||
$(element).addClass(ClassName.SHOW); |
|||
} else { |
|||
$(element).removeClass(ClassName.FADE); |
|||
} |
|||
|
|||
if (element.parentNode && $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) { |
|||
|
|||
var 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
|
|||
|
|||
Tab._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var $this = $(this); |
|||
var data = $this.data(DATA_KEY); |
|||
|
|||
if (!data) { |
|||
data = new Tab(this); |
|||
$this.data(DATA_KEY, data); |
|||
} |
|||
|
|||
if (typeof config === 'string') { |
|||
if (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Tab, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}]); |
|||
|
|||
return Tab; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* 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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=tab.js.map
|
@ -0,0 +1,623 @@ |
|||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; |
|||
|
|||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|||
|
|||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): tooltip.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Tooltip = function ($) { |
|||
|
|||
/** |
|||
* Check for Tether dependency |
|||
* Tether - http://tether.io/
|
|||
*/ |
|||
if (typeof Tether === 'undefined') { |
|||
throw new Error('Bootstrap tooltips require Tether (http://tether.io/)'); |
|||
} |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var NAME = 'tooltip'; |
|||
var VERSION = '4.0.0-alpha.6'; |
|||
var DATA_KEY = 'bs.tooltip'; |
|||
var EVENT_KEY = '.' + DATA_KEY; |
|||
var JQUERY_NO_CONFLICT = $.fn[NAME]; |
|||
var TRANSITION_DURATION = 150; |
|||
var CLASS_PREFIX = 'bs-tether'; |
|||
|
|||
var Default = { |
|||
animation: true, |
|||
template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-inner"></div></div>', |
|||
trigger: 'hover focus', |
|||
title: '', |
|||
delay: 0, |
|||
html: false, |
|||
selector: false, |
|||
placement: 'top', |
|||
offset: '0 0', |
|||
constraints: [], |
|||
container: false |
|||
}; |
|||
|
|||
var DefaultType = { |
|||
animation: 'boolean', |
|||
template: 'string', |
|||
title: '(string|element|function)', |
|||
trigger: 'string', |
|||
delay: '(number|object)', |
|||
html: 'boolean', |
|||
selector: '(string|boolean)', |
|||
placement: '(string|function)', |
|||
offset: 'string', |
|||
constraints: 'array', |
|||
container: '(string|element|boolean)' |
|||
}; |
|||
|
|||
var AttachmentMap = { |
|||
TOP: 'bottom center', |
|||
RIGHT: 'middle left', |
|||
BOTTOM: 'top center', |
|||
LEFT: 'middle right' |
|||
}; |
|||
|
|||
var HoverState = { |
|||
SHOW: 'show', |
|||
OUT: 'out' |
|||
}; |
|||
|
|||
var 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 |
|||
}; |
|||
|
|||
var ClassName = { |
|||
FADE: 'fade', |
|||
SHOW: 'show' |
|||
}; |
|||
|
|||
var Selector = { |
|||
TOOLTIP: '.tooltip', |
|||
TOOLTIP_INNER: '.tooltip-inner' |
|||
}; |
|||
|
|||
var TetherClass = { |
|||
element: false, |
|||
enabled: false |
|||
}; |
|||
|
|||
var Trigger = { |
|||
HOVER: 'hover', |
|||
FOCUS: 'focus', |
|||
CLICK: 'click', |
|||
MANUAL: 'manual' |
|||
}; |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var Tooltip = function () { |
|||
function Tooltip(element, config) { |
|||
_classCallCheck(this, Tooltip); |
|||
|
|||
// private
|
|||
this._isEnabled = true; |
|||
this._timeout = 0; |
|||
this._hoverState = ''; |
|||
this._activeTrigger = {}; |
|||
this._isTransitioning = false; |
|||
this._tether = null; |
|||
|
|||
// protected
|
|||
this.element = element; |
|||
this.config = this._getConfig(config); |
|||
this.tip = null; |
|||
|
|||
this._setListeners(); |
|||
} |
|||
|
|||
// getters
|
|||
|
|||
// public
|
|||
|
|||
Tooltip.prototype.enable = function enable() { |
|||
this._isEnabled = true; |
|||
}; |
|||
|
|||
Tooltip.prototype.disable = function disable() { |
|||
this._isEnabled = false; |
|||
}; |
|||
|
|||
Tooltip.prototype.toggleEnabled = function toggleEnabled() { |
|||
this._isEnabled = !this._isEnabled; |
|||
}; |
|||
|
|||
Tooltip.prototype.toggle = function toggle(event) { |
|||
if (event) { |
|||
var dataKey = this.constructor.DATA_KEY; |
|||
var 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); |
|||
} |
|||
}; |
|||
|
|||
Tooltip.prototype.dispose = function dispose() { |
|||
clearTimeout(this._timeout); |
|||
|
|||
this.cleanupTether(); |
|||
|
|||
$.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; |
|||
this._tether = null; |
|||
|
|||
this.element = null; |
|||
this.config = null; |
|||
this.tip = null; |
|||
}; |
|||
|
|||
Tooltip.prototype.show = function show() { |
|||
var _this = this; |
|||
|
|||
if ($(this.element).css('display') === 'none') { |
|||
throw new Error('Please use show on visible elements'); |
|||
} |
|||
|
|||
var showEvent = $.Event(this.constructor.Event.SHOW); |
|||
if (this.isWithContent() && this._isEnabled) { |
|||
if (this._isTransitioning) { |
|||
throw new Error('Tooltip is transitioning'); |
|||
} |
|||
$(this.element).trigger(showEvent); |
|||
|
|||
var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element); |
|||
|
|||
if (showEvent.isDefaultPrevented() || !isInTheDom) { |
|||
return; |
|||
} |
|||
|
|||
var tip = this.getTipElement(); |
|||
var 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); |
|||
} |
|||
|
|||
var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement; |
|||
|
|||
var attachment = this._getAttachment(placement); |
|||
|
|||
var container = this.config.container === false ? document.body : $(this.config.container); |
|||
|
|||
$(tip).data(this.constructor.DATA_KEY, this).appendTo(container); |
|||
|
|||
$(this.element).trigger(this.constructor.Event.INSERTED); |
|||
|
|||
this._tether = new Tether({ |
|||
attachment: attachment, |
|||
element: tip, |
|||
target: this.element, |
|||
classes: TetherClass, |
|||
classPrefix: CLASS_PREFIX, |
|||
offset: this.config.offset, |
|||
constraints: this.config.constraints, |
|||
addTargetClasses: false |
|||
}); |
|||
|
|||
Util.reflow(tip); |
|||
this._tether.position(); |
|||
|
|||
$(tip).addClass(ClassName.SHOW); |
|||
|
|||
var complete = function complete() { |
|||
var prevHoverState = _this._hoverState; |
|||
_this._hoverState = null; |
|||
_this._isTransitioning = false; |
|||
|
|||
$(_this.element).trigger(_this.constructor.Event.SHOWN); |
|||
|
|||
if (prevHoverState === HoverState.OUT) { |
|||
_this._leave(null, _this); |
|||
} |
|||
}; |
|||
|
|||
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) { |
|||
this._isTransitioning = true; |
|||
$(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(Tooltip._TRANSITION_DURATION); |
|||
return; |
|||
} |
|||
|
|||
complete(); |
|||
} |
|||
}; |
|||
|
|||
Tooltip.prototype.hide = function hide(callback) { |
|||
var _this2 = this; |
|||
|
|||
var tip = this.getTipElement(); |
|||
var hideEvent = $.Event(this.constructor.Event.HIDE); |
|||
if (this._isTransitioning) { |
|||
throw new Error('Tooltip is transitioning'); |
|||
} |
|||
var complete = function complete() { |
|||
if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) { |
|||
tip.parentNode.removeChild(tip); |
|||
} |
|||
|
|||
_this2.element.removeAttribute('aria-describedby'); |
|||
$(_this2.element).trigger(_this2.constructor.Event.HIDDEN); |
|||
_this2._isTransitioning = false; |
|||
_this2.cleanupTether(); |
|||
|
|||
if (callback) { |
|||
callback(); |
|||
} |
|||
}; |
|||
|
|||
$(this.element).trigger(hideEvent); |
|||
|
|||
if (hideEvent.isDefaultPrevented()) { |
|||
return; |
|||
} |
|||
|
|||
$(tip).removeClass(ClassName.SHOW); |
|||
|
|||
this._activeTrigger[Trigger.CLICK] = false; |
|||
this._activeTrigger[Trigger.FOCUS] = false; |
|||
this._activeTrigger[Trigger.HOVER] = false; |
|||
|
|||
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) { |
|||
this._isTransitioning = true; |
|||
$(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION); |
|||
} else { |
|||
complete(); |
|||
} |
|||
|
|||
this._hoverState = ''; |
|||
}; |
|||
|
|||
// protected
|
|||
|
|||
Tooltip.prototype.isWithContent = function isWithContent() { |
|||
return Boolean(this.getTitle()); |
|||
}; |
|||
|
|||
Tooltip.prototype.getTipElement = function getTipElement() { |
|||
return this.tip = this.tip || $(this.config.template)[0]; |
|||
}; |
|||
|
|||
Tooltip.prototype.setContent = function setContent() { |
|||
var $tip = $(this.getTipElement()); |
|||
|
|||
this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle()); |
|||
|
|||
$tip.removeClass(ClassName.FADE + ' ' + ClassName.SHOW); |
|||
|
|||
this.cleanupTether(); |
|||
}; |
|||
|
|||
Tooltip.prototype.setElementContent = function setElementContent($element, content) { |
|||
var html = this.config.html; |
|||
if ((typeof content === 'undefined' ? 'undefined' : _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); |
|||
} |
|||
}; |
|||
|
|||
Tooltip.prototype.getTitle = function getTitle() { |
|||
var 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; |
|||
}; |
|||
|
|||
Tooltip.prototype.cleanupTether = function cleanupTether() { |
|||
if (this._tether) { |
|||
this._tether.destroy(); |
|||
} |
|||
}; |
|||
|
|||
// private
|
|||
|
|||
Tooltip.prototype._getAttachment = function _getAttachment(placement) { |
|||
return AttachmentMap[placement.toUpperCase()]; |
|||
}; |
|||
|
|||
Tooltip.prototype._setListeners = function _setListeners() { |
|||
var _this3 = this; |
|||
|
|||
var triggers = this.config.trigger.split(' '); |
|||
|
|||
triggers.forEach(function (trigger) { |
|||
if (trigger === 'click') { |
|||
$(_this3.element).on(_this3.constructor.Event.CLICK, _this3.config.selector, function (event) { |
|||
return _this3.toggle(event); |
|||
}); |
|||
} else if (trigger !== Trigger.MANUAL) { |
|||
var eventIn = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSEENTER : _this3.constructor.Event.FOCUSIN; |
|||
var eventOut = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSELEAVE : _this3.constructor.Event.FOCUSOUT; |
|||
|
|||
$(_this3.element).on(eventIn, _this3.config.selector, function (event) { |
|||
return _this3._enter(event); |
|||
}).on(eventOut, _this3.config.selector, function (event) { |
|||
return _this3._leave(event); |
|||
}); |
|||
} |
|||
|
|||
$(_this3.element).closest('.modal').on('hide.bs.modal', function () { |
|||
return _this3.hide(); |
|||
}); |
|||
}); |
|||
|
|||
if (this.config.selector) { |
|||
this.config = $.extend({}, this.config, { |
|||
trigger: 'manual', |
|||
selector: '' |
|||
}); |
|||
} else { |
|||
this._fixTitle(); |
|||
} |
|||
}; |
|||
|
|||
Tooltip.prototype._fixTitle = function _fixTitle() { |
|||
var 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', ''); |
|||
} |
|||
}; |
|||
|
|||
Tooltip.prototype._enter = function _enter(event, context) { |
|||
var 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(function () { |
|||
if (context._hoverState === HoverState.SHOW) { |
|||
context.show(); |
|||
} |
|||
}, context.config.delay.show); |
|||
}; |
|||
|
|||
Tooltip.prototype._leave = function _leave(event, context) { |
|||
var 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(function () { |
|||
if (context._hoverState === HoverState.OUT) { |
|||
context.hide(); |
|||
} |
|||
}, context.config.delay.hide); |
|||
}; |
|||
|
|||
Tooltip.prototype._isWithActiveTrigger = function _isWithActiveTrigger() { |
|||
for (var trigger in this._activeTrigger) { |
|||
if (this._activeTrigger[trigger]) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
}; |
|||
|
|||
Tooltip.prototype._getConfig = function _getConfig(config) { |
|||
config = $.extend({}, this.constructor.Default, $(this.element).data(), config); |
|||
|
|||
if (config.delay && typeof config.delay === 'number') { |
|||
config.delay = { |
|||
show: config.delay, |
|||
hide: config.delay |
|||
}; |
|||
} |
|||
|
|||
Util.typeCheckConfig(NAME, config, this.constructor.DefaultType); |
|||
|
|||
return config; |
|||
}; |
|||
|
|||
Tooltip.prototype._getDelegateConfig = function _getDelegateConfig() { |
|||
var config = {}; |
|||
|
|||
if (this.config) { |
|||
for (var key in this.config) { |
|||
if (this.constructor.Default[key] !== this.config[key]) { |
|||
config[key] = this.config[key]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return config; |
|||
}; |
|||
|
|||
// static
|
|||
|
|||
Tooltip._jQueryInterface = function _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
var data = $(this).data(DATA_KEY); |
|||
var _config = (typeof config === 'undefined' ? 'undefined' : _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 (data[config] === undefined) { |
|||
throw new Error('No method named "' + config + '"'); |
|||
} |
|||
data[config](); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
_createClass(Tooltip, null, [{ |
|||
key: 'VERSION', |
|||
get: function get() { |
|||
return VERSION; |
|||
} |
|||
}, { |
|||
key: 'Default', |
|||
get: function get() { |
|||
return Default; |
|||
} |
|||
}, { |
|||
key: 'NAME', |
|||
get: function get() { |
|||
return NAME; |
|||
} |
|||
}, { |
|||
key: 'DATA_KEY', |
|||
get: function get() { |
|||
return DATA_KEY; |
|||
} |
|||
}, { |
|||
key: 'Event', |
|||
get: function get() { |
|||
return Event; |
|||
} |
|||
}, { |
|||
key: 'EVENT_KEY', |
|||
get: function get() { |
|||
return EVENT_KEY; |
|||
} |
|||
}, { |
|||
key: 'DefaultType', |
|||
get: function get() { |
|||
return DefaultType; |
|||
} |
|||
}]); |
|||
|
|||
return Tooltip; |
|||
}(); |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* jQuery |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$.fn[NAME] = Tooltip._jQueryInterface; |
|||
$.fn[NAME].Constructor = Tooltip; |
|||
$.fn[NAME].noConflict = function () { |
|||
$.fn[NAME] = JQUERY_NO_CONFLICT; |
|||
return Tooltip._jQueryInterface; |
|||
}; |
|||
|
|||
return Tooltip; |
|||
}(jQuery); /* global Tether */ |
|||
//# sourceMappingURL=tooltip.js.map
|
@ -0,0 +1,150 @@ |
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): util.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Util = function ($) { |
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Private TransitionEnd Helpers |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
var transition = false; |
|||
|
|||
var MAX_UID = 1000000; |
|||
|
|||
var TransitionEndEvent = { |
|||
WebkitTransition: 'webkitTransitionEnd', |
|||
MozTransition: 'transitionend', |
|||
OTransition: 'oTransitionEnd otransitionend', |
|||
transition: 'transitionend' |
|||
}; |
|||
|
|||
// shoutout AngusCroll (https://goo.gl/pxwQGp)
|
|||
function toType(obj) { |
|||
return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); |
|||
} |
|||
|
|||
function isElement(obj) { |
|||
return (obj[0] || obj).nodeType; |
|||
} |
|||
|
|||
function getSpecialTransitionEndEvent() { |
|||
return { |
|||
bindType: transition.end, |
|||
delegateType: transition.end, |
|||
handle: function handle(event) { |
|||
if ($(event.target).is(this)) { |
|||
return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
|
|||
} |
|||
return undefined; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
function transitionEndTest() { |
|||
if (window.QUnit) { |
|||
return false; |
|||
} |
|||
|
|||
var el = document.createElement('bootstrap'); |
|||
|
|||
for (var name in TransitionEndEvent) { |
|||
if (el.style[name] !== undefined) { |
|||
return { |
|||
end: TransitionEndEvent[name] |
|||
}; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
function transitionEndEmulator(duration) { |
|||
var _this = this; |
|||
|
|||
var called = false; |
|||
|
|||
$(this).one(Util.TRANSITION_END, function () { |
|||
called = true; |
|||
}); |
|||
|
|||
setTimeout(function () { |
|||
if (!called) { |
|||
Util.triggerTransitionEnd(_this); |
|||
} |
|||
}, duration); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
function setTransitionEndSupport() { |
|||
transition = transitionEndTest(); |
|||
|
|||
$.fn.emulateTransitionEnd = transitionEndEmulator; |
|||
|
|||
if (Util.supportsTransitionEnd()) { |
|||
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Public Util Api |
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
var Util = { |
|||
|
|||
TRANSITION_END: 'bsTransitionEnd', |
|||
|
|||
getUID: function 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: function getSelectorFromElement(element) { |
|||
var selector = element.getAttribute('data-target'); |
|||
|
|||
if (!selector) { |
|||
selector = element.getAttribute('href') || ''; |
|||
selector = /^#[a-z]/i.test(selector) ? selector : null; |
|||
} |
|||
|
|||
return selector; |
|||
}, |
|||
reflow: function reflow(element) { |
|||
return element.offsetHeight; |
|||
}, |
|||
triggerTransitionEnd: function triggerTransitionEnd(element) { |
|||
$(element).trigger(transition.end); |
|||
}, |
|||
supportsTransitionEnd: function supportsTransitionEnd() { |
|||
return Boolean(transition); |
|||
}, |
|||
typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) { |
|||
for (var property in configTypes) { |
|||
if (configTypes.hasOwnProperty(property)) { |
|||
var expectedTypes = configTypes[property]; |
|||
var value = config[property]; |
|||
var valueType = value && 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; |
|||
}(jQuery); |
|||
//# sourceMappingURL=util.js.map
|
@ -0,0 +1,193 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): alert.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Alert = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'alert' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Alert |
@ -0,0 +1,176 @@ |
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): button.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Button = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'button' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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 |
|||
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) { |
|||
input.checked = !$(this._element).hasClass(ClassName.ACTIVE) |
|||
$(input).trigger('change') |
|||
} |
|||
|
|||
input.focus() |
|||
} |
|||
|
|||
} |
|||
|
|||
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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Button |
@ -0,0 +1,497 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): carousel.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Carousel = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'carousel' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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 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}`, |
|||
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._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) { |
|||
throw new Error('Carousel is sliding') |
|||
} |
|||
this._slide(Direction.NEXT) |
|||
} |
|||
|
|||
nextWhenVisible() { |
|||
// Don't call next when the page isn't visible
|
|||
if (!document.hidden) { |
|||
this.next() |
|||
} |
|||
} |
|||
|
|||
prev() { |
|||
if (this._isSliding) { |
|||
throw new Error('Carousel is sliding') |
|||
} |
|||
this._slide(Direction.PREVIOUS) |
|||
} |
|||
|
|||
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.PREVIOUS |
|||
|
|||
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 = $.extend({}, 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' && |
|||
!('ontouchstart' in document.documentElement)) { |
|||
$(this._element) |
|||
.on(Event.MOUSEENTER, (event) => this.pause(event)) |
|||
.on(Event.MOUSELEAVE, (event) => this.cycle(event)) |
|||
} |
|||
} |
|||
|
|||
_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: |
|||
return |
|||
} |
|||
} |
|||
|
|||
_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.PREVIOUS |
|||
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.PREVIOUS ? -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 slideEvent = $.Event(Event.SLIDE, { |
|||
relatedTarget, |
|||
direction: eventDirectionName |
|||
}) |
|||
|
|||
$(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 nextElement = element || activeElement && |
|||
this._getItemByDirection(direction, activeElement) |
|||
|
|||
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 |
|||
}) |
|||
|
|||
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) |
|||
const _config = $.extend({}, Default, $(this).data()) |
|||
|
|||
if (typeof config === 'object') { |
|||
$.extend(_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 (data[action] === undefined) { |
|||
throw new Error(`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 = $.extend({}, $(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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Carousel |
@ -0,0 +1,387 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): collapse.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Collapse = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'collapse' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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' |
|||
} |
|||
|
|||
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 : '.card > .show, .card > .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}"]` |
|||
)) |
|||
|
|||
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) { |
|||
throw new Error('Collapse is transitioning') |
|||
} |
|||
|
|||
if ($(this._element).hasClass(ClassName.SHOW)) { |
|||
return |
|||
} |
|||
|
|||
let actives |
|||
let activesData |
|||
|
|||
if (this._parent) { |
|||
actives = $.makeArray($(this._parent).find(Selector.ACTIVES)) |
|||
if (!actives.length) { |
|||
actives = null |
|||
} |
|||
} |
|||
|
|||
if (actives) { |
|||
activesData = $(actives).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), '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 |
|||
this._element.setAttribute('aria-expanded', true) |
|||
|
|||
if (this._triggerArray.length) { |
|||
$(this._triggerArray) |
|||
.removeClass(ClassName.COLLAPSED) |
|||
.attr('aria-expanded', true) |
|||
} |
|||
|
|||
this.setTransitioning(true) |
|||
|
|||
const complete = () => { |
|||
$(this._element) |
|||
.removeClass(ClassName.COLLAPSING) |
|||
.addClass(ClassName.COLLAPSE) |
|||
.addClass(ClassName.SHOW) |
|||
|
|||
this._element.style[dimension] = '' |
|||
|
|||
this.setTransitioning(false) |
|||
|
|||
$(this._element).trigger(Event.SHOWN) |
|||
} |
|||
|
|||
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) { |
|||
throw new Error('Collapse is transitioning') |
|||
} |
|||
|
|||
if (!$(this._element).hasClass(ClassName.SHOW)) { |
|||
return |
|||
} |
|||
|
|||
const startEvent = $.Event(Event.HIDE) |
|||
$(this._element).trigger(startEvent) |
|||
if (startEvent.isDefaultPrevented()) { |
|||
return |
|||
} |
|||
|
|||
const dimension = this._getDimension() |
|||
const offsetDimension = dimension === Dimension.WIDTH ? |
|||
'offsetWidth' : 'offsetHeight' |
|||
|
|||
this._element.style[dimension] = `${this._element[offsetDimension]}px` |
|||
|
|||
Util.reflow(this._element) |
|||
|
|||
$(this._element) |
|||
.addClass(ClassName.COLLAPSING) |
|||
.removeClass(ClassName.COLLAPSE) |
|||
.removeClass(ClassName.SHOW) |
|||
|
|||
this._element.setAttribute('aria-expanded', false) |
|||
|
|||
if (this._triggerArray.length) { |
|||
$(this._triggerArray) |
|||
.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 = $.extend({}, 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() { |
|||
const 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) |
|||
element.setAttribute('aria-expanded', isOpen) |
|||
|
|||
if (triggerArray.length) { |
|||
$(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 = $.extend( |
|||
{}, |
|||
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 (data[config] === undefined) { |
|||
throw new Error(`No method named "${config}"`) |
|||
} |
|||
data[config]() |
|||
} |
|||
}) |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Data Api implementation |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { |
|||
event.preventDefault() |
|||
|
|||
const target = Collapse._getTargetFromElement(this) |
|||
const data = $(target).data(DATA_KEY) |
|||
const config = data ? 'toggle' : $(this).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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Collapse |
@ -0,0 +1,303 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): dropdown.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Dropdown = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'dropdown' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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 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 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}`, |
|||
FOCUSIN_DATA_API : `focusin${EVENT_KEY}${DATA_API_KEY}`, |
|||
KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}` |
|||
} |
|||
|
|||
const ClassName = { |
|||
BACKDROP : 'dropdown-backdrop', |
|||
DISABLED : 'disabled', |
|||
SHOW : 'show' |
|||
} |
|||
|
|||
const Selector = { |
|||
BACKDROP : '.dropdown-backdrop', |
|||
DATA_TOGGLE : '[data-toggle="dropdown"]', |
|||
FORM_CHILD : '.dropdown form', |
|||
ROLE_MENU : '[role="menu"]', |
|||
ROLE_LISTBOX : '[role="listbox"]', |
|||
NAVBAR_NAV : '.navbar-nav', |
|||
VISIBLE_ITEMS : '[role="menu"] li:not(.disabled) a, ' |
|||
+ '[role="listbox"] li:not(.disabled) a' |
|||
} |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
class Dropdown { |
|||
|
|||
constructor(element) { |
|||
this._element = element |
|||
|
|||
this._addEventListeners() |
|||
} |
|||
|
|||
|
|||
// getters
|
|||
|
|||
static get VERSION() { |
|||
return VERSION |
|||
} |
|||
|
|||
|
|||
// public
|
|||
|
|||
toggle() { |
|||
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { |
|||
return false |
|||
} |
|||
|
|||
const parent = Dropdown._getParentFromElement(this) |
|||
const isActive = $(parent).hasClass(ClassName.SHOW) |
|||
|
|||
Dropdown._clearMenus() |
|||
|
|||
if (isActive) { |
|||
return false |
|||
} |
|||
|
|||
if ('ontouchstart' in document.documentElement && |
|||
!$(parent).closest(Selector.NAVBAR_NAV).length) { |
|||
|
|||
// if mobile we use a backdrop because click events don't delegate
|
|||
const dropdown = document.createElement('div') |
|||
dropdown.className = ClassName.BACKDROP |
|||
$(dropdown).insertBefore(this) |
|||
$(dropdown).on('click', Dropdown._clearMenus) |
|||
} |
|||
|
|||
const relatedTarget = { |
|||
relatedTarget : this |
|||
} |
|||
const showEvent = $.Event(Event.SHOW, relatedTarget) |
|||
|
|||
$(parent).trigger(showEvent) |
|||
|
|||
if (showEvent.isDefaultPrevented()) { |
|||
return false |
|||
} |
|||
|
|||
this.focus() |
|||
this.setAttribute('aria-expanded', true) |
|||
|
|||
$(parent).toggleClass(ClassName.SHOW) |
|||
$(parent).trigger($.Event(Event.SHOWN, relatedTarget)) |
|||
|
|||
return false |
|||
} |
|||
|
|||
dispose() { |
|||
$.removeData(this._element, DATA_KEY) |
|||
$(this._element).off(EVENT_KEY) |
|||
this._element = null |
|||
} |
|||
|
|||
|
|||
// private
|
|||
|
|||
_addEventListeners() { |
|||
$(this._element).on(Event.CLICK, this.toggle) |
|||
} |
|||
|
|||
|
|||
// static
|
|||
|
|||
static _jQueryInterface(config) { |
|||
return this.each(function () { |
|||
let data = $(this).data(DATA_KEY) |
|||
|
|||
if (!data) { |
|||
data = new Dropdown(this) |
|||
$(this).data(DATA_KEY, data) |
|||
} |
|||
|
|||
if (typeof config === 'string') { |
|||
if (data[config] === undefined) { |
|||
throw new Error(`No method named "${config}"`) |
|||
} |
|||
data[config].call(this) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
static _clearMenus(event) { |
|||
if (event && event.which === RIGHT_MOUSE_BUTTON_WHICH) { |
|||
return |
|||
} |
|||
|
|||
const backdrop = $(Selector.BACKDROP)[0] |
|||
if (backdrop) { |
|||
backdrop.parentNode.removeChild(backdrop) |
|||
} |
|||
|
|||
const toggles = $.makeArray($(Selector.DATA_TOGGLE)) |
|||
|
|||
for (let i = 0; i < toggles.length; i++) { |
|||
const parent = Dropdown._getParentFromElement(toggles[i]) |
|||
const relatedTarget = { |
|||
relatedTarget : toggles[i] |
|||
} |
|||
|
|||
if (!$(parent).hasClass(ClassName.SHOW)) { |
|||
continue |
|||
} |
|||
|
|||
if (event && (event.type === 'click' && |
|||
/input|textarea/i.test(event.target.tagName) || event.type === 'focusin') |
|||
&& $.contains(parent, event.target)) { |
|||
continue |
|||
} |
|||
|
|||
const hideEvent = $.Event(Event.HIDE, relatedTarget) |
|||
$(parent).trigger(hideEvent) |
|||
if (hideEvent.isDefaultPrevented()) { |
|||
continue |
|||
} |
|||
|
|||
toggles[i].setAttribute('aria-expanded', 'false') |
|||
|
|||
$(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 |
|||
} |
|||
|
|||
static _dataApiKeydownHandler(event) { |
|||
if (!/(38|40|27|32)/.test(event.which) || |
|||
/input|textarea/i.test(event.target.tagName)) { |
|||
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 || |
|||
isActive && event.which === ESCAPE_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) { |
|||
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.ROLE_MENU, Dropdown._dataApiKeydownHandler) |
|||
.on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler) |
|||
.on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus) |
|||
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Dropdown |
@ -0,0 +1,545 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): modal.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Modal = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'modal' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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' |
|||
} |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* 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._isTransitioning = 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) { |
|||
throw new Error('Modal is transitioning') |
|||
} |
|||
|
|||
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() |
|||
|
|||
$(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) { |
|||
throw new Error('Modal is transitioning') |
|||
} |
|||
|
|||
const transition = Util.supportsTransitionEnd() && |
|||
$(this._element).hasClass(ClassName.FADE) |
|||
if (transition) { |
|||
this._isTransitioning = true |
|||
} |
|||
|
|||
const hideEvent = $.Event(Event.HIDE) |
|||
$(this._element).trigger(hideEvent) |
|||
|
|||
if (!this._isShown || hideEvent.isDefaultPrevented()) { |
|||
return |
|||
} |
|||
|
|||
this._isShown = false |
|||
|
|||
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._originalBodyPadding = null |
|||
this._scrollbarWidth = null |
|||
} |
|||
|
|||
|
|||
// private
|
|||
|
|||
_getConfig(config) { |
|||
config = $.extend({}, 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 modals 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) { |
|||
this._element.focus() |
|||
} |
|||
}) |
|||
} |
|||
|
|||
_setEscapeEvent() { |
|||
if (this._isShown && this._config.keyboard) { |
|||
$(this._element).on(Event.KEYDOWN_DISMISS, (event) => { |
|||
if (event.which === ESCAPE_KEYCODE) { |
|||
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
|
|||
// ----------------------------------------------------------------------
|
|||
|
|||
_handleUpdate() { |
|||
this._adjustDialog() |
|||
} |
|||
|
|||
_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() { |
|||
this._isBodyOverflowing = document.body.clientWidth < window.innerWidth |
|||
this._scrollbarWidth = this._getScrollbarWidth() |
|||
} |
|||
|
|||
_setScrollbar() { |
|||
const bodyPadding = parseInt( |
|||
$(Selector.FIXED_CONTENT).css('padding-right') || 0, |
|||
10 |
|||
) |
|||
|
|||
this._originalBodyPadding = document.body.style.paddingRight || '' |
|||
|
|||
if (this._isBodyOverflowing) { |
|||
document.body.style.paddingRight = |
|||
`${bodyPadding + this._scrollbarWidth}px` |
|||
} |
|||
} |
|||
|
|||
_resetScrollbar() { |
|||
document.body.style.paddingRight = this._originalBodyPadding |
|||
} |
|||
|
|||
_getScrollbarWidth() { // thx d.walsh
|
|||
const scrollDiv = document.createElement('div') |
|||
scrollDiv.className = ClassName.SCROLLBAR_MEASURER |
|||
document.body.appendChild(scrollDiv) |
|||
const scrollbarWidth = scrollDiv.offsetWidth - 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 = $.extend( |
|||
{}, |
|||
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 (data[config] === undefined) { |
|||
throw new Error(`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' : $.extend({}, $(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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Modal |
@ -0,0 +1,179 @@ |
|||
import Tooltip from './tooltip' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): popover.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Popover = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'popover' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
const DATA_KEY = 'bs.popover' |
|||
const EVENT_KEY = `.${DATA_KEY}` |
|||
const JQUERY_NO_CONFLICT = $.fn[NAME] |
|||
|
|||
const Default = $.extend({}, Tooltip.Default, { |
|||
placement : 'right', |
|||
trigger : 'click', |
|||
content : '', |
|||
template : '<div class="popover" role="tooltip">' |
|||
+ '<h3 class="popover-title"></h3>' |
|||
+ '<div class="popover-content"></div></div>' |
|||
}) |
|||
|
|||
const DefaultType = $.extend({}, Tooltip.DefaultType, { |
|||
content : '(string|element|function)' |
|||
}) |
|||
|
|||
const ClassName = { |
|||
FADE : 'fade', |
|||
SHOW : 'show' |
|||
} |
|||
|
|||
const Selector = { |
|||
TITLE : '.popover-title', |
|||
CONTENT : '.popover-content' |
|||
} |
|||
|
|||
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() |
|||
} |
|||
|
|||
getTipElement() { |
|||
return this.tip = this.tip || $(this.config.template)[0] |
|||
} |
|||
|
|||
setContent() { |
|||
const $tip = $(this.getTipElement()) |
|||
|
|||
// we use append for html objects to maintain js events
|
|||
this.setElementContent($tip.find(Selector.TITLE), this.getTitle()) |
|||
this.setElementContent($tip.find(Selector.CONTENT), this._getContent()) |
|||
|
|||
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) |
|||
|
|||
this.cleanupTether() |
|||
} |
|||
|
|||
// private
|
|||
|
|||
_getContent() { |
|||
return this.element.getAttribute('data-content') |
|||
|| (typeof this.config.content === 'function' ? |
|||
this.config.content.call(this.element) : |
|||
this.config.content) |
|||
} |
|||
|
|||
|
|||
// 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 (data[config] === undefined) { |
|||
throw new Error(`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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Popover |
@ -0,0 +1,332 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): scrollspy.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const ScrollSpy = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'scrollspy' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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', |
|||
NAV_LINK : 'nav-link', |
|||
NAV : 'nav', |
|||
ACTIVE : 'active' |
|||
} |
|||
|
|||
const Selector = { |
|||
DATA_SPY : '[data-spy="scroll"]', |
|||
ACTIVE : '.active', |
|||
LIST_ITEM : '.list-item', |
|||
LI : 'li', |
|||
LI_DROPDOWN : 'li.dropdown', |
|||
NAV_LINKS : '.nav-link', |
|||
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.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.POSITION : OffsetMethod.OFFSET |
|||
|
|||
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 && (target.offsetWidth || target.offsetHeight)) { |
|||
// 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 = $.extend({}, 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.offsetHeight |
|||
} |
|||
|
|||
_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] |
|||
&& (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(',') |
|||
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 { |
|||
// todo (fat) this is kinda sus...
|
|||
// recursively add actives to tested nav-links
|
|||
$link.parents(Selector.LI).find(`> ${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 (data[config] === undefined) { |
|||
throw new Error(`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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default ScrollSpy |
@ -0,0 +1,278 @@ |
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): tab.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Tab = (($) => { |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'tab' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
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 = { |
|||
A : 'a', |
|||
LI : 'li', |
|||
DROPDOWN : '.dropdown', |
|||
LIST : 'ul:not(.dropdown-menu), ol:not(.dropdown-menu), nav:not(.dropdown-menu)', |
|||
FADE_CHILD : '> .nav-item .fade, > .fade', |
|||
ACTIVE : '.active', |
|||
ACTIVE_CHILD : '> .nav-item > .active, > .active', |
|||
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"]', |
|||
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.LIST)[0] |
|||
const selector = Util.getSelectorFromElement(this._element) |
|||
|
|||
if (listElement) { |
|||
previous = $.makeArray($(listElement).find(Selector.ACTIVE)) |
|||
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() { |
|||
$.removeClass(this._element, DATA_KEY) |
|||
this._element = null |
|||
} |
|||
|
|||
|
|||
// private
|
|||
|
|||
_activate(element, container, callback) { |
|||
const active = $(container).find(Selector.ACTIVE_CHILD)[0] |
|||
const isTransitioning = callback |
|||
&& Util.supportsTransitionEnd() |
|||
&& (active && $(active).hasClass(ClassName.FADE) |
|||
|| Boolean($(container).find(Selector.FADE_CHILD)[0])) |
|||
|
|||
const complete = () => this._transitionComplete( |
|||
element, |
|||
active, |
|||
isTransitioning, |
|||
callback |
|||
) |
|||
|
|||
if (active && isTransitioning) { |
|||
$(active) |
|||
.one(Util.TRANSITION_END, complete) |
|||
.emulateTransitionEnd(TRANSITION_DURATION) |
|||
|
|||
} else { |
|||
complete() |
|||
} |
|||
|
|||
if (active) { |
|||
$(active).removeClass(ClassName.SHOW) |
|||
} |
|||
} |
|||
|
|||
_transitionComplete(element, active, isTransitioning, callback) { |
|||
if (active) { |
|||
$(active).removeClass(ClassName.ACTIVE) |
|||
|
|||
const dropdownChild = $(active.parentNode).find( |
|||
Selector.DROPDOWN_ACTIVE_CHILD |
|||
)[0] |
|||
|
|||
if (dropdownChild) { |
|||
$(dropdownChild).removeClass(ClassName.ACTIVE) |
|||
} |
|||
|
|||
active.setAttribute('aria-expanded', false) |
|||
} |
|||
|
|||
$(element).addClass(ClassName.ACTIVE) |
|||
element.setAttribute('aria-expanded', true) |
|||
|
|||
if (isTransitioning) { |
|||
Util.reflow(element) |
|||
$(element).addClass(ClassName.SHOW) |
|||
} else { |
|||
$(element).removeClass(ClassName.FADE) |
|||
} |
|||
|
|||
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 (data[config] === undefined) { |
|||
throw new Error(`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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Tab |
@ -0,0 +1,675 @@ |
|||
/* global Tether */ |
|||
|
|||
import Util from './util' |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): tooltip.js |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
* -------------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
const Tooltip = (($) => { |
|||
|
|||
/** |
|||
* Check for Tether dependency |
|||
* Tether - http://tether.io/
|
|||
*/ |
|||
if (typeof Tether === 'undefined') { |
|||
throw new Error('Bootstrap tooltips require Tether (http://tether.io/)') |
|||
} |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Constants |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
const NAME = 'tooltip' |
|||
const VERSION = '4.0.0-alpha.6' |
|||
const DATA_KEY = 'bs.tooltip' |
|||
const EVENT_KEY = `.${DATA_KEY}` |
|||
const JQUERY_NO_CONFLICT = $.fn[NAME] |
|||
const TRANSITION_DURATION = 150 |
|||
const CLASS_PREFIX = 'bs-tether' |
|||
|
|||
const Default = { |
|||
animation : true, |
|||
template : '<div class="tooltip" role="tooltip">' |
|||
+ '<div class="tooltip-inner"></div></div>', |
|||
trigger : 'hover focus', |
|||
title : '', |
|||
delay : 0, |
|||
html : false, |
|||
selector : false, |
|||
placement : 'top', |
|||
offset : '0 0', |
|||
constraints : [], |
|||
container : false |
|||
} |
|||
|
|||
const DefaultType = { |
|||
animation : 'boolean', |
|||
template : 'string', |
|||
title : '(string|element|function)', |
|||
trigger : 'string', |
|||
delay : '(number|object)', |
|||
html : 'boolean', |
|||
selector : '(string|boolean)', |
|||
placement : '(string|function)', |
|||
offset : 'string', |
|||
constraints : 'array', |
|||
container : '(string|element|boolean)' |
|||
} |
|||
|
|||
const AttachmentMap = { |
|||
TOP : 'bottom center', |
|||
RIGHT : 'middle left', |
|||
BOTTOM : 'top center', |
|||
LEFT : 'middle right' |
|||
} |
|||
|
|||
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' |
|||
} |
|||
|
|||
const TetherClass = { |
|||
element : false, |
|||
enabled : false |
|||
} |
|||
|
|||
const Trigger = { |
|||
HOVER : 'hover', |
|||
FOCUS : 'focus', |
|||
CLICK : 'click', |
|||
MANUAL : 'manual' |
|||
} |
|||
|
|||
|
|||
/** |
|||
* ------------------------------------------------------------------------ |
|||
* Class Definition |
|||
* ------------------------------------------------------------------------ |
|||
*/ |
|||
|
|||
class Tooltip { |
|||
|
|||
constructor(element, config) { |
|||
|
|||
// private
|
|||
this._isEnabled = true |
|||
this._timeout = 0 |
|||
this._hoverState = '' |
|||
this._activeTrigger = {} |
|||
this._isTransitioning = false |
|||
this._tether = 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 (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) |
|||
|
|||
this.cleanupTether() |
|||
|
|||
$.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 |
|||
this._tether = 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) { |
|||
if (this._isTransitioning) { |
|||
throw new Error('Tooltip is transitioning') |
|||
} |
|||
$(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) |
|||
|
|||
const container = this.config.container === false ? document.body : $(this.config.container) |
|||
|
|||
$(tip) |
|||
.data(this.constructor.DATA_KEY, this) |
|||
.appendTo(container) |
|||
|
|||
$(this.element).trigger(this.constructor.Event.INSERTED) |
|||
|
|||
this._tether = new Tether({ |
|||
attachment, |
|||
element : tip, |
|||
target : this.element, |
|||
classes : TetherClass, |
|||
classPrefix : CLASS_PREFIX, |
|||
offset : this.config.offset, |
|||
constraints : this.config.constraints, |
|||
addTargetClasses: false |
|||
}) |
|||
|
|||
Util.reflow(tip) |
|||
this._tether.position() |
|||
|
|||
$(tip).addClass(ClassName.SHOW) |
|||
|
|||
const complete = () => { |
|||
const prevHoverState = this._hoverState |
|||
this._hoverState = null |
|||
this._isTransitioning = false |
|||
|
|||
$(this.element).trigger(this.constructor.Event.SHOWN) |
|||
|
|||
if (prevHoverState === HoverState.OUT) { |
|||
this._leave(null, this) |
|||
} |
|||
} |
|||
|
|||
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) { |
|||
this._isTransitioning = true |
|||
$(this.tip) |
|||
.one(Util.TRANSITION_END, complete) |
|||
.emulateTransitionEnd(Tooltip._TRANSITION_DURATION) |
|||
return |
|||
} |
|||
|
|||
complete() |
|||
} |
|||
} |
|||
|
|||
hide(callback) { |
|||
const tip = this.getTipElement() |
|||
const hideEvent = $.Event(this.constructor.Event.HIDE) |
|||
if (this._isTransitioning) { |
|||
throw new Error('Tooltip is transitioning') |
|||
} |
|||
const complete = () => { |
|||
if (this._hoverState !== HoverState.SHOW && tip.parentNode) { |
|||
tip.parentNode.removeChild(tip) |
|||
} |
|||
|
|||
this.element.removeAttribute('aria-describedby') |
|||
$(this.element).trigger(this.constructor.Event.HIDDEN) |
|||
this._isTransitioning = false |
|||
this.cleanupTether() |
|||
|
|||
if (callback) { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
$(this.element).trigger(hideEvent) |
|||
|
|||
if (hideEvent.isDefaultPrevented()) { |
|||
return |
|||
} |
|||
|
|||
$(tip).removeClass(ClassName.SHOW) |
|||
|
|||
this._activeTrigger[Trigger.CLICK] = false |
|||
this._activeTrigger[Trigger.FOCUS] = false |
|||
this._activeTrigger[Trigger.HOVER] = false |
|||
|
|||
if (Util.supportsTransitionEnd() && |
|||
$(this.tip).hasClass(ClassName.FADE)) { |
|||
this._isTransitioning = true |
|||
$(tip) |
|||
.one(Util.TRANSITION_END, complete) |
|||
.emulateTransitionEnd(TRANSITION_DURATION) |
|||
|
|||
} else { |
|||
complete() |
|||
} |
|||
|
|||
this._hoverState = '' |
|||
} |
|||
|
|||
|
|||
// protected
|
|||
|
|||
isWithContent() { |
|||
return Boolean(this.getTitle()) |
|||
} |
|||
|
|||
getTipElement() { |
|||
return this.tip = this.tip || $(this.config.template)[0] |
|||
} |
|||
|
|||
setContent() { |
|||
const $tip = $(this.getTipElement()) |
|||
|
|||
this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle()) |
|||
|
|||
$tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) |
|||
|
|||
this.cleanupTether() |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
cleanupTether() { |
|||
if (this._tether) { |
|||
this._tether.destroy() |
|||
} |
|||
} |
|||
|
|||
|
|||
// 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 = $.extend({}, 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 = $.extend( |
|||
{}, |
|||
this.constructor.Default, |
|||
$(this.element).data(), |
|||
config |
|||
) |
|||
|
|||
if (config.delay && typeof config.delay === 'number') { |
|||
config.delay = { |
|||
show : config.delay, |
|||
hide : config.delay |
|||
} |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
|
|||
// 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 (data[config] === undefined) { |
|||
throw new Error(`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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Tooltip |
@ -0,0 +1,161 @@ |
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* Bootstrap (v4.0.0-alpha.6): 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 |
|||
|
|||
const TransitionEndEvent = { |
|||
WebkitTransition : 'webkitTransitionEnd', |
|||
MozTransition : 'transitionend', |
|||
OTransition : 'oTransitionEnd otransitionend', |
|||
transition : 'transitionend' |
|||
} |
|||
|
|||
// shoutout AngusCroll (https://goo.gl/pxwQGp)
|
|||
function toType(obj) { |
|||
return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() |
|||
} |
|||
|
|||
function isElement(obj) { |
|||
return (obj[0] || obj).nodeType |
|||
} |
|||
|
|||
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 |
|||
} |
|||
} |
|||
} |
|||
|
|||
function transitionEndTest() { |
|||
if (window.QUnit) { |
|||
return false |
|||
} |
|||
|
|||
const el = document.createElement('bootstrap') |
|||
|
|||
for (const name in TransitionEndEvent) { |
|||
if (el.style[name] !== undefined) { |
|||
return { |
|||
end: TransitionEndEvent[name] |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
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() |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* -------------------------------------------------------------------------- |
|||
* 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 = element.getAttribute('href') || '' |
|||
selector = /^#[a-z]/i.test(selector) ? selector : null |
|||
} |
|||
|
|||
return selector |
|||
}, |
|||
|
|||
reflow(element) { |
|||
return element.offsetHeight |
|||
}, |
|||
|
|||
triggerTransitionEnd(element) { |
|||
$(element).trigger(transition.end) |
|||
}, |
|||
|
|||
supportsTransitionEnd() { |
|||
return Boolean(transition) |
|||
}, |
|||
|
|||
typeCheckConfig(componentName, config, configTypes) { |
|||
for (const property in configTypes) { |
|||
if (configTypes.hasOwnProperty(property)) { |
|||
const expectedTypes = configTypes[property] |
|||
const value = config[property] |
|||
const valueType = value && 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 |
|||
|
|||
})(jQuery) |
|||
|
|||
export default Util |
@ -0,0 +1,61 @@ |
|||
## How does Bootstrap's test suite work? |
|||
|
|||
Bootstrap uses [QUnit](https://qunitjs.com/), a powerful, easy-to-use JavaScript unit test framework. Each plugin has a file dedicated to its tests in `unit/<plugin-name>.js`. |
|||
|
|||
* `unit/` contains the unit test files for each Bootstrap plugin. |
|||
* `vendor/` contains third-party testing-related code (QUnit and jQuery). |
|||
* `visual/` contains "visual" tests which are run interactively in real browsers and require manual verification by humans. |
|||
|
|||
To run the unit test suite via [PhantomJS](http://phantomjs.org/), run `grunt test-js`. |
|||
|
|||
To run the unit test suite via a real web browser, open `index.html` in the browser. |
|||
|
|||
|
|||
## How do I add a new unit test? |
|||
|
|||
1. Locate and open the file dedicated to the plugin which you need to add tests to (`unit/<plugin-name>.js`). |
|||
2. Review the [QUnit API Documentation](https://api.qunitjs.com/) and use the existing tests as references for how to structure your new tests. |
|||
3. Write the necessary unit test(s) for the new or revised functionality. |
|||
4. Run `grunt test-js` to see the results of your newly-added test(s). |
|||
|
|||
**Note:** Your new unit tests should fail before your changes are applied to the plugin, and should pass after your changes are applied to the plugin. |
|||
|
|||
## What should a unit test look like? |
|||
|
|||
* Each test should have a unique name clearly stating what unit is being tested. |
|||
* Each test should test only one unit per test, although one test can include several assertions. Create multiple tests for multiple units of functionality. |
|||
* Each test should begin with [`assert.expect`](https://api.qunitjs.com/expect/) to ensure that the expected assertions are run. |
|||
* Each test should follow the project's [JavaScript Code Guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#js) |
|||
|
|||
### Example tests |
|||
|
|||
```javascript |
|||
// Synchronous test |
|||
QUnit.test('should describe the unit being tested', function (assert) { |
|||
assert.expect(1) |
|||
var templateHTML = '<div class="alert alert-danger fade in">' |
|||
+ '<a class="close" href="#" data-dismiss="alert">×</a>' |
|||
+ '<p><strong>Template necessary for the test.</p>' |
|||
+ '</div>' |
|||
var $alert = $(templateHTML).appendTo('#qunit-fixture').bootstrapAlert() |
|||
|
|||
$alert.find('.close').click() |
|||
|
|||
// Make assertion |
|||
assert.strictEqual($alert.hasClass('in'), false, 'remove .in class on .close click') |
|||
}) |
|||
|
|||
// Asynchronous test |
|||
QUnit.test('should describe the unit being tested', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.tooltip', function () { |
|||
assert.ok(true, '"shown" event was fired after calling "show"') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
``` |
@ -0,0 +1,79 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('alert plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).alert, 'alert method is defined') |
|||
}) |
|||
|
|||
QUnit.module('alert', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapAlert = $.fn.alert.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.alert = $.fn.bootstrapAlert |
|||
delete $.fn.bootstrapAlert |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.alert, undefined, 'alert was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $alert = $el.bootstrapAlert() |
|||
assert.ok($alert instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($alert[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should fade element out on clicking .close', function (assert) { |
|||
assert.expect(1) |
|||
var alertHTML = '<div class="alert alert-danger fade show">' |
|||
+ '<a class="close" href="#" data-dismiss="alert">×</a>' |
|||
+ '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>' |
|||
+ '</div>' |
|||
|
|||
var $alert = $(alertHTML).bootstrapAlert().appendTo($('#qunit-fixture')) |
|||
|
|||
$alert.find('.close').trigger('click') |
|||
|
|||
assert.strictEqual($alert.hasClass('show'), false, 'remove .show class on .close click') |
|||
}) |
|||
|
|||
QUnit.test('should remove element when clicking .close', function (assert) { |
|||
assert.expect(2) |
|||
var alertHTML = '<div class="alert alert-danger fade show">' |
|||
+ '<a class="close" href="#" data-dismiss="alert">×</a>' |
|||
+ '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>' |
|||
+ '</div>' |
|||
var $alert = $(alertHTML).appendTo('#qunit-fixture').bootstrapAlert() |
|||
|
|||
assert.notEqual($('#qunit-fixture').find('.alert').length, 0, 'element added to dom') |
|||
|
|||
$alert.find('.close').trigger('click') |
|||
|
|||
assert.strictEqual($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom') |
|||
}) |
|||
|
|||
QUnit.test('should not fire closed when close is prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
$('<div class="alert"/>') |
|||
.on('close.bs.alert', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'close event fired') |
|||
done() |
|||
}) |
|||
.on('closed.bs.alert', function () { |
|||
assert.ok(false, 'closed event fired') |
|||
}) |
|||
.bootstrapAlert('close') |
|||
}) |
|||
|
|||
}) |
@ -0,0 +1,141 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('button plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).button, 'button method is defined') |
|||
}) |
|||
|
|||
QUnit.module('button', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapButton = $.fn.button.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.button = $.fn.bootstrapButton |
|||
delete $.fn.bootstrapButton |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.button, undefined, 'button was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $button = $el.bootstrapButton() |
|||
assert.ok($button instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($button[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should toggle active', function (assert) { |
|||
assert.expect(2) |
|||
var $btn = $('<button class="btn" data-toggle="button">mdo</button>') |
|||
assert.ok(!$btn.hasClass('active'), 'btn does not have active class') |
|||
$btn.bootstrapButton('toggle') |
|||
assert.ok($btn.hasClass('active'), 'btn has class active') |
|||
}) |
|||
|
|||
QUnit.test('should toggle active when btn children are clicked', function (assert) { |
|||
assert.expect(2) |
|||
var $btn = $('<button class="btn" data-toggle="button">mdo</button>') |
|||
var $inner = $('<i/>') |
|||
$btn |
|||
.append($inner) |
|||
.appendTo('#qunit-fixture') |
|||
assert.ok(!$btn.hasClass('active'), 'btn does not have active class') |
|||
$inner.trigger('click') |
|||
assert.ok($btn.hasClass('active'), 'btn has class active') |
|||
}) |
|||
|
|||
QUnit.test('should toggle aria-pressed', function (assert) { |
|||
assert.expect(2) |
|||
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>') |
|||
assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false') |
|||
$btn.bootstrapButton('toggle') |
|||
assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true') |
|||
}) |
|||
|
|||
QUnit.test('should toggle aria-pressed on buttons with container', function (assert) { |
|||
assert.expect(1) |
|||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' + |
|||
'<button id="btn1" class="btn btn-secondary" type="button">One</button>' + |
|||
'<button class="btn btn-secondary" type="button">Two</button>' + |
|||
'</div>' |
|||
$('#qunit-fixture').append(groupHTML) |
|||
$('#btn1').bootstrapButton('toggle') |
|||
assert.strictEqual($('#btn1').attr('aria-pressed'), 'true') |
|||
}) |
|||
|
|||
QUnit.test('should toggle aria-pressed when btn children are clicked', function (assert) { |
|||
assert.expect(2) |
|||
var $btn = $('<button class="btn" data-toggle="button" aria-pressed="false">redux</button>') |
|||
var $inner = $('<i/>') |
|||
$btn |
|||
.append($inner) |
|||
.appendTo('#qunit-fixture') |
|||
assert.strictEqual($btn.attr('aria-pressed'), 'false', 'btn aria-pressed state is false') |
|||
$inner.trigger('click') |
|||
assert.strictEqual($btn.attr('aria-pressed'), 'true', 'btn aria-pressed state is true') |
|||
}) |
|||
|
|||
QUnit.test('should trigger input change event when toggled button has input field', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' |
|||
+ '<label class="btn btn-primary">' |
|||
+ '<input type="radio" id="radio" autocomplete="off">Radio' |
|||
+ '</label>' |
|||
+ '</div>' |
|||
var $group = $(groupHTML).appendTo('#qunit-fixture') |
|||
|
|||
$group.find('input').on('change', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'change event fired') |
|||
done() |
|||
}) |
|||
|
|||
$group.find('label').trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should check for closest matching toggle', function (assert) { |
|||
assert.expect(12) |
|||
var groupHTML = '<div class="btn-group" data-toggle="buttons">' |
|||
+ '<label class="btn btn-primary active">' |
|||
+ '<input type="radio" name="options" id="option1" checked="true"> Option 1' |
|||
+ '</label>' |
|||
+ '<label class="btn btn-primary">' |
|||
+ '<input type="radio" name="options" id="option2"> Option 2' |
|||
+ '</label>' |
|||
+ '<label class="btn btn-primary">' |
|||
+ '<input type="radio" name="options" id="option3"> Option 3' |
|||
+ '</label>' |
|||
+ '</div>' |
|||
var $group = $(groupHTML).appendTo('#qunit-fixture') |
|||
|
|||
var $btn1 = $group.children().eq(0) |
|||
var $btn2 = $group.children().eq(1) |
|||
|
|||
assert.ok($btn1.hasClass('active'), 'btn1 has active class') |
|||
assert.ok($btn1.find('input').prop('checked'), 'btn1 is checked') |
|||
assert.ok(!$btn2.hasClass('active'), 'btn2 does not have active class') |
|||
assert.ok(!$btn2.find('input').prop('checked'), 'btn2 is not checked') |
|||
$btn2.find('input').trigger('click') |
|||
assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class') |
|||
assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked') |
|||
assert.ok($btn2.hasClass('active'), 'btn2 has active class') |
|||
assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked') |
|||
|
|||
$btn2.find('input').trigger('click') // clicking an already checked radio should not un-check it
|
|||
assert.ok(!$btn1.hasClass('active'), 'btn1 does not have active class') |
|||
assert.ok(!$btn1.find('input').prop('checked'), 'btn1 is not checked') |
|||
assert.ok($btn2.hasClass('active'), 'btn2 has active class') |
|||
assert.ok($btn2.find('input').prop('checked'), 'btn2 is checked') |
|||
}) |
|||
|
|||
}) |
@ -0,0 +1,825 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('carousel plugin') |
|||
|
|||
QUnit.test('should be defined on jQuery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).carousel, 'carousel method is defined') |
|||
}) |
|||
|
|||
QUnit.module('carousel', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapCarousel = $.fn.carousel.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.carousel = $.fn.bootstrapCarousel |
|||
delete $.fn.bootstrapCarousel |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.carousel, undefined, 'carousel was set back to undefined (orig value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapCarousel() |
|||
try { |
|||
$el.bootstrapCarousel('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $carousel = $el.bootstrapCarousel() |
|||
assert.ok($carousel instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($carousel[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should type check config options', function (assert) { |
|||
assert.expect(2) |
|||
|
|||
var message |
|||
var expectedMessage = 'CAROUSEL: Option "interval" provided type "string" but expected type "(number|boolean)".' |
|||
var config = { |
|||
interval: 'fat sux' |
|||
} |
|||
|
|||
try { |
|||
$('<div/>').bootstrapCarousel(config) |
|||
} catch (e) { |
|||
message = e.message |
|||
} |
|||
|
|||
assert.ok(message === expectedMessage, 'correct error message') |
|||
|
|||
config = { |
|||
keyboard: document.createElement('div') |
|||
} |
|||
expectedMessage = 'CAROUSEL: Option "keyboard" provided type "element" but expected type "boolean".' |
|||
|
|||
try { |
|||
$('<div/>').bootstrapCarousel(config) |
|||
} catch (e) { |
|||
message = e.message |
|||
} |
|||
|
|||
assert.ok(message === expectedMessage, 'correct error message') |
|||
}) |
|||
|
|||
|
|||
QUnit.test('should not fire slid when slide is prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
$('<div class="carousel"/>') |
|||
.on('slide.bs.carousel', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'slide event fired') |
|||
done() |
|||
}) |
|||
.on('slid.bs.carousel', function () { |
|||
assert.ok(false, 'slid event fired') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should reset when slide is prevented', function (assert) { |
|||
assert.expect(6) |
|||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">' |
|||
+ '<ol class="carousel-indicators">' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="1"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>' |
|||
+ '</ol>' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' |
|||
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
|
|||
var done = assert.async() |
|||
$carousel |
|||
.one('slide.bs.carousel', function (e) { |
|||
e.preventDefault() |
|||
setTimeout(function () { |
|||
assert.ok($carousel.find('.carousel-item:eq(0)').is('.active'), 'first item still active') |
|||
assert.ok($carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active') |
|||
$carousel.bootstrapCarousel('next') |
|||
}, 0) |
|||
}) |
|||
.one('slid.bs.carousel', function () { |
|||
setTimeout(function () { |
|||
assert.ok(!$carousel.find('.carousel-item:eq(0)').is('.active'), 'first item still active') |
|||
assert.ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active') |
|||
assert.ok($carousel.find('.carousel-item:eq(1)').is('.active'), 'second item active') |
|||
assert.ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active') |
|||
done() |
|||
}, 0) |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should fire slide event with direction', function (assert) { |
|||
assert.expect(4) |
|||
var carouselHTML = '<div id="myCarousel" class="carousel slide">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>First Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Second Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Third Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' |
|||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
|
|||
var done = assert.async() |
|||
|
|||
$carousel |
|||
.one('slide.bs.carousel', function (e) { |
|||
assert.ok(e.direction, 'direction present on next') |
|||
assert.strictEqual(e.direction, 'left', 'direction is left on next') |
|||
|
|||
$carousel |
|||
.one('slide.bs.carousel', function (e) { |
|||
assert.ok(e.direction, 'direction present on prev') |
|||
assert.strictEqual(e.direction, 'right', 'direction is right on prev') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('prev') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should fire slid event with direction', function (assert) { |
|||
assert.expect(4) |
|||
var carouselHTML = '<div id="myCarousel" class="carousel slide">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>First Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Second Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Third Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' |
|||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
|
|||
var done = assert.async() |
|||
|
|||
$carousel |
|||
.one('slid.bs.carousel', function (e) { |
|||
assert.ok(e.direction, 'direction present on next') |
|||
assert.strictEqual(e.direction, 'left', 'direction is left on next') |
|||
|
|||
$carousel |
|||
.one('slid.bs.carousel', function (e) { |
|||
assert.ok(e.direction, 'direction present on prev') |
|||
assert.strictEqual(e.direction, 'right', 'direction is right on prev') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('prev') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should fire slide event with relatedTarget', function (assert) { |
|||
assert.expect(2) |
|||
var template = '<div id="myCarousel" class="carousel slide">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>First Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Second Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Third Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' |
|||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' |
|||
+ '</div>' |
|||
|
|||
var done = assert.async() |
|||
|
|||
$(template) |
|||
.on('slide.bs.carousel', function (e) { |
|||
assert.ok(e.relatedTarget, 'relatedTarget present') |
|||
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should fire slid event with relatedTarget', function (assert) { |
|||
assert.expect(2) |
|||
var template = '<div id="myCarousel" class="carousel slide">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>First Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Second Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Third Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' |
|||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' |
|||
+ '</div>' |
|||
|
|||
var done = assert.async() |
|||
|
|||
$(template) |
|||
.on('slid.bs.carousel', function (e) { |
|||
assert.ok(e.relatedTarget, 'relatedTarget present') |
|||
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should set interval from data attribute', function (assert) { |
|||
assert.expect(4) |
|||
var templateHTML = '<div id="myCarousel" class="carousel slide">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>First Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Second Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '<div class="carousel-caption">' |
|||
+ '<h4>Third Thumbnail label</h4>' |
|||
+ '<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec ' |
|||
+ 'id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ' |
|||
+ 'ultricies vehicula ut id elit.</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a>' |
|||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>' |
|||
+ '</div>' |
|||
var $carousel = $(templateHTML) |
|||
$carousel.attr('data-interval', 1814) |
|||
|
|||
$carousel.appendTo('body') |
|||
$('[data-slide]').first().trigger('click') |
|||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814) |
|||
$carousel.remove() |
|||
|
|||
$carousel.appendTo('body').attr('data-modal', 'foobar') |
|||
$('[data-slide]').first().trigger('click') |
|||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set') |
|||
$carousel.remove() |
|||
|
|||
$carousel.appendTo('body') |
|||
$('[data-slide]').first().trigger('click') |
|||
$carousel.attr('data-interval', 1860) |
|||
$('[data-slide]').first().trigger('click') |
|||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization') |
|||
$carousel.remove() |
|||
|
|||
$carousel.attr('data-interval', false) |
|||
$carousel.appendTo('body') |
|||
$carousel.bootstrapCarousel(1) |
|||
assert.strictEqual($carousel.data('bs.carousel')._config.interval, false, 'data attribute has higher priority than default options') |
|||
$carousel.remove() |
|||
}) |
|||
|
|||
QUnit.test('should skip over non-items when using item indices', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<script type="text/x-metamorph" id="thingy"/>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
|
|||
$template.bootstrapCarousel(1) |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') |
|||
}) |
|||
|
|||
QUnit.test('should skip over non-items when using next/prev methods', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="1814">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<script type="text/x-metamorph" id="thingy"/>' |
|||
+ '<div class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
|
|||
$template.bootstrapCarousel('next') |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') |
|||
}) |
|||
|
|||
QUnit.test('should go to previous item if left arrow key is pressed', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="third" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') |
|||
|
|||
$template.trigger($.Event('keydown', { which: 37 })) |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
}) |
|||
|
|||
QUnit.test('should go to next item if right arrow key is pressed', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="third" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
|
|||
$template.trigger($.Event('keydown', { which: 39 })) |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') |
|||
}) |
|||
|
|||
QUnit.test('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
var done = assert.async() |
|||
|
|||
var eventArrowDown = $.Event('keydown', { which: 40 }) |
|||
var eventArrowUp = $.Event('keydown', { which: 38 }) |
|||
|
|||
$template.one('keydown', function (event) { |
|||
assert.strictEqual(event.isDefaultPrevented(), false) |
|||
}) |
|||
|
|||
$template.trigger(eventArrowDown) |
|||
|
|||
$template.one('keydown', function (event) { |
|||
assert.strictEqual(event.isDefaultPrevented(), false) |
|||
done() |
|||
}) |
|||
|
|||
$template.trigger(eventArrowUp) |
|||
}) |
|||
|
|||
QUnit.test('should support disabling the keyboard navigation', function (assert) { |
|||
assert.expect(3) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-keyboard="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="third" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
|
|||
$template.trigger($.Event('keydown', { which: 39 })) |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press') |
|||
|
|||
$template.trigger($.Event('keydown', { which: 37 })) |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press') |
|||
}) |
|||
|
|||
QUnit.test('should ignore keyboard events within <input>s and <textarea>s', function (assert) { |
|||
assert.expect(7) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '<input type="text" id="in-put">' |
|||
+ '<textarea id="text-area"></textarea>' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="third" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
var $input = $template.find('#in-put') |
|||
var $textarea = $template.find('#text-area') |
|||
|
|||
assert.strictEqual($input.length, 1, 'found <input>') |
|||
assert.strictEqual($textarea.length, 1, 'found <textarea>') |
|||
|
|||
$template.bootstrapCarousel() |
|||
|
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') |
|||
|
|||
|
|||
$input.trigger($.Event('keydown', { which: 39 })) |
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <input>') |
|||
|
|||
$input.trigger($.Event('keydown', { which: 37 })) |
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <input>') |
|||
|
|||
|
|||
$textarea.trigger($.Event('keydown', { which: 39 })) |
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after right arrow press in <textarea>') |
|||
|
|||
$textarea.trigger($.Event('keydown', { which: 37 })) |
|||
assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item still active after left arrow press in <textarea>') |
|||
}) |
|||
|
|||
QUnit.test('should only add mouseenter and mouseleave listeners when not on mobile', function (assert) { |
|||
assert.expect(2) |
|||
var isMobile = 'ontouchstart' in document.documentElement |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false" data-pause="hover">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item active">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '<div id="third" class="carousel-item">' |
|||
+ '<img alt="">' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML).bootstrapCarousel() |
|||
|
|||
$.each(['mouseover', 'mouseout'], function (i, type) { |
|||
assert.strictEqual(type in $._data($template[0], 'events'), !isMobile, 'does' + (isMobile ? ' not' : '') + ' listen for ' + type + ' events') |
|||
}) |
|||
}) |
|||
|
|||
QUnit.test('should wrap around from end to start when wrap option is true', function (assert) { |
|||
assert.expect(3) |
|||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' |
|||
+ '<ol class="carousel-indicators">' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="1"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>' |
|||
+ '</ol>' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active" id="one">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="two">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="three">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' |
|||
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') } |
|||
|
|||
var done = assert.async() |
|||
|
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') |
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') |
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should wrap around from start to end when wrap option is true', function (assert) { |
|||
assert.expect(1) |
|||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' |
|||
+ '<ol class="carousel-indicators">' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="1"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>' |
|||
+ '</ol>' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active" id="one">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="two">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="three">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' |
|||
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
|
|||
var done = assert.async() |
|||
|
|||
$carousel |
|||
.on('slid.bs.carousel', function () { |
|||
assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('prev') |
|||
}) |
|||
|
|||
QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) { |
|||
assert.expect(3) |
|||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' |
|||
+ '<ol class="carousel-indicators">' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="1"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>' |
|||
+ '</ol>' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active" id="one">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="two">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="three">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' |
|||
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') } |
|||
|
|||
var done = assert.async() |
|||
|
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') |
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') |
|||
$carousel |
|||
.one('slid.bs.carousel', function () { |
|||
assert.ok(false, 'carousel slid when it should not have slid') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide') |
|||
done() |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
.bootstrapCarousel('next') |
|||
}) |
|||
|
|||
QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) { |
|||
assert.expect(1) |
|||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' |
|||
+ '<ol class="carousel-indicators">' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="1"/>' |
|||
+ '<li data-target="#carousel-example-generic" data-slide-to="2"/>' |
|||
+ '</ol>' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div class="carousel-item active" id="one">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="two">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '<div class="carousel-item" id="three">' |
|||
+ '<div class="carousel-caption"/>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>' |
|||
+ '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' |
|||
+ '</div>' |
|||
var $carousel = $(carouselHTML) |
|||
|
|||
$carousel |
|||
.on('slid.bs.carousel', function () { |
|||
assert.ok(false, 'carousel slid when it should not have slid') |
|||
}) |
|||
.bootstrapCarousel('prev') |
|||
assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide') |
|||
}) |
|||
|
|||
QUnit.test('should not prevent keydown for inputs and textareas', function (assert) { |
|||
assert.expect(2) |
|||
var templateHTML = '<div id="myCarousel" class="carousel" data-interval="false">' |
|||
+ '<div class="carousel-inner">' |
|||
+ '<div id="first" class="carousel-item">' |
|||
+ '<input type="text" id="inputText" />' |
|||
+ '</div>' |
|||
+ '<div id="second" class="carousel-item active">' |
|||
+ '<textarea id="txtArea"></textarea>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $template = $(templateHTML) |
|||
var done = assert.async() |
|||
$template.appendTo('#qunit-fixture') |
|||
var $inputText = $template.find('#inputText') |
|||
var $textArea = $template.find('#txtArea') |
|||
$template.bootstrapCarousel() |
|||
|
|||
var eventKeyDown = $.Event('keydown', { which: 65 }) // 65 for "a"
|
|||
$inputText.on('keydown', function (event) { |
|||
assert.strictEqual(event.isDefaultPrevented(), false) |
|||
}) |
|||
$inputText.trigger(eventKeyDown) |
|||
|
|||
$textArea.on('keydown', function (event) { |
|||
assert.strictEqual(event.isDefaultPrevented(), false) |
|||
done() |
|||
}) |
|||
$textArea.trigger(eventKeyDown) |
|||
}) |
|||
}) |
@ -0,0 +1,493 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('collapse plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).collapse, 'collapse method is defined') |
|||
}) |
|||
|
|||
QUnit.module('collapse', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapCollapse = $.fn.collapse.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.collapse = $.fn.bootstrapCollapse |
|||
delete $.fn.bootstrapCollapse |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.collapse, undefined, 'collapse was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapCollapse() |
|||
try { |
|||
$el.bootstrapCollapse('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $collapse = $el.bootstrapCollapse() |
|||
assert.ok($collapse instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($collapse[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should show a collapsed element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div class="collapse"/>').bootstrapCollapse('show') |
|||
|
|||
assert.ok($el.hasClass('show'), 'has class "show"') |
|||
assert.ok(!/height/i.test($el.attr('style')), 'has height reset') |
|||
}) |
|||
|
|||
QUnit.test('should collapse only the first collapse', function (assert) { |
|||
assert.expect(2) |
|||
var html = [ |
|||
'<div class="panel-group" id="accordion1">', |
|||
'<div class="panel">', |
|||
'<div id="collapse1" class="collapse"/>', |
|||
'</div>', |
|||
'</div>', |
|||
'<div class="panel-group" id="accordion2">', |
|||
'<div class="panel">', |
|||
'<div id="collapse2" class="collapse show"/>', |
|||
'</div>', |
|||
'</div>' |
|||
].join('') |
|||
$(html).appendTo('#qunit-fixture') |
|||
var $el1 = $('#collapse1') |
|||
var $el2 = $('#collapse2') |
|||
$el1.bootstrapCollapse('show') |
|||
|
|||
assert.ok($el1.hasClass('show')) |
|||
assert.ok($el2.hasClass('show')) |
|||
}) |
|||
|
|||
QUnit.test('should hide a collapsed element', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div class="collapse"/>').bootstrapCollapse('hide') |
|||
|
|||
assert.ok(!$el.hasClass('show'), 'does not have class "show"') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown when show is prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="collapse"/>') |
|||
.on('show.bs.collapse', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok(false, 'shown event fired') |
|||
}) |
|||
.bootstrapCollapse('show') |
|||
}) |
|||
|
|||
QUnit.test('should reset style to auto after finishing opening collapse', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="collapse" style="height: 0px"/>') |
|||
.on('show.bs.collapse', function () { |
|||
assert.strictEqual(this.style.height, '0px', 'height is 0px') |
|||
}) |
|||
.on('shown.bs.collapse', function () { |
|||
assert.strictEqual(this.style.height, '', 'height is auto') |
|||
done() |
|||
}) |
|||
.bootstrapCollapse('show') |
|||
}) |
|||
|
|||
QUnit.test('should reset style to auto after finishing closing collapse', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="collapse"/>') |
|||
.on('shown.bs.collapse', function () { |
|||
$(this).bootstrapCollapse('hide') |
|||
}) |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.strictEqual(this.style.height, '', 'height is auto') |
|||
done() |
|||
}) |
|||
.bootstrapCollapse('show') |
|||
}) |
|||
|
|||
QUnit.test('should remove "collapsed" class from target when collapse is shown', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok(!$target.hasClass('collapsed'), 'target does not have collapsed class') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should add "collapsed" class to target when collapse is hidden', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.ok($target.hasClass('collapsed'), 'target has collapsed class') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should remove "collapsed" class from all triggers targeting the collapse when the collapse is shown', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') |
|||
var $alt = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok(!$target.hasClass('collapsed'), 'target trigger does not have collapsed class') |
|||
assert.ok(!$alt.hasClass('collapsed'), 'alt trigger does not have collapsed class') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should add "collapsed" class to all triggers targeting the collapse when the collapse is hidden', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') |
|||
var $alt = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.ok($target.hasClass('collapsed'), 'target has collapsed class') |
|||
assert.ok($alt.hasClass('collapsed'), 'alt trigger has collapsed class') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should not close a collapse when initialized with "show" option if already shown', function (assert) { |
|||
assert.expect(0) |
|||
var done = assert.async() |
|||
|
|||
var $test = $('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hide.bs.collapse', function () { |
|||
assert.ok(false) |
|||
}) |
|||
|
|||
$test.bootstrapCollapse('show') |
|||
|
|||
setTimeout(done, 0) |
|||
}) |
|||
|
|||
QUnit.test('should open a collapse when initialized with "show" option if not already shown', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $test = $('<div id="test1" />') |
|||
.appendTo('#qunit-fixture') |
|||
.on('show.bs.collapse', function () { |
|||
assert.ok(true) |
|||
}) |
|||
|
|||
$test.bootstrapCollapse('show') |
|||
|
|||
setTimeout(done, 0) |
|||
}) |
|||
|
|||
QUnit.test('should not show a collapse when initialized with "hide" option if already hidden', function (assert) { |
|||
assert.expect(0) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="collapse"></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('show.bs.collapse', function () { |
|||
assert.ok(false, 'showing a previously-uninitialized hidden collapse when the "hide" method is called') |
|||
}) |
|||
.bootstrapCollapse('hide') |
|||
|
|||
setTimeout(done, 0) |
|||
}) |
|||
|
|||
QUnit.test('should hide a collapse when initialized with "hide" option if not already hidden', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="collapse show"></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hide.bs.collapse', function () { |
|||
assert.ok(true, 'hiding a previously-uninitialized shown collapse when the "hide" method is called') |
|||
}) |
|||
.bootstrapCollapse('hide') |
|||
|
|||
setTimeout(done, 0) |
|||
}) |
|||
|
|||
QUnit.test('should remove "collapsed" class from active accordion target', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var accordionHTML = '<div id="accordion">' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '</div>' |
|||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card') |
|||
|
|||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1" data-parent="#accordion"/>').appendTo($groups.eq(0)) |
|||
|
|||
$('<div id="body1" class="show"/>').appendTo($groups.eq(0)) |
|||
|
|||
var $target2 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body2" data-parent="#accordion"/>').appendTo($groups.eq(1)) |
|||
|
|||
$('<div id="body2"/>').appendTo($groups.eq(1)) |
|||
|
|||
var $target3 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body3" data-parent="#accordion"/>').appendTo($groups.eq(2)) |
|||
|
|||
$('<div id="body3"/>') |
|||
.appendTo($groups.eq(2)) |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"') |
|||
assert.ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"') |
|||
assert.ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"') |
|||
|
|||
done() |
|||
}) |
|||
|
|||
$target3.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should allow dots in data-parent', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var accordionHTML = '<div class="accordion">' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '</div>' |
|||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card') |
|||
|
|||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1" data-parent=".accordion"/>').appendTo($groups.eq(0)) |
|||
|
|||
$('<div id="body1" class="show"/>').appendTo($groups.eq(0)) |
|||
|
|||
var $target2 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body2" data-parent=".accordion"/>').appendTo($groups.eq(1)) |
|||
|
|||
$('<div id="body2"/>').appendTo($groups.eq(1)) |
|||
|
|||
var $target3 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body3" data-parent=".accordion"/>').appendTo($groups.eq(2)) |
|||
|
|||
$('<div id="body3"/>') |
|||
.appendTo($groups.eq(2)) |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"') |
|||
assert.ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"') |
|||
assert.ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"') |
|||
|
|||
done() |
|||
}) |
|||
|
|||
$target3.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="true" on target when collapse is shown', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.collapse', function () { |
|||
assert.strictEqual($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="false" on target when collapse is hidden', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.strictEqual($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="true" on all triggers targeting the collapse when the collapse is shown', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture') |
|||
var $alt = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.collapse', function () { |
|||
assert.strictEqual($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"') |
|||
assert.strictEqual($alt.attr('aria-expanded'), 'true', 'aria-expanded on alt is "true"') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="false" on all triggers targeting the collapse when the collapse is hidden', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture') |
|||
var $alt = $('<a role="button" data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.strictEqual($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"') |
|||
assert.strictEqual($alt.attr('aria-expanded'), 'false', 'aria-expanded on alt is "false"') |
|||
done() |
|||
}) |
|||
|
|||
$target.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should change aria-expanded from active accordion target to "false" and set the newly active one to "true"', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var accordionHTML = '<div id="accordion">' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '</div>' |
|||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card') |
|||
|
|||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1" data-parent="#accordion"/>').appendTo($groups.eq(0)) |
|||
|
|||
$('<div id="body1" aria-expanded="true" class="show"/>').appendTo($groups.eq(0)) |
|||
|
|||
var $target2 = $('<a role="button" data-toggle="collapse" href="#body2" data-parent="#accordion" class="collapsed" />').appendTo($groups.eq(1)) |
|||
|
|||
$('<div id="body2" aria-expanded="false"/>').appendTo($groups.eq(1)) |
|||
|
|||
var $target3 = $('<a class="collapsed" data-toggle="collapse" role="button" href="#body3" data-parent="#accordion"/>').appendTo($groups.eq(2)) |
|||
|
|||
$('<div id="body3" aria-expanded="false"/>') |
|||
.appendTo($groups.eq(2)) |
|||
.on('shown.bs.collapse', function () { |
|||
assert.strictEqual($target1.attr('aria-expanded'), 'false', 'inactive target 1 has aria-expanded="false"') |
|||
assert.strictEqual($target2.attr('aria-expanded'), 'false', 'inactive target 2 has aria-expanded="false"') |
|||
assert.strictEqual($target3.attr('aria-expanded'), 'true', 'active target 3 has aria-expanded="false"') |
|||
|
|||
done() |
|||
}) |
|||
|
|||
$target3.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should not fire show event if show is prevented because other element is still transitioning', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var accordionHTML = '<div id="accordion">' |
|||
+ '<div class="card"/>' |
|||
+ '<div class="card"/>' |
|||
+ '</div>' |
|||
var showFired = false |
|||
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card') |
|||
|
|||
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1" data-parent="#accordion"/>').appendTo($groups.eq(0)) |
|||
|
|||
$('<div id="body1" class="collapse"/>') |
|||
.appendTo($groups.eq(0)) |
|||
.on('show.bs.collapse', function () { |
|||
showFired = true |
|||
}) |
|||
|
|||
var $target2 = $('<a role="button" data-toggle="collapse" href="#body2" data-parent="#accordion"/>').appendTo($groups.eq(1)) |
|||
var $body2 = $('<div id="body2" class="collapse"/>').appendTo($groups.eq(1)) |
|||
|
|||
$target2.trigger('click') |
|||
|
|||
$body2 |
|||
.toggleClass('show collapsing') |
|||
.data('bs.collapse')._isTransitioning = 1 |
|||
|
|||
$target1.trigger('click') |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!showFired, 'show event did not fire') |
|||
done() |
|||
}, 1) |
|||
}) |
|||
|
|||
QUnit.test('should add "collapsed" class to target when collapse is hidden via manual invocation', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1" class="show"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.collapse', function () { |
|||
assert.ok($target.hasClass('collapsed')) |
|||
done() |
|||
}) |
|||
.bootstrapCollapse('hide') |
|||
}) |
|||
|
|||
QUnit.test('should remove "collapsed" class from target when collapse is shown via manual invocation', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="test1"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.collapse', function () { |
|||
assert.ok(!$target.hasClass('collapsed')) |
|||
done() |
|||
}) |
|||
.bootstrapCollapse('show') |
|||
}) |
|||
|
|||
}) |
@ -0,0 +1,495 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('dropdowns plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).dropdown, 'dropdown method is defined') |
|||
}) |
|||
|
|||
QUnit.module('dropdowns', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapDropdown = $.fn.dropdown.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.dropdown = $.fn.bootstrapDropdown |
|||
delete $.fn.bootstrapDropdown |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.dropdown, undefined, 'dropdown was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapDropdown() |
|||
try { |
|||
$el.bootstrapDropdown('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $dropdown = $el.bootstrapDropdown() |
|||
assert.ok($dropdown instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($dropdown[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should not open dropdown if target is disabled via attribute', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<button disabled href="#" class="btn dropdown-toggle" data-toggle="dropdown">Dropdown</button>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').bootstrapDropdown().trigger('click') |
|||
|
|||
assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="true" on target when dropdown menu is shown', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
assert.strictEqual($dropdown.attr('aria-expanded'), 'true', 'aria-expanded is set to string "true" on click') |
|||
}) |
|||
|
|||
QUnit.test('should set aria-expanded="false" on target when dropdown menu is hidden', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" aria-expanded="false" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
|
|||
$dropdown |
|||
.parent('.dropdown') |
|||
.on('hidden.bs.dropdown', function () { |
|||
assert.strictEqual($dropdown.attr('aria-expanded'), 'false', 'aria-expanded is set to string "false" on hide') |
|||
done() |
|||
}) |
|||
|
|||
$dropdown.trigger('click') |
|||
$(document.body).trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should not open dropdown if target is disabled via class', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<button href="#" class="btn dropdown-toggle disabled" data-toggle="dropdown">Dropdown</button>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').bootstrapDropdown().trigger('click') |
|||
|
|||
assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
}) |
|||
|
|||
QUnit.test('should add class show to menu if clicked', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').bootstrapDropdown().trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
}) |
|||
|
|||
QUnit.test('should test if element has a # before assuming it\'s a selector', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="/foo/" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').bootstrapDropdown().trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
}) |
|||
|
|||
|
|||
QUnit.test('should remove "show" class if body is clicked', function (assert) { |
|||
assert.expect(2) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
$(document.body).trigger('click') |
|||
assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed') |
|||
}) |
|||
|
|||
QUnit.test('should remove "show" class if body is focused', function (assert) { |
|||
assert.expect(2) |
|||
var dropdownHTML = '<div class="tabs">' |
|||
+ '<div class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<div class="dropdown-menu">' |
|||
+ '<a class="dropdown-item" href="#">Secondary link</a>' |
|||
+ '<a class="dropdown-item" href="#">Something else here</a>' |
|||
+ '<div class="dropdown-divider"/>' |
|||
+ '<a class="dropdown-item" href="#">Another link</a>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') |
|||
$(document.body).trigger('focusin') |
|||
assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed') |
|||
}) |
|||
|
|||
QUnit.test('should remove "show" class if body is clicked, with multiple dropdowns', function (assert) { |
|||
assert.expect(7) |
|||
var dropdownHTML = '<ul class="nav">' |
|||
+ '<li><a href="#menu1">Menu 1</a></li>' |
|||
+ '<li class="dropdown" id="testmenu">' |
|||
+ '<a class="dropdown-toggle" data-toggle="dropdown" href="#testmenu">Test menu <span class="caret"/></a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#sub1">Submenu 1</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
+ '<div class="btn-group">' |
|||
+ '<button class="btn">Actions</button>' |
|||
+ '<button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"/></button>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Action 1</a></li>' |
|||
+ '</ul>' |
|||
+ '</div>' |
|||
var $dropdowns = $(dropdownHTML).appendTo('#qunit-fixture').find('[data-toggle="dropdown"]') |
|||
var $first = $dropdowns.first() |
|||
var $last = $dropdowns.last() |
|||
|
|||
assert.strictEqual($dropdowns.length, 2, 'two dropdowns') |
|||
|
|||
$first.trigger('click') |
|||
assert.strictEqual($first.parents('.show').length, 1, '"show" class added on click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is shown') |
|||
$(document.body).trigger('click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed') |
|||
|
|||
$last.trigger('click') |
|||
assert.strictEqual($last.parent('.show').length, 1, '"show" class added on click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is shown') |
|||
$(document.body).trigger('click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed') |
|||
}) |
|||
|
|||
QUnit.test('should remove "show" class if body is focused, with multiple dropdowns', function (assert) { |
|||
assert.expect(7) |
|||
var dropdownHTML = '<div class="nav">' |
|||
+ '<div class="dropdown" id="testmenu">' |
|||
+ '<a class="dropdown-toggle" data-toggle="dropdown" href="#testmenu">Test menu <span class="caret"/></a>' |
|||
+ '<div class="dropdown-menu">' |
|||
+ '<a class="dropdown-item" href="#sub1">Submenu 1</a>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div class="btn-group">' |
|||
+ '<button class="btn">Actions</button>' |
|||
+ '<button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"/></button>' |
|||
+ '<div class="dropdown-menu">' |
|||
+ '<a class="dropdown-item" href="#">Action 1</a>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $dropdowns = $(dropdownHTML).appendTo('#qunit-fixture').find('[data-toggle="dropdown"]') |
|||
var $first = $dropdowns.first() |
|||
var $last = $dropdowns.last() |
|||
|
|||
assert.strictEqual($dropdowns.length, 2, 'two dropdowns') |
|||
|
|||
$first.trigger('click') |
|||
assert.strictEqual($first.parents('.show').length, 1, '"show" class added on click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is show') |
|||
$(document.body).trigger('focusin') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed') |
|||
|
|||
$last.trigger('click') |
|||
assert.strictEqual($last.parent('.show').length, 1, '"show" class added on click') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is show') |
|||
$(document.body).trigger('focusin') |
|||
assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed') |
|||
}) |
|||
|
|||
QUnit.test('should fire show and hide event', function (assert) { |
|||
assert.expect(2) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
|
|||
var done = assert.async() |
|||
|
|||
$dropdown |
|||
.parent('.dropdown') |
|||
.on('show.bs.dropdown', function () { |
|||
assert.ok(true, 'show was fired') |
|||
}) |
|||
.on('hide.bs.dropdown', function () { |
|||
assert.ok(true, 'hide was fired') |
|||
done() |
|||
}) |
|||
|
|||
$dropdown.trigger('click') |
|||
$(document.body).trigger('click') |
|||
}) |
|||
|
|||
|
|||
QUnit.test('should fire shown and hidden event', function (assert) { |
|||
assert.expect(2) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
|
|||
var done = assert.async() |
|||
|
|||
$dropdown |
|||
.parent('.dropdown') |
|||
.on('shown.bs.dropdown', function () { |
|||
assert.ok(true, 'shown was fired') |
|||
}) |
|||
.on('hidden.bs.dropdown', function () { |
|||
assert.ok(true, 'hidden was fired') |
|||
done() |
|||
}) |
|||
|
|||
$dropdown.trigger('click') |
|||
$(document.body).trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should fire shown and hidden event with a relatedTarget', function (assert) { |
|||
assert.expect(2) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
var done = assert.async() |
|||
|
|||
$dropdown.parent('.dropdown') |
|||
.on('hidden.bs.dropdown', function (e) { |
|||
assert.strictEqual(e.relatedTarget, $dropdown[0]) |
|||
done() |
|||
}) |
|||
.on('shown.bs.dropdown', function (e) { |
|||
assert.strictEqual(e.relatedTarget, $dropdown[0]) |
|||
$(document.body).trigger('click') |
|||
}) |
|||
|
|||
$dropdown.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should ignore keyboard events within <input>s and <textarea>s', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#">Secondary link</a></li>' |
|||
+ '<li><a href="#">Something else here</a></li>' |
|||
+ '<li class="divider"/>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '<li><input type="text" id="input"></li>' |
|||
+ '<li><textarea id="textarea"/></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
|
|||
var $input = $('#input') |
|||
var $textarea = $('#textarea') |
|||
|
|||
$dropdown |
|||
.parent('.dropdown') |
|||
.on('shown.bs.dropdown', function () { |
|||
assert.ok(true, 'shown was fired') |
|||
|
|||
$input.trigger('focus').trigger($.Event('keydown', { which: 38 })) |
|||
assert.ok($(document.activeElement).is($input), 'input still focused') |
|||
|
|||
$textarea.trigger('focus').trigger($.Event('keydown', { which: 38 })) |
|||
assert.ok($(document.activeElement).is($textarea), 'textarea still focused') |
|||
|
|||
done() |
|||
}) |
|||
|
|||
$dropdown.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should skip disabled element when using keyboard navigation', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<ul class="tabs">' |
|||
+ '<li class="dropdown">' |
|||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li class="disabled"><a href="#">Disabled link</a></li>' |
|||
+ '<li><a href="#">Another link</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
$dropdown.trigger($.Event('keydown', { which: 40 })) |
|||
$dropdown.trigger($.Event('keydown', { which: 40 })) |
|||
|
|||
assert.ok(!$(document.activeElement).parent().is('.disabled'), '.disabled is not focused') |
|||
}) |
|||
|
|||
QUnit.test('should not close the dropdown if the user clicks on a text field', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<div class="btn-group">' |
|||
+ '<button type="button" data-toggle="dropdown">Dropdown</button>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><input id="textField" type="text" /></li>' |
|||
+ '</ul>' |
|||
+ '</div>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
$('#textField').trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.btn-group').hasClass('show'), 'dropdown menu is shown') |
|||
}) |
|||
|
|||
QUnit.test('should not close the dropdown if the user clicks on a textarea', function (assert) { |
|||
assert.expect(1) |
|||
var dropdownHTML = '<div class="btn-group">' |
|||
+ '<button type="button" data-toggle="dropdown">Dropdown</button>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><textarea id="textArea"></textarea></li>' |
|||
+ '</ul>' |
|||
+ '</div>' |
|||
var $dropdown = $(dropdownHTML) |
|||
.appendTo('#qunit-fixture') |
|||
.find('[data-toggle="dropdown"]') |
|||
.bootstrapDropdown() |
|||
.trigger('click') |
|||
|
|||
$('#textArea').trigger('click') |
|||
|
|||
assert.ok($dropdown.parent('.btn-group').hasClass('show'), 'dropdown menu is shown') |
|||
}) |
|||
}) |
@ -0,0 +1,479 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('modal plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).modal, 'modal method is defined') |
|||
}) |
|||
|
|||
QUnit.module('modal', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapModal = $.fn.modal.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.modal = $.fn.bootstrapModal |
|||
delete $.fn.bootstrapModal |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.modal, undefined, 'modal was set back to undefined (orig value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div id="modal-test"/>') |
|||
$el.bootstrapModal() |
|||
try { |
|||
$el.bootstrapModal('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div id="modal-test"/>') |
|||
var $modal = $el.bootstrapModal() |
|||
assert.ok($modal instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($modal[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should expose defaults var for settings', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($.fn.bootstrapModal.Constructor.Default, 'default object exposed') |
|||
}) |
|||
|
|||
QUnit.test('should insert into dom when show method is called', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should fire show event', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('show.bs.modal', function () { |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown when show was prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('show.bs.modal', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok(false, 'shown event fired') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should hide modal when hide is called', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom') |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should toggle when toggle is called', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom') |
|||
$(this).bootstrapModal('toggle') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('toggle') |
|||
}) |
|||
|
|||
QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom') |
|||
$(this).find('.close').trigger('click') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('toggle') |
|||
}) |
|||
|
|||
QUnit.test('should allow modal close with "backdrop:false"', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test" data-backdrop="false"/>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should close modal when clicking outside of modal-content', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"><div class="contents"/></div>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom') |
|||
$('.contents').trigger('click') |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
$('#modal-test').trigger('click') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should not close modal when clicking outside of modal-content if data-backdrop="true"', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test" data-backdrop="false"><div class="contents"/></div>') |
|||
.on('shown.bs.modal', function () { |
|||
$('#modal-test').trigger('click') |
|||
assert.ok($('#modal-test').is(':visible'), 'modal not hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should close modal when escape key is pressed via keydown', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var $div = $('<div id="modal-test"/>') |
|||
$div |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').length, 'modal inserted into dom') |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
$div.trigger($.Event('keydown', { which: 27 })) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
$div.remove() |
|||
done() |
|||
}, 0) |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var $div = $('<div id="modal-test"/>') |
|||
$div |
|||
.on('shown.bs.modal', function () { |
|||
assert.ok($('#modal-test').length, 'modal inserted into dom') |
|||
assert.ok($('#modal-test').is(':visible'), 'modal visible') |
|||
$div.trigger($.Event('keyup', { which: 27 })) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($div.is(':visible'), 'modal still visible') |
|||
$div.remove() |
|||
done() |
|||
}, 0) |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var triggered |
|||
|
|||
$('<div id="modal-test"><div class="contents"/></div>') |
|||
.on('shown.bs.modal', function () { |
|||
triggered = 0 |
|||
$('#modal-test').trigger('click') |
|||
}) |
|||
.on('hide.bs.modal', function () { |
|||
triggered += 1 |
|||
assert.strictEqual(triggered, 1, 'modal hide triggered once') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should remove aria-hidden attribute when shown, add it back when hidden', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test" aria-hidden="true"/>') |
|||
.on('shown.bs.modal', function () { |
|||
assert.notOk($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute removed') |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute added') |
|||
assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'correct aria-hidden="true" added') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>') |
|||
.one('shown.bs.modal', function () { |
|||
$('#close').trigger('click') |
|||
}) |
|||
.one('hidden.bs.modal', function () { |
|||
// after one open-close cycle
|
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
$(this) |
|||
.one('shown.bs.modal', function () { |
|||
$('#close').trigger('click') |
|||
}) |
|||
.one('hidden.bs.modal', function () { |
|||
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden') |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>') |
|||
.on('hidden.bs.modal', function () { |
|||
setTimeout(function () { |
|||
assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused') |
|||
done() |
|||
}, 0) |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$('#close').trigger('click') |
|||
}) |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$toggleBtn.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture') |
|||
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div>') |
|||
.one('show.bs.modal', function (e) { |
|||
e.preventDefault() |
|||
$otherBtn.trigger('focus') |
|||
setTimeout($.proxy(function () { |
|||
$(this).bootstrapModal('show') |
|||
}, this), 0) |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
setTimeout(function () { |
|||
assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element') |
|||
done() |
|||
}, 0) |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$('#close').trigger('click') |
|||
}) |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$toggleBtn.trigger('click') |
|||
}) |
|||
|
|||
QUnit.test('should restore inline body padding after closing', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
var originalBodyPad = 0 |
|||
var $body = $(document.body) |
|||
|
|||
$body.css('padding-right', originalBodyPad) |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('hidden.bs.modal', function () { |
|||
var currentBodyPad = parseInt($body.css('padding-right'), 10) |
|||
assert.notStrictEqual($body.attr('style'), '', 'body has non-empty style attribute') |
|||
assert.strictEqual(currentBodyPad, originalBodyPad, 'original body padding was not changed') |
|||
$body.removeAttr('style') |
|||
done() |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should ignore values set via CSS when trying to restore body padding after closing', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var $body = $(document.body) |
|||
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head') |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('hidden.bs.modal', function () { |
|||
assert.ok(!$body.attr('style'), 'body does not have inline padding set') |
|||
$style.remove() |
|||
done() |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should have a paddingRight when the modal is taller than the viewport', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
$('<div class="fixed-top fixed-bottom sticky-top is-fixed">@Johann-S</div>').appendTo('#qunit-fixture') |
|||
$('.fixed-top, .fixed-bottom, .is-fixed, .sticky-top').css('padding-right', '10px') |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('shown.bs.modal', function () { |
|||
var paddingRight = parseInt($(document.body).css('padding-right'), 10) |
|||
assert.strictEqual(isNaN(paddingRight), false) |
|||
assert.strictEqual(paddingRight !== 0, true) |
|||
$(document.body).css('padding-right', '') // Because test case "should ignore other inline styles when trying to restore body padding after closing" fail if not
|
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should remove padding-right on modal after closing', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
$('<div class="fixed-top fixed-bottom is-fixed sticky-top">@Johann-S</div>').appendTo('#qunit-fixture') |
|||
$('.fixed-top, .fixed-bottom, .is-fixed, .sticky-top').css('padding-right', '10px') |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('shown.bs.modal', function () { |
|||
var paddingRight = parseInt($(document.body).css('padding-right'), 10) |
|||
assert.strictEqual(isNaN(paddingRight), false) |
|||
assert.strictEqual(paddingRight !== 0, true) |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.on('hidden.bs.modal', function () { |
|||
var paddingRight = parseInt($(document.body).css('padding-right'), 10) |
|||
assert.strictEqual(paddingRight, 0) |
|||
done() |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should ignore other inline styles when trying to restore body padding after closing', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
var $body = $(document.body) |
|||
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head') |
|||
|
|||
$body.css('color', 'red') |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('hidden.bs.modal', function () { |
|||
assert.strictEqual($body[0].style.paddingRight, '', 'body does not have inline padding set') |
|||
assert.strictEqual($body[0].style.color, 'red', 'body still has other inline styles set') |
|||
$body.removeAttr('style') |
|||
$style.remove() |
|||
done() |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should properly restore non-pixel inline body padding after closing', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var $body = $(document.body) |
|||
|
|||
$body.css('padding-right', '5%') |
|||
|
|||
$('<div id="modal-test"/>') |
|||
.on('hidden.bs.modal', function () { |
|||
assert.strictEqual($body[0].style.paddingRight, '5%', 'body does not have inline padding set') |
|||
$body.removeAttr('style') |
|||
done() |
|||
}) |
|||
.on('shown.bs.modal', function () { |
|||
$(this).bootstrapModal('hide') |
|||
}) |
|||
.bootstrapModal('show') |
|||
}) |
|||
|
|||
QUnit.test('should not follow link in area tag', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<map><area id="test" shape="default" data-toggle="modal" data-target="#modal-test" href="demo.html"/></map>') |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>') |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$('#test') |
|||
.on('click.bs.modal.data-api', function (event) { |
|||
assert.notOk(event.isDefaultPrevented(), 'navigating to href will happen') |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(event.isDefaultPrevented(), 'model shown instead of navigating to href') |
|||
done() |
|||
}, 1) |
|||
}) |
|||
.trigger('click') |
|||
}) |
|||
}) |
@ -0,0 +1,72 @@ |
|||
/* |
|||
* grunt-contrib-qunit |
|||
* http://gruntjs.com/
|
|||
* |
|||
* Copyright (c) 2014 "Cowboy" Ben Alman, contributors |
|||
* Licensed under the MIT license. |
|||
*/ |
|||
|
|||
(function () { |
|||
'use strict' |
|||
|
|||
// Don't re-order tests.
|
|||
QUnit.config.reorder = false |
|||
// Run tests serially, not in parallel.
|
|||
QUnit.config.autorun = false |
|||
|
|||
// Send messages to the parent PhantomJS process via alert! Good times!!
|
|||
function sendMessage() { |
|||
var args = [].slice.call(arguments) |
|||
alert(JSON.stringify(args)) |
|||
} |
|||
|
|||
// These methods connect QUnit to PhantomJS.
|
|||
QUnit.log(function (obj) { |
|||
// What is this I don’t even
|
|||
if (obj.message === '[object Object], undefined:undefined') { return } |
|||
|
|||
// Parse some stuff before sending it.
|
|||
var actual |
|||
var expected |
|||
if (!obj.result) { |
|||
// Dumping large objects can be very slow, and the dump isn't used for
|
|||
// passing tests, so only dump if the test failed.
|
|||
actual = QUnit.dump.parse(obj.actual) |
|||
expected = QUnit.dump.parse(obj.expected) |
|||
} |
|||
// Send it.
|
|||
sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source) |
|||
}) |
|||
|
|||
QUnit.testStart(function (obj) { |
|||
sendMessage('qunit.testStart', obj.name) |
|||
}) |
|||
|
|||
QUnit.testDone(function (obj) { |
|||
sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total, obj.duration) |
|||
}) |
|||
|
|||
QUnit.moduleStart(function (obj) { |
|||
sendMessage('qunit.moduleStart', obj.name) |
|||
}) |
|||
|
|||
QUnit.moduleDone(function (obj) { |
|||
if (obj.failed === 0) { |
|||
console.log('\r\u221A All tests passed in "' + obj.name + '" module') |
|||
} else { |
|||
console.log('\u00D7 ' + obj.failed + ' tests failed in "' + obj.name + '" module') |
|||
} |
|||
sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total) |
|||
}) |
|||
|
|||
QUnit.begin(function () { |
|||
sendMessage('qunit.begin') |
|||
console.log('\n\nStarting test suite') |
|||
console.log('================================================\n') |
|||
}) |
|||
|
|||
QUnit.done(function (obj) { |
|||
sendMessage('qunit.done', obj.failed, obj.passed, obj.total, obj.runtime) |
|||
}) |
|||
|
|||
}()) |
@ -0,0 +1,367 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('popover plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).popover, 'popover method is defined') |
|||
}) |
|||
|
|||
QUnit.module('popover', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapPopover = $.fn.popover.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.popover = $.fn.bootstrapPopover |
|||
delete $.fn.bootstrapPopover |
|||
$('.popover').remove() |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.popover, undefined, 'popover was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapPopover() |
|||
try { |
|||
$el.bootstrapPopover('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $popover = $el.bootstrapPopover() |
|||
assert.ok($popover instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($popover[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should render popover element', function (assert) { |
|||
assert.expect(2) |
|||
var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover removed') |
|||
}) |
|||
|
|||
QUnit.test('should store popover instance in popover data object', function (assert) { |
|||
assert.expect(1) |
|||
var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>').bootstrapPopover() |
|||
|
|||
assert.ok($popover.data('bs.popover'), 'popover instance exists') |
|||
}) |
|||
|
|||
QUnit.test('should store popover trigger in popover instance data object', function (assert) { |
|||
assert.expect(1) |
|||
var $popover = $('<a href="#" title="ResentedHook">@ResentedHook</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover() |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
|
|||
assert.ok($('.popover').data('bs.popover'), 'popover trigger stored in instance data') |
|||
}) |
|||
|
|||
QUnit.test('should get title and content from options', function (assert) { |
|||
assert.expect(4) |
|||
var $popover = $('<a href="#">@fat</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
title: function () { |
|||
return '@fat' |
|||
}, |
|||
content: function () { |
|||
return 'loves writing tests (╯°□°)╯︵ ┻━┻' |
|||
} |
|||
}) |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.strictEqual($('.popover .popover-title').text(), '@fat', 'title correctly inserted') |
|||
assert.strictEqual($('.popover .popover-content').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
|
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should allow DOMElement title and content (html: true)', function (assert) { |
|||
assert.expect(5) |
|||
var title = document.createTextNode('@glebm <3 writing tests') |
|||
var content = $('<i>¯\\_(ツ)_/¯</i>').get(0) |
|||
var $popover = $('<a href="#" rel="tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ html: true, title: title, content: content }) |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover inserted') |
|||
assert.strictEqual($('.popover .popover-title').text(), '@glebm <3 writing tests', 'title inserted') |
|||
assert.ok($.contains($('.popover').get(0), title), 'title node moved, not copied') |
|||
// toLowerCase because IE8 will return <I>...</I>
|
|||
assert.strictEqual($('.popover .popover-content').html().toLowerCase(), '<i>¯\\_(ツ)_/¯</i>', 'content inserted') |
|||
assert.ok($.contains($('.popover').get(0), content), 'content node moved, not copied') |
|||
}) |
|||
|
|||
QUnit.test('should allow DOMElement title and content (html: false)', function (assert) { |
|||
assert.expect(5) |
|||
var title = document.createTextNode('@glebm <3 writing tests') |
|||
var content = $('<i>¯\\_(ツ)_/¯</i>').get(0) |
|||
var $popover = $('<a href="#" rel="tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ title: title, content: content }) |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover inserted') |
|||
assert.strictEqual($('.popover .popover-title').text(), '@glebm <3 writing tests', 'title inserted') |
|||
assert.ok(!$.contains($('.popover').get(0), title), 'title node copied, not moved') |
|||
assert.strictEqual($('.popover .popover-content').html(), '¯\\_(ツ)_/¯', 'content inserted') |
|||
assert.ok(!$.contains($('.popover').get(0), content), 'content node copied, not moved') |
|||
}) |
|||
|
|||
|
|||
QUnit.test('should not duplicate HTML object', function (assert) { |
|||
assert.expect(6) |
|||
var $div = $('<div/>').html('loves writing tests (╯°□°)╯︵ ┻━┻') |
|||
|
|||
var $popover = $('<a href="#">@fat</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
html: true, |
|||
content: function () { |
|||
return $div |
|||
} |
|||
}) |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.equal($('.popover .popover-content').html(), $div[0].outerHTML, 'content correctly inserted') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.equal($('.popover .popover-content').html(), $div[0].outerHTML, 'content correctly inserted') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should get title and content from attributes', function (assert) { |
|||
assert.expect(4) |
|||
var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover() |
|||
.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.strictEqual($('.popover .popover-title').text(), '@mdo', 'title correctly inserted') |
|||
assert.strictEqual($('.popover .popover-content').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should get title and content from attributes ignoring options passed via js', function (assert) { |
|||
assert.expect(4) |
|||
var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
title: 'ignored title option', |
|||
content: 'ignored content option' |
|||
}) |
|||
.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.strictEqual($('.popover .popover-title').text(), '@mdo', 'title correctly inserted') |
|||
assert.strictEqual($('.popover .popover-content').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should respect custom template', function (assert) { |
|||
assert.expect(3) |
|||
var $popover = $('<a href="#">@fat</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
title: 'Test', |
|||
content: 'Test', |
|||
template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>' |
|||
}) |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
|
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.ok($('.popover').hasClass('foobar'), 'custom class is present') |
|||
|
|||
$popover.bootstrapPopover('hide') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should destroy popover', function (assert) { |
|||
assert.expect(7) |
|||
var $popover = $('<div/>') |
|||
.bootstrapPopover({ |
|||
trigger: 'hover' |
|||
}) |
|||
.on('click.foo', $.noop) |
|||
|
|||
assert.ok($popover.data('bs.popover'), 'popover has data') |
|||
assert.ok($._data($popover[0], 'events').mouseover && $._data($popover[0], 'events').mouseout, 'popover has hover event') |
|||
assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover has extra click.foo event') |
|||
|
|||
$popover.bootstrapPopover('show') |
|||
$popover.bootstrapPopover('dispose') |
|||
|
|||
assert.ok(!$popover.hasClass('show'), 'popover is hidden') |
|||
assert.ok(!$popover.data('popover'), 'popover does not have data') |
|||
assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover still has click.foo') |
|||
assert.ok(!$._data($popover[0], 'events').mouseover && !$._data($popover[0], 'events').mouseout, 'popover does not have any events') |
|||
}) |
|||
|
|||
QUnit.test('should render popover element using delegated selector', function (assert) { |
|||
assert.expect(2) |
|||
var $div = $('<div><a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
selector: 'a', |
|||
trigger: 'click' |
|||
}) |
|||
|
|||
$div.find('a').trigger('click') |
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
|
|||
$div.find('a').trigger('click') |
|||
assert.strictEqual($('.popover').length, 0, 'popover was removed') |
|||
}) |
|||
|
|||
QUnit.test('should detach popover content rather than removing it so that event handlers are left intact', function (assert) { |
|||
assert.expect(1) |
|||
var $content = $('<div class="content-with-handler"><a class="btn btn-warning">Button with event handler</a></div>').appendTo('#qunit-fixture') |
|||
|
|||
var handlerCalled = false |
|||
$('.content-with-handler .btn').on('click', function () { |
|||
handlerCalled = true |
|||
}) |
|||
|
|||
var $div = $('<div><a href="#">Show popover</a></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapPopover({ |
|||
html: true, |
|||
trigger: 'manual', |
|||
container: 'body', |
|||
content: function () { |
|||
return $content |
|||
} |
|||
}) |
|||
|
|||
var done = assert.async() |
|||
$div |
|||
.one('shown.bs.popover', function () { |
|||
$div |
|||
.one('hidden.bs.popover', function () { |
|||
$div |
|||
.one('shown.bs.popover', function () { |
|||
$('.content-with-handler .btn').trigger('click') |
|||
$div.bootstrapPopover('dispose') |
|||
assert.ok(handlerCalled, 'content\'s event handler still present') |
|||
done() |
|||
}) |
|||
.bootstrapPopover('show') |
|||
}) |
|||
.bootstrapPopover('hide') |
|||
}) |
|||
.bootstrapPopover('show') |
|||
}) |
|||
|
|||
QUnit.test('should do nothing when an attempt is made to hide an uninitialized popover', function (assert) { |
|||
assert.expect(1) |
|||
|
|||
var $popover = $('<span data-toggle="popover" data-title="some title" data-content="some content">some text</span>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.popover shown.bs.popover', function () { |
|||
assert.ok(false, 'should not fire any popover events') |
|||
}) |
|||
.bootstrapPopover('hide') |
|||
assert.strictEqual($popover.data('bs.popover'), undefined, 'should not initialize the popover') |
|||
}) |
|||
|
|||
QUnit.test('should fire inserted event', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<a href="#">@Johann-S</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('inserted.bs.popover', function () { |
|||
assert.notEqual($('.popover').length, 0, 'popover was inserted') |
|||
assert.ok(true, 'inserted event fired') |
|||
done() |
|||
}) |
|||
.bootstrapPopover({ |
|||
title: 'Test', |
|||
content: 'Test' |
|||
}) |
|||
.bootstrapPopover('show') |
|||
}) |
|||
|
|||
QUnit.test('should throw an error when show is called on hidden elements', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
try { |
|||
$('<div data-toggle="popover" data-title="some title" data-content="@Johann-S" style="display: none"/>').bootstrapPopover('show') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'Please use show on visible elements') |
|||
done() |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should hide popovers when their containing modal is closed', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var templateHTML = '<div id="modal-test" class="modal">' + |
|||
'<div class="modal-dialog" role="document">' + |
|||
'<div class="modal-content">' + |
|||
'<div class="modal-body">' + |
|||
'<button id="popover-test" type="button" class="btn btn-secondary" data-toggle="popover" data-placement="top" data-content="Popover">' + |
|||
'Popover on top' + |
|||
'</button>' + |
|||
'</div>' + |
|||
'</div>' + |
|||
'</div>' + |
|||
'</div>' |
|||
|
|||
$(templateHTML).appendTo('#qunit-fixture') |
|||
$('#popover-test') |
|||
.on('shown.bs.popover', function () { |
|||
$('#modal-test').modal('hide') |
|||
}) |
|||
.on('hide.bs.popover', function () { |
|||
assert.ok(true, 'popover hide') |
|||
done() |
|||
}) |
|||
|
|||
$('#modal-test') |
|||
.on('shown.bs.modal', function () { |
|||
$('#popover-test').bootstrapPopover('show') |
|||
}) |
|||
.modal('show') |
|||
}) |
|||
}) |
@ -0,0 +1,464 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('scrollspy plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).scrollspy, 'scrollspy method is defined') |
|||
}) |
|||
|
|||
QUnit.module('scrollspy', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapScrollspy = $.fn.scrollspy.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.scrollspy = $.fn.bootstrapScrollspy |
|||
delete $.fn.bootstrapScrollspy |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.scrollspy, undefined, 'scrollspy was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapScrollspy() |
|||
try { |
|||
$el.bootstrapScrollspy('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $scrollspy = $el.bootstrapScrollspy() |
|||
assert.ok($scrollspy instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($scrollspy[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should only switch "active" class on current target', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var sectionHTML = '<div id="root" class="active">' |
|||
+ '<div class="topbar">' |
|||
+ '<div class="topbar-inner">' |
|||
+ '<div class="container" id="ss-target">' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a href="#masthead">Overview</a></li>' |
|||
+ '<li><a href="#detail">Detail</a></li>' |
|||
+ '</ul>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div id="scrollspy-example" style="height: 100px; overflow: auto;">' |
|||
+ '<div style="height: 200px;">' |
|||
+ '<h4 id="masthead">Overview</h4>' |
|||
+ '<p style="height: 200px">' |
|||
+ 'Ad leggings keytar, brunch id art party dolor labore.' |
|||
+ '</p>' |
|||
+ '</div>' |
|||
+ '<div style="height: 200px;">' |
|||
+ '<h4 id="detail">Detail</h4>' |
|||
+ '<p style="height: 200px">' |
|||
+ 'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' |
|||
+ '</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $section = $(sectionHTML).appendTo('#qunit-fixture') |
|||
|
|||
var $scrollspy = $section |
|||
.show() |
|||
.find('#scrollspy-example') |
|||
.bootstrapScrollspy({ target: '#ss-target' }) |
|||
|
|||
$scrollspy.one('scroll', function () { |
|||
assert.ok($section.hasClass('active'), '"active" class still on root node') |
|||
done() |
|||
}) |
|||
|
|||
$scrollspy.scrollTop(350) |
|||
}) |
|||
|
|||
QUnit.test('should only switch "active" class on current target specified w element', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var sectionHTML = '<div id="root" class="active">' |
|||
+ '<div class="topbar">' |
|||
+ '<div class="topbar-inner">' |
|||
+ '<div class="container" id="ss-target">' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a href="#masthead">Overview</a></li>' |
|||
+ '<li><a href="#detail">Detail</a></li>' |
|||
+ '</ul>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '<div id="scrollspy-example" style="height: 100px; overflow: auto;">' |
|||
+ '<div style="height: 200px;">' |
|||
+ '<h4 id="masthead">Overview</h4>' |
|||
+ '<p style="height: 200px">' |
|||
+ 'Ad leggings keytar, brunch id art party dolor labore.' |
|||
+ '</p>' |
|||
+ '</div>' |
|||
+ '<div style="height: 200px;">' |
|||
+ '<h4 id="detail">Detail</h4>' |
|||
+ '<p style="height: 200px">' |
|||
+ 'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' |
|||
+ '</p>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
var $section = $(sectionHTML).appendTo('#qunit-fixture') |
|||
|
|||
var $scrollspy = $section |
|||
.show() |
|||
.find('#scrollspy-example') |
|||
.bootstrapScrollspy({ target: document.getElementById('#ss-target') }) |
|||
|
|||
$scrollspy.one('scroll', function () { |
|||
assert.ok($section.hasClass('active'), '"active" class still on root node') |
|||
done() |
|||
}) |
|||
|
|||
$scrollspy.scrollTop(350) |
|||
}) |
|||
|
|||
QUnit.test('should correctly select middle navigation option when large offset is used', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' |
|||
+ '<nav id="navigation" class="navbar">' |
|||
+ '<ul class="nav navbar-nav">' |
|||
+ '<li class="active"><a class="nav-link" id="one-link" href="#one">One</a></li>' |
|||
+ '<li><a class="nav-link" id="two-link" href="#two">Two</a></li>' |
|||
+ '<li><a class="nav-link" id="three-link" href="#three">Three</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
+ '<div id="content" style="height: 200px; overflow-y: auto;">' |
|||
+ '<div id="one" style="height: 500px;"></div>' |
|||
+ '<div id="two" style="height: 300px;"></div>' |
|||
+ '<div id="three" style="height: 10px;"></div>' |
|||
+ '</div>' |
|||
var $section = $(sectionHTML).appendTo('#qunit-fixture') |
|||
var $scrollspy = $section |
|||
.show() |
|||
.filter('#content') |
|||
|
|||
$scrollspy.bootstrapScrollspy({ target: '#navigation', offset: $scrollspy.position().top }) |
|||
|
|||
$scrollspy.one('scroll', function () { |
|||
assert.ok(!$section.find('#one-link').hasClass('active'), '"active" class removed from first section') |
|||
assert.ok($section.find('#two-link').hasClass('active'), '"active" class on middle section') |
|||
assert.ok(!$section.find('#three-link').hasClass('active'), '"active" class not on last section') |
|||
done() |
|||
}) |
|||
|
|||
$scrollspy.scrollTop(550) |
|||
}) |
|||
|
|||
QUnit.test('should add the active class to the correct element', function (assert) { |
|||
assert.expect(2) |
|||
var navbarHtml = |
|||
'<nav class="navbar">' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>' |
|||
+ '<li><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
var contentHtml = |
|||
'<div class="content" style="overflow: auto; height: 50px">' |
|||
+ '<div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>' |
|||
+ '<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' |
|||
+ '</div>' |
|||
|
|||
$(navbarHtml).appendTo('#qunit-fixture') |
|||
var $content = $(contentHtml) |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapScrollspy({ offset: 0, target: '.navbar' }) |
|||
|
|||
var done = assert.async() |
|||
var testElementIsActiveAfterScroll = function (element, target) { |
|||
var deferred = $.Deferred() |
|||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top) |
|||
$content.one('scroll', function () { |
|||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element' + element) |
|||
deferred.resolve() |
|||
}) |
|||
$content.scrollTop(scrollHeight) |
|||
return deferred.promise() |
|||
} |
|||
|
|||
$.when(testElementIsActiveAfterScroll('#a-1', '#div-1')) |
|||
.then(function () { return testElementIsActiveAfterScroll('#a-2', '#div-2') }) |
|||
.then(function () { done() }) |
|||
}) |
|||
|
|||
QUnit.test('should add the active class correctly when there are nested elements at 0 scroll offset', function (assert) { |
|||
assert.expect(6) |
|||
var times = 0 |
|||
var done = assert.async() |
|||
var navbarHtml = '<nav id="navigation" class="navbar">' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a id="a-1" class="nav-link" href="#div-1">div 1</a>' |
|||
+ '<ul>' |
|||
+ '<li><a id="a-2" class="nav-link" href="#div-2">div 2</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
|
|||
var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' |
|||
+ '<div id="div-1" style="padding: 0; margin: 0">' |
|||
+ '<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' |
|||
+ '</div>' |
|||
+ '</div>' |
|||
|
|||
$(navbarHtml).appendTo('#qunit-fixture') |
|||
|
|||
var $content = $(contentHtml) |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapScrollspy({ offset: 0, target: '#navigation' }) |
|||
|
|||
function testActiveElements() { |
|||
if (++times > 3) { return done() } |
|||
|
|||
$content.one('scroll', function () { |
|||
assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class') |
|||
assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class') |
|||
testActiveElements() |
|||
}) |
|||
|
|||
$content.scrollTop($content.scrollTop() + 10) |
|||
} |
|||
|
|||
testActiveElements() |
|||
}) |
|||
|
|||
QUnit.test('should clear selection if above the first section', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' |
|||
+ '<nav id="navigation" class="navbar">' |
|||
+ '<ul class="nav navbar-nav">' |
|||
+ '<li><a id="one-link" class="nav-link active" href="#one">One</a></li>' |
|||
+ '<li><a id="two-link" class="nav-link" href="#two">Two</a></li>' |
|||
+ '<li><a id="three-link" class="nav-link" href="#three">Three</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
$(sectionHTML).appendTo('#qunit-fixture') |
|||
|
|||
var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' |
|||
+ '<div id="spacer" style="height: 100px;"/>' |
|||
+ '<div id="one" style="height: 100px;"/>' |
|||
+ '<div id="two" style="height: 100px;"/>' |
|||
+ '<div id="three" style="height: 100px;"/>' |
|||
+ '<div id="spacer" style="height: 100px;"/>' |
|||
+ '</div>' |
|||
var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture') |
|||
|
|||
$scrollspy |
|||
.bootstrapScrollspy({ |
|||
target: '#navigation', |
|||
offset: $scrollspy.position().top |
|||
}) |
|||
.one('scroll', function () { |
|||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present') |
|||
assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section') |
|||
$scrollspy |
|||
.one('scroll', function () { |
|||
assert.strictEqual($('.active').length, 0, 'selection cleared') |
|||
done() |
|||
}) |
|||
.scrollTop(0) |
|||
}) |
|||
.scrollTop(201) |
|||
}) |
|||
|
|||
QUnit.test('should NOT clear selection if above the first section and first section is at the top', function (assert) { |
|||
assert.expect(4) |
|||
var done = assert.async() |
|||
|
|||
var sectionHTML = '<div id="header" style="height: 500px;"></div>' |
|||
+ '<nav id="navigation" class="navbar">' |
|||
+ '<ul class="nav navbar-nav">' |
|||
+ '<li><a id="one-link" class="nav-link active" href="#one">One</a></li>' |
|||
+ '<li><a id="two-link" class="nav-link" href="#two">Two</a></li>' |
|||
+ '<li><a id="three-link" class="nav-link" href="#three">Three</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
$(sectionHTML).appendTo('#qunit-fixture') |
|||
|
|||
var negativeHeight = -10 |
|||
var startOfSectionTwo = 101 |
|||
|
|||
var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' |
|||
+ '<div id="one" style="height: 100px;"/>' |
|||
+ '<div id="two" style="height: 100px;"/>' |
|||
+ '<div id="three" style="height: 100px;"/>' |
|||
+ '<div id="spacer" style="height: 100px;"/>' |
|||
+ '</div>' |
|||
var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture') |
|||
|
|||
$scrollspy |
|||
.bootstrapScrollspy({ |
|||
target: '#navigation', |
|||
offset: $scrollspy.position().top |
|||
}) |
|||
.one('scroll', function () { |
|||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present') |
|||
assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section') |
|||
$scrollspy |
|||
.one('scroll', function () { |
|||
assert.strictEqual($('.active').length, 1, '"active" class on only one element present') |
|||
assert.strictEqual($('.active').is('#one-link'), true, '"active" class on first section') |
|||
done() |
|||
}) |
|||
.scrollTop(negativeHeight) |
|||
}) |
|||
.scrollTop(startOfSectionTwo) |
|||
}) |
|||
|
|||
QUnit.test('should correctly select navigation element on backward scrolling when each target section height is 100%', function (assert) { |
|||
assert.expect(5) |
|||
var navbarHtml = |
|||
'<nav class="navbar">' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>' |
|||
+ '<li><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>' |
|||
+ '<li><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>' |
|||
+ '<li><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>' |
|||
+ '<li><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
var contentHtml = |
|||
'<div class="content" style="position: relative; overflow: auto; height: 100px">' |
|||
+ '<div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>' |
|||
+ '<div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>' |
|||
+ '<div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>' |
|||
+ '<div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>' |
|||
+ '<div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>' |
|||
+ '</div>' |
|||
|
|||
$(navbarHtml).appendTo('#qunit-fixture') |
|||
var $content = $(contentHtml) |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapScrollspy({ offset: 0, target: '.navbar' }) |
|||
|
|||
var testElementIsActiveAfterScroll = function (element, target) { |
|||
var deferred = $.Deferred() |
|||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top) |
|||
$content.one('scroll', function () { |
|||
assert.ok($(element).hasClass('active'), 'target:' + target + ', element: ' + element) |
|||
deferred.resolve() |
|||
}) |
|||
$content.scrollTop(scrollHeight) |
|||
return deferred.promise() |
|||
} |
|||
|
|||
var done = assert.async() |
|||
$.when(testElementIsActiveAfterScroll('#li-100-5', '#div-100-5')) |
|||
.then(function () { return testElementIsActiveAfterScroll('#li-100-4', '#div-100-4') }) |
|||
.then(function () { return testElementIsActiveAfterScroll('#li-100-3', '#div-100-3') }) |
|||
.then(function () { return testElementIsActiveAfterScroll('#li-100-2', '#div-100-2') }) |
|||
.then(function () { return testElementIsActiveAfterScroll('#li-100-1', '#div-100-1') }) |
|||
.then(function () { done() }) |
|||
}) |
|||
|
|||
QUnit.test('should allow passed in option offset method: offset', function (assert) { |
|||
assert.expect(4) |
|||
|
|||
var testOffsetMethod = function (type) { |
|||
var $navbar = $( |
|||
'<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a id="li-' + type + 'm-1" class="nav-link" href="#div-' + type + 'm-1">div 1</a></li>' |
|||
+ '<li><a id="li-' + type + 'm-2" class="nav-link" href="#div-' + type + 'm-2">div 2</a></li>' |
|||
+ '<li><a id="li-' + type + 'm-3" class="nav-link" href="#div-' + type + 'm-3">div 3</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
) |
|||
var $content = $( |
|||
'<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="offset"' : '') + ' style="position: relative; overflow: auto; height: 100px">' |
|||
+ '<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' |
|||
+ '<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' |
|||
+ '<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' |
|||
+ '</div>' |
|||
) |
|||
|
|||
$navbar.appendTo('#qunit-fixture') |
|||
$content.appendTo('#qunit-fixture') |
|||
|
|||
if (type === 'js') { |
|||
$content.bootstrapScrollspy({ target: '.navbar', offset: 0, method: 'offset' }) |
|||
} |
|||
else if (type === 'data') { |
|||
$(window).trigger('load') |
|||
} |
|||
|
|||
var $target = $('#div-' + type + 'm-2') |
|||
var scrollspy = $content.data('bs.scrollspy') |
|||
|
|||
assert.ok(scrollspy._offsets[1] === $target.offset().top, 'offset method with ' + type + ' option') |
|||
assert.ok(scrollspy._offsets[1] !== $target.position().top, 'position method with ' + type + ' option') |
|||
$navbar.remove() |
|||
$content.remove() |
|||
} |
|||
|
|||
testOffsetMethod('js') |
|||
testOffsetMethod('data') |
|||
}) |
|||
|
|||
QUnit.test('should allow passed in option offset method: position', function (assert) { |
|||
assert.expect(4) |
|||
|
|||
var testOffsetMethod = function (type) { |
|||
var $navbar = $( |
|||
'<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' |
|||
+ '<ul class="nav">' |
|||
+ '<li><a class="nav-link" id="li-' + type + 'm-1" href="#div-' + type + 'm-1">div 1</a></li>' |
|||
+ '<li><a class="nav-link" id="li-' + type + 'm-2" href="#div-' + type + 'm-2">div 2</a></li>' |
|||
+ '<li><a class="nav-link" id="li-' + type + 'm-3" href="#div-' + type + 'm-3">div 3</a></li>' |
|||
+ '</ul>' |
|||
+ '</nav>' |
|||
) |
|||
var $content = $( |
|||
'<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="position"' : '') + ' style="position: relative; overflow: auto; height: 100px">' |
|||
+ '<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' |
|||
+ '<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' |
|||
+ '<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' |
|||
+ '</div>' |
|||
) |
|||
|
|||
$navbar.appendTo('#qunit-fixture') |
|||
$content.appendTo('#qunit-fixture') |
|||
|
|||
if (type === 'js') { $content.bootstrapScrollspy({ target: '.navbar', offset: 0, method: 'position' }) } |
|||
else if (type === 'data') { $(window).trigger('load') } |
|||
|
|||
var $target = $('#div-' + type + 'm-2') |
|||
var scrollspy = $content.data('bs.scrollspy') |
|||
|
|||
assert.ok(scrollspy._offsets[1] !== $target.offset().top, 'offset method with ' + type + ' option') |
|||
assert.ok(scrollspy._offsets[1] === $target.position().top, 'position method with ' + type + ' option') |
|||
$navbar.remove() |
|||
$content.remove() |
|||
} |
|||
|
|||
testOffsetMethod('js') |
|||
testOffsetMethod('data') |
|||
}) |
|||
|
|||
}) |
@ -0,0 +1,330 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('tabs plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).tab, 'tabs method is defined') |
|||
}) |
|||
|
|||
QUnit.module('tabs', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapTab = $.fn.tab.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.tab = $.fn.bootstrapTab |
|||
delete $.fn.bootstrapTab |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.tab, undefined, 'tab was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapTab() |
|||
try { |
|||
$el.bootstrapTab('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $tab = $el.bootstrapTab() |
|||
assert.ok($tab instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($tab[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should activate element by tab id', function (assert) { |
|||
assert.expect(2) |
|||
var tabsHTML = '<ul class="tabs">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ul>' |
|||
|
|||
$('<ul><li id="home"/><li id="profile"/></ul>').appendTo('#qunit-fixture') |
|||
|
|||
$(tabsHTML).find('li:last a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile') |
|||
|
|||
$(tabsHTML).find('li:first a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home') |
|||
}) |
|||
|
|||
QUnit.test('should activate element by tab id', function (assert) { |
|||
assert.expect(2) |
|||
var pillsHTML = '<ul class="pills">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ul>' |
|||
|
|||
$('<ul><li id="home"/><li id="profile"/></ul>').appendTo('#qunit-fixture') |
|||
|
|||
$(pillsHTML).find('li:last a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile') |
|||
|
|||
$(pillsHTML).find('li:first a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home') |
|||
}) |
|||
|
|||
QUnit.test('should activate element by tab id in ordered list', function (assert) { |
|||
assert.expect(2) |
|||
var pillsHTML = '<ol class="pills">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ol>' |
|||
|
|||
$('<ol><li id="home"/><li id="profile"/></ol>').appendTo('#qunit-fixture') |
|||
|
|||
$(pillsHTML).find('li:last a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile') |
|||
|
|||
$(pillsHTML).find('li:first a').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home') |
|||
}) |
|||
|
|||
QUnit.test('should activate element by tab id in nav list', function (assert) { |
|||
assert.expect(2) |
|||
var tabsHTML = '<nav class="nav">' + |
|||
'<a href="#home">Home</a>' + |
|||
'<a href="#profile">Profile</a>' + |
|||
'</nav>' |
|||
|
|||
$('<nav><div id="home"></div><div id="profile"></div></nav>').appendTo('#qunit-fixture') |
|||
|
|||
$(tabsHTML).find('a:last').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'profile') |
|||
|
|||
$(tabsHTML).find('a:first').bootstrapTab('show') |
|||
assert.strictEqual($('#qunit-fixture').find('.active').attr('id'), 'home') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown when show is prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div class="tab"/>') |
|||
.on('show.bs.tab', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.on('shown.bs.tab', function () { |
|||
assert.ok(false, 'shown event fired') |
|||
}) |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown when tab is already active', function (assert) { |
|||
assert.expect(0) |
|||
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' |
|||
+ '<li class="nav-item"><a href="#home" class="nav-link active" role="tab">Home</a></li>' |
|||
+ '<li class="nav-item"><a href="#profile" class="nav-link" role="tab">Profile</a></li>' |
|||
+ '</ul>' |
|||
+ '<div class="tab-content">' |
|||
+ '<div class="tab-pane active" id="home" role="tabpanel"></div>' |
|||
+ '<div class="tab-pane" id="profile" role="tabpanel"></div>' |
|||
+ '</div>' |
|||
|
|||
$(tabsHTML) |
|||
.find('a.active') |
|||
.on('shown.bs.tab', function () { |
|||
assert.ok(true, 'shown event fired') |
|||
}) |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown when tab is disabled', function (assert) { |
|||
assert.expect(0) |
|||
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' |
|||
+ '<li class="nav-item"><a href="#home" class="nav-link active" role="tab">Home</a></li>' |
|||
+ '<li class="nav-item"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>' |
|||
+ '</ul>' |
|||
+ '<div class="tab-content">' |
|||
+ '<div class="tab-pane active" id="home" role="tabpanel"></div>' |
|||
+ '<div class="tab-pane" id="profile" role="tabpanel"></div>' |
|||
+ '</div>' |
|||
|
|||
$(tabsHTML) |
|||
.find('a.disabled') |
|||
.on('shown.bs.tab', function () { |
|||
assert.ok(true, 'shown event fired') |
|||
}) |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('show and shown events should reference correct relatedTarget', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var dropHTML = '<ul class="drop">' |
|||
+ '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>' |
|||
+ '<ul class="dropdown-menu">' |
|||
+ '<li><a href="#1-1" data-toggle="tab">1-1</a></li>' |
|||
+ '<li><a href="#1-2" data-toggle="tab">1-2</a></li>' |
|||
+ '</ul>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
|
|||
$(dropHTML) |
|||
.find('ul > li:first a') |
|||
.bootstrapTab('show') |
|||
.end() |
|||
.find('ul > li:last a') |
|||
.on('show.bs.tab', function (e) { |
|||
assert.strictEqual(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget') |
|||
}) |
|||
.on('shown.bs.tab', function (e) { |
|||
assert.strictEqual(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget') |
|||
done() |
|||
}) |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('should fire hide and hidden events', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var tabsHTML = '<ul class="tabs">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ul>' |
|||
|
|||
$(tabsHTML) |
|||
.find('li:first a') |
|||
.on('hide.bs.tab', function () { |
|||
assert.ok(true, 'hide event fired') |
|||
}) |
|||
.bootstrapTab('show') |
|||
.end() |
|||
.find('li:last a') |
|||
.bootstrapTab('show') |
|||
|
|||
$(tabsHTML) |
|||
.find('li:first a') |
|||
.on('hidden.bs.tab', function () { |
|||
assert.ok(true, 'hidden event fired') |
|||
done() |
|||
}) |
|||
.bootstrapTab('show') |
|||
.end() |
|||
.find('li:last a') |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire hidden when hide is prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var tabsHTML = '<ul class="tabs">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ul>' |
|||
|
|||
$(tabsHTML) |
|||
.find('li:first a') |
|||
.on('hide.bs.tab', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'hide event fired') |
|||
done() |
|||
}) |
|||
.on('hidden.bs.tab', function () { |
|||
assert.ok(false, 'hidden event fired') |
|||
}) |
|||
.bootstrapTab('show') |
|||
.end() |
|||
.find('li:last a') |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('hide and hidden events contain correct relatedTarget', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var tabsHTML = '<ul class="tabs">' |
|||
+ '<li><a href="#home">Home</a></li>' |
|||
+ '<li><a href="#profile">Profile</a></li>' |
|||
+ '</ul>' |
|||
|
|||
$(tabsHTML) |
|||
.find('li:first a') |
|||
.on('hide.bs.tab', function (e) { |
|||
assert.strictEqual(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget') |
|||
}) |
|||
.on('hidden.bs.tab', function (e) { |
|||
assert.strictEqual(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget') |
|||
done() |
|||
}) |
|||
.bootstrapTab('show') |
|||
.end() |
|||
.find('li:last a') |
|||
.bootstrapTab('show') |
|||
}) |
|||
|
|||
QUnit.test('selected tab should have aria-expanded', function (assert) { |
|||
assert.expect(8) |
|||
var tabsHTML = '<ul class="nav nav-tabs">' |
|||
+ '<li><a class="nav-item active" href="#home" toggle="tab" aria-expanded="true">Home</a></li>' |
|||
+ '<li><a class="nav-item" href="#profile" toggle="tab" aria-expanded="false">Profile</a></li>' |
|||
+ '</ul>' |
|||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture') |
|||
|
|||
$tabs.find('li:first a').bootstrapTab('show') |
|||
assert.strictEqual($tabs.find('.active').attr('aria-expanded'), 'true', 'shown tab has aria-expanded = true') |
|||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-expanded'), 'false', 'hidden tab has aria-expanded = false') |
|||
|
|||
$tabs.find('li:last a').trigger('click') |
|||
assert.strictEqual($tabs.find('.active').attr('aria-expanded'), 'true', 'after click, shown tab has aria-expanded = true') |
|||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-expanded'), 'false', 'after click, hidden tab has aria-expanded = false') |
|||
|
|||
$tabs.find('li:first a').bootstrapTab('show') |
|||
assert.strictEqual($tabs.find('.active').attr('aria-expanded'), 'true', 'shown tab has aria-expanded = true') |
|||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-expanded'), 'false', 'hidden tab has aria-expanded = false') |
|||
|
|||
$tabs.find('li:first a').trigger('click') |
|||
assert.strictEqual($tabs.find('.active').attr('aria-expanded'), 'true', 'after second show event, shown tab still has aria-expanded = true') |
|||
assert.strictEqual($tabs.find('a:not(.active)').attr('aria-expanded'), 'false', 'after second show event, hidden tab has aria-expanded = false') |
|||
}) |
|||
|
|||
QUnit.test('selected tab should deactivate previous selected tab', function (assert) { |
|||
assert.expect(2) |
|||
var tabsHTML = '<ul class="nav nav-tabs">' |
|||
+ '<li class="nav-item"><a class="nav-link active" href="#home" data-toggle="tab">Home</a></li>' |
|||
+ '<li class="nav-item"><a class="nav-link" href="#profile" data-toggle="tab">Profile</a></li>' |
|||
+ '</ul>' |
|||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture') |
|||
|
|||
$tabs.find('li:last a').trigger('click') |
|||
assert.notOk($tabs.find('li:first a').hasClass('active')) |
|||
assert.ok($tabs.find('li:last a').hasClass('active')) |
|||
}) |
|||
|
|||
QUnit.test('selected tab should deactivate previous selected link in dropdown', function (assert) { |
|||
assert.expect(3) |
|||
var tabsHTML = '<ul class="nav nav-tabs">' |
|||
+ '<li class="nav-item"><a class="nav-link" href="#home" data-toggle="tab">Home</a></li>' |
|||
+ '<li class="nav-item"><a class="nav-link" href="#profile" data-toggle="tab">Profile</a></li>' |
|||
+ '<li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" data-toggle="dropdown" href="#">Dropdown</a>' |
|||
+ '<div class="dropdown-menu">' |
|||
+ '<a class="dropdown-item active" href="#dropdown1" id="dropdown1-tab" data-toggle="tab">@fat</a>' |
|||
+ '<a class="dropdown-item" href="#dropdown2" id="dropdown2-tab" data-toggle="tab">@mdo</a>' |
|||
+ '</div>' |
|||
+ '</li>' |
|||
+ '</ul>' |
|||
var $tabs = $(tabsHTML).appendTo('#qunit-fixture') |
|||
|
|||
$tabs.find('li:first > a').trigger('click') |
|||
assert.ok($tabs.find('li:first a').hasClass('active')) |
|||
assert.notOk($tabs.find('li:last > a').hasClass('active')) |
|||
assert.notOk($tabs.find('li:last > .dropdown-menu > a:first').hasClass('active')) |
|||
}) |
|||
}) |
@ -0,0 +1,872 @@ |
|||
$(function () { |
|||
'use strict' |
|||
|
|||
QUnit.module('tooltip plugin') |
|||
|
|||
QUnit.test('should be defined on jquery object', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($(document.body).tooltip, 'tooltip method is defined') |
|||
}) |
|||
|
|||
QUnit.module('tooltip', { |
|||
beforeEach: function () { |
|||
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
|
|||
$.fn.bootstrapTooltip = $.fn.tooltip.noConflict() |
|||
}, |
|||
afterEach: function () { |
|||
$.fn.tooltip = $.fn.bootstrapTooltip |
|||
delete $.fn.bootstrapTooltip |
|||
$('.tooltip').remove() |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should provide no conflict', function (assert) { |
|||
assert.expect(1) |
|||
assert.strictEqual($.fn.tooltip, undefined, 'tooltip was set back to undefined (org value)') |
|||
}) |
|||
|
|||
QUnit.test('should throw explicit error on undefined method', function (assert) { |
|||
assert.expect(1) |
|||
var $el = $('<div/>') |
|||
$el.bootstrapTooltip() |
|||
try { |
|||
$el.bootstrapTooltip('noMethod') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'No method named "noMethod"') |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should return jquery collection containing the element', function (assert) { |
|||
assert.expect(2) |
|||
var $el = $('<div/>') |
|||
var $tooltip = $el.bootstrapTooltip() |
|||
assert.ok($tooltip instanceof $, 'returns jquery collection') |
|||
assert.strictEqual($tooltip[0], $el[0], 'collection contains element') |
|||
}) |
|||
|
|||
QUnit.test('should expose default settings', function (assert) { |
|||
assert.expect(1) |
|||
assert.ok($.fn.bootstrapTooltip.Constructor.Default, 'defaults is defined') |
|||
}) |
|||
|
|||
QUnit.test('should empty title attribute', function (assert) { |
|||
assert.expect(1) |
|||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip() |
|||
assert.strictEqual($trigger.attr('title'), '', 'title attribute was emptied') |
|||
}) |
|||
|
|||
QUnit.test('should add data attribute for referencing original title', function (assert) { |
|||
assert.expect(1) |
|||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip() |
|||
assert.strictEqual($trigger.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute') |
|||
}) |
|||
|
|||
QUnit.test('should add aria-describedby to the trigger on show', function (assert) { |
|||
assert.expect(3) |
|||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.bootstrapTooltip() |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip('show') |
|||
|
|||
var id = $('.tooltip').attr('id') |
|||
|
|||
assert.strictEqual($('#' + id).length, 1, 'has a unique id') |
|||
assert.strictEqual($('.tooltip').attr('aria-describedby'), $trigger.attr('id'), 'tooltip id and aria-describedby on trigger match') |
|||
assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby') |
|||
}) |
|||
|
|||
QUnit.test('should remove aria-describedby from trigger on hide', function (assert) { |
|||
assert.expect(2) |
|||
var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.bootstrapTooltip() |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$trigger.bootstrapTooltip('show') |
|||
assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby') |
|||
|
|||
$trigger.bootstrapTooltip('hide') |
|||
assert.ok(!$trigger[0].hasAttribute('aria-describedby'), 'trigger does not have aria-describedby') |
|||
}) |
|||
|
|||
QUnit.test('should assign a unique id tooltip element', function (assert) { |
|||
assert.expect(2) |
|||
$('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip('show') |
|||
|
|||
var id = $('.tooltip').attr('id') |
|||
|
|||
assert.strictEqual($('#' + id).length, 1, 'tooltip has unique id') |
|||
assert.strictEqual(id.indexOf('tooltip'), 0, 'tooltip id has prefix') |
|||
}) |
|||
|
|||
QUnit.test('should place tooltips relative to placement option', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ placement: 'bottom' }) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
|
|||
assert |
|||
.ok($('.tooltip') |
|||
.is('.fade.bs-tether-element-attached-top.bs-tether-element-attached-center.show'), 'has correct classes applied') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
|
|||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed') |
|||
}) |
|||
|
|||
QUnit.test('should allow html entities', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="<b>@fat</b>"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ html: true }) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
assert.notEqual($('.tooltip b').length, 0, 'b tag was inserted') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed') |
|||
}) |
|||
|
|||
QUnit.test('should allow DOMElement title (html: false)', function (assert) { |
|||
assert.expect(3) |
|||
var title = document.createTextNode('<3 writing tests') |
|||
var $tooltip = $('<a href="#" rel="tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ title: title }) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
|
|||
assert.notEqual($('.tooltip').length, 0, 'tooltip inserted') |
|||
assert.strictEqual($('.tooltip').text(), '<3 writing tests', 'title inserted') |
|||
assert.ok(!$.contains($('.tooltip').get(0), title), 'title node copied, not moved') |
|||
}) |
|||
|
|||
QUnit.test('should allow DOMElement title (html: true)', function (assert) { |
|||
assert.expect(3) |
|||
var title = document.createTextNode('<3 writing tests') |
|||
var $tooltip = $('<a href="#" rel="tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ html: true, title: title }) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
|
|||
assert.notEqual($('.tooltip').length, 0, 'tooltip inserted') |
|||
assert.strictEqual($('.tooltip').text(), '<3 writing tests', 'title inserted') |
|||
assert.ok($.contains($('.tooltip').get(0), title), 'title node moved, not copied') |
|||
}) |
|||
|
|||
|
|||
QUnit.test('should respect custom classes', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>' }) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
assert.ok($('.tooltip').hasClass('some-class'), 'custom class is present') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($tooltip.data('bs.tooltip').tip.parentNode, null, 'tooltip removed') |
|||
}) |
|||
|
|||
QUnit.test('should fire show event', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.on('show.bs.tooltip', function () { |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should throw an error when show is called on hidden elements', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
try { |
|||
$('<div title="tooltip title" style="display: none"/>').bootstrapTooltip('show') |
|||
} |
|||
catch (err) { |
|||
assert.strictEqual(err.message, 'Please use show on visible elements') |
|||
done() |
|||
} |
|||
}) |
|||
|
|||
QUnit.test('should fire inserted event', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('inserted.bs.tooltip', function () { |
|||
assert.notEqual($('.tooltip').length, 0, 'tooltip was inserted') |
|||
assert.ok(true, 'inserted event fired') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should fire shown event', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"></div>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.tooltip', function () { |
|||
assert.ok(true, 'shown was called') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire shown event when show was prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.on('show.bs.tooltip', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'show event fired') |
|||
done() |
|||
}) |
|||
.on('shown.bs.tooltip', function () { |
|||
assert.ok(false, 'shown event fired') |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should fire hide event', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.tooltip', function () { |
|||
$(this).bootstrapTooltip('hide') |
|||
}) |
|||
.on('hide.bs.tooltip', function () { |
|||
assert.ok(true, 'hide event fired') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should fire hidden event', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.tooltip', function () { |
|||
$(this).bootstrapTooltip('hide') |
|||
}) |
|||
.on('hidden.bs.tooltip', function () { |
|||
assert.ok(true, 'hidden event fired') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should not fire hidden event when hide was prevented', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
$('<div title="tooltip title"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('shown.bs.tooltip', function () { |
|||
$(this).bootstrapTooltip('hide') |
|||
}) |
|||
.on('hide.bs.tooltip', function (e) { |
|||
e.preventDefault() |
|||
assert.ok(true, 'hide event fired') |
|||
done() |
|||
}) |
|||
.on('hidden.bs.tooltip', function () { |
|||
assert.ok(false, 'hidden event fired') |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should destroy tooltip', function (assert) { |
|||
assert.expect(7) |
|||
var $tooltip = $('<div/>') |
|||
.bootstrapTooltip() |
|||
.on('click.foo', function () {}) |
|||
|
|||
assert.ok($tooltip.data('bs.tooltip'), 'tooltip has data') |
|||
assert.ok($._data($tooltip[0], 'events').mouseover && $._data($tooltip[0], 'events').mouseout, 'tooltip has hover events') |
|||
assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip has extra click.foo event') |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
$tooltip.bootstrapTooltip('dispose') |
|||
|
|||
assert.ok(!$tooltip.hasClass('show'), 'tooltip is hidden') |
|||
assert.ok(!$._data($tooltip[0], 'bs.tooltip'), 'tooltip does not have data') |
|||
assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip still has click.foo') |
|||
assert.ok(!$._data($tooltip[0], 'events').mouseover && !$._data($tooltip[0], 'events').mouseout, 'tooltip does not have hover events') |
|||
}) |
|||
|
|||
// QUnit.test('should show tooltip with delegate selector on click', function (assert) {
|
|||
// assert.expect(2)
|
|||
// var $div = $('<div><a href="#" rel="tooltip" title="Another tooltip"/></div>')
|
|||
// .appendTo('#qunit-fixture')
|
|||
// .bootstrapTooltip({
|
|||
// selector: 'a[rel="tooltip"]',
|
|||
// trigger: 'click'
|
|||
// })
|
|||
|
|||
// $div.find('a').trigger('click')
|
|||
// assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
|
|||
|
|||
// $div.find('a').trigger('click')
|
|||
// assert.strictEqual($div.data('bs.tooltip').tip.parentNode, null, 'tooltip removed')
|
|||
// })
|
|||
|
|||
QUnit.test('should show tooltip when toggle is called', function (assert) { |
|||
assert.expect(1) |
|||
$('<a href="#" rel="tooltip" title="tooltip on toggle"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ trigger: 'manual' }) |
|||
.bootstrapTooltip('toggle') |
|||
|
|||
assert.ok($('.tooltip').is('.fade.show'), 'tooltip is faded active') |
|||
}) |
|||
|
|||
QUnit.test('should hide previously shown tooltip when toggle is called on tooltip', function (assert) { |
|||
assert.expect(1) |
|||
$('<a href="#" rel="tooltip" title="tooltip on toggle">@ResentedHook</a>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ trigger: 'manual' }) |
|||
.bootstrapTooltip('show') |
|||
|
|||
$('.tooltip').bootstrapTooltip('toggle') |
|||
assert.ok($('.tooltip').not('.fade.show'), 'tooltip was faded out') |
|||
}) |
|||
|
|||
QUnit.test('should place tooltips inside body when container is body', function (assert) { |
|||
assert.expect(3) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ container: 'body' }) |
|||
.bootstrapTooltip('show') |
|||
|
|||
assert.notEqual($('body > .tooltip').length, 0, 'tooltip is direct descendant of body') |
|||
assert.strictEqual($('#qunit-fixture > .tooltip').length, 0, 'tooltip is not in parent') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($('body > .tooltip').length, 0, 'tooltip was removed from dom') |
|||
}) |
|||
|
|||
QUnit.test('should add position class before positioning so that position-specific styles are taken into account', function (assert) { |
|||
assert.expect(1) |
|||
var styles = '<style>' |
|||
+ '.tooltip.right { white-space: nowrap; }' |
|||
+ '.tooltip.right .tooltip-inner { max-width: none; }' |
|||
+ '</style>' |
|||
var $styles = $(styles).appendTo('head') |
|||
|
|||
var $container = $('<div/>').appendTo('#qunit-fixture') |
|||
var $target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"/>') |
|||
.appendTo($container) |
|||
.bootstrapTooltip({ |
|||
placement: 'right' |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
|
|||
var $tooltip = $($target.data('bs.tooltip').tip) |
|||
|
|||
// this is some dumb hack stuff because sub pixels in firefox
|
|||
var top = Math.round($target.offset().top + $target[0].offsetHeight / 2 - $tooltip[0].offsetHeight / 2) |
|||
var top2 = Math.round($tooltip.offset().top) |
|||
var topDiff = top - top2 |
|||
assert.ok(topDiff <= 1 && topDiff >= -1) |
|||
$target.bootstrapTooltip('hide') |
|||
|
|||
$container.remove() |
|||
$styles.remove() |
|||
}) |
|||
|
|||
QUnit.test('should use title attribute for tooltip text', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip() |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') |
|||
}) |
|||
|
|||
QUnit.test('should prefer title attribute over title option', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ |
|||
title: 'This is a tooltip with some content' |
|||
}) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while preferred over title option') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') |
|||
}) |
|||
|
|||
QUnit.test('should use title option', function (assert) { |
|||
assert.expect(2) |
|||
var $tooltip = $('<a href="#" rel="tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ |
|||
title: 'This is a tooltip with some content' |
|||
}) |
|||
|
|||
$tooltip.bootstrapTooltip('show') |
|||
assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set') |
|||
|
|||
$tooltip.bootstrapTooltip('hide') |
|||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') |
|||
}) |
|||
|
|||
QUnit.test('should not error when trying to show an top-placed tooltip that has been removed from the dom', function (assert) { |
|||
assert.expect(1) |
|||
var passed = true |
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.one('show.bs.tooltip', function () { |
|||
$(this).remove() |
|||
}) |
|||
.bootstrapTooltip({ placement: 'top' }) |
|||
|
|||
try { |
|||
$tooltip.bootstrapTooltip('show') |
|||
} catch (err) { |
|||
passed = false |
|||
console.log(err) |
|||
} |
|||
|
|||
assert.ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom') |
|||
}) |
|||
|
|||
QUnit.test('should place tooltip on top of element', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
|
|||
var containerHTML = '<div>' |
|||
+ '<p style="margin-top: 200px">' |
|||
+ '<a href="#" title="very very very very very very very long tooltip">Hover me</a>' |
|||
+ '</p>' |
|||
+ '</div>' |
|||
|
|||
var $container = $(containerHTML) |
|||
.css({ |
|||
position: 'absolute', |
|||
bottom: 0, |
|||
left: 0, |
|||
textAlign: 'right', |
|||
width: 300, |
|||
height: 300 |
|||
}) |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
var $trigger = $container |
|||
.find('a') |
|||
.css('margin-top', 200) |
|||
.bootstrapTooltip({ |
|||
placement: 'top', |
|||
animate: false |
|||
}) |
|||
.bootstrapTooltip('show') |
|||
|
|||
var $tooltip = $($trigger.data('bs.tooltip').tip) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top)) |
|||
done() |
|||
}, 0) |
|||
}) |
|||
|
|||
QUnit.test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: 150 }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip is not faded active') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($('.tooltip').is('.fade.show'), '200ms: tooltip is faded active') |
|||
done() |
|||
}, 200) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: 150 }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active') |
|||
$tooltip.trigger('mouseout') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '200ms: tooltip not faded active') |
|||
done() |
|||
}, 200) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: { show: 0, hide: 150 } }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($('.tooltip').is('.fade.show'), '1ms: tooltip faded active') |
|||
$tooltip.trigger('mouseout') |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($('.tooltip').is('.fade.show'), '100ms: tooltip still faded active') |
|||
$tooltip.trigger('mouseenter') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($('.tooltip').is('.fade.show'), '200ms: tooltip still faded active') |
|||
done() |
|||
}, 200) |
|||
}, 0) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: 150 }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active') |
|||
$tooltip.trigger('mouseout') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '200ms: tooltip not faded active') |
|||
done() |
|||
}, 200) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) { |
|||
assert.expect(2) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: { show: 150, hide: 0 } }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '100ms: tooltip not faded active') |
|||
$tooltip.trigger('mouseout') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$('.tooltip').is('.fade.show'), '250ms: tooltip not faded active') |
|||
done() |
|||
}, 250) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should wait 200ms before hiding the tooltip', function (assert) { |
|||
assert.expect(3) |
|||
var done = assert.async() |
|||
|
|||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ delay: { show: 0, hide: 150 } }) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($($tooltip.data('bs.tooltip').tip).is('.fade.show'), '1ms: tooltip faded active') |
|||
|
|||
$tooltip.trigger('mouseout') |
|||
|
|||
setTimeout(function () { |
|||
assert.ok($($tooltip.data('bs.tooltip').tip).is('.fade.show'), '100ms: tooltip still faded active') |
|||
}, 100) |
|||
|
|||
setTimeout(function () { |
|||
assert.ok(!$($tooltip.data('bs.tooltip').tip).is('.show'), '200ms: tooltip removed') |
|||
done() |
|||
}, 200) |
|||
|
|||
}, 0) |
|||
|
|||
$tooltip.trigger('mouseenter') |
|||
}) |
|||
|
|||
QUnit.test('should correctly position tooltips on SVG elements', function (assert) { |
|||
if (!window.SVGElement) { |
|||
// Skip IE8 since it doesn't support SVG
|
|||
assert.expect(0) |
|||
return |
|||
} |
|||
assert.expect(2) |
|||
|
|||
var done = assert.async() |
|||
|
|||
var styles = '<style>' |
|||
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' |
|||
+ '.tooltip { position: absolute; }' |
|||
+ '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }' |
|||
+ '</style>' |
|||
var $styles = $(styles).appendTo('head') |
|||
|
|||
$('#qunit-fixture').append( |
|||
'<div style="position: fixed; top: 0; left: 0;">' |
|||
+ ' <svg width="200" height="200">' |
|||
+ ' <circle cx="100" cy="100" r="10" title="m" id="theCircle" />' |
|||
+ ' </svg>' |
|||
+ '</div>') |
|||
var $circle = $('#theCircle') |
|||
|
|||
$circle |
|||
.on('shown.bs.tooltip', function () { |
|||
var offset = $('.tooltip').offset() |
|||
$styles.remove() |
|||
assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location') |
|||
$circle.bootstrapTooltip('hide') |
|||
assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip({ placement: 'top', trigger: 'manual' }) |
|||
|
|||
$circle.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should not reload the tooltip on subsequent mouseenter events', function (assert) { |
|||
assert.expect(1) |
|||
var titleHtml = function () { |
|||
var uid = Util.getUID('tooltip') |
|||
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>' |
|||
} |
|||
|
|||
var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>') |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$tooltip.bootstrapTooltip({ |
|||
html: true, |
|||
animation: false, |
|||
trigger: 'hover', |
|||
delay: { show: 0, hide: 500 }, |
|||
container: $tooltip, |
|||
title: titleHtml |
|||
}) |
|||
|
|||
$('#tt-outer').trigger('mouseenter') |
|||
|
|||
var currentUid = $('#tt-content').text() |
|||
|
|||
$('#tt-content').trigger('mouseenter') |
|||
assert.strictEqual(currentUid, $('#tt-content').text()) |
|||
}) |
|||
|
|||
QUnit.test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function (assert) { |
|||
assert.expect(4) |
|||
|
|||
var titleHtml = function () { |
|||
var uid = Util.getUID('tooltip') |
|||
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>' |
|||
} |
|||
|
|||
var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>') |
|||
.appendTo('#qunit-fixture') |
|||
|
|||
$tooltip.bootstrapTooltip({ |
|||
html: true, |
|||
animation: false, |
|||
trigger: 'hover', |
|||
delay: { show: 0, hide: 500 }, |
|||
title: titleHtml |
|||
}) |
|||
|
|||
var obj = $tooltip.data('bs.tooltip') |
|||
|
|||
$('#tt-outer').trigger('mouseenter') |
|||
|
|||
var currentUid = $('#tt-content').text() |
|||
|
|||
$('#tt-outer').trigger('mouseleave') |
|||
assert.strictEqual(currentUid, $('#tt-content').text()) |
|||
|
|||
assert.ok(obj._hoverState === 'out', 'the tooltip hoverState should be set to "out"') |
|||
|
|||
$('#tt-outer').trigger('mouseenter') |
|||
assert.ok(obj._hoverState === 'show', 'the tooltip hoverState should be set to "show"') |
|||
|
|||
assert.strictEqual(currentUid, $('#tt-content').text()) |
|||
}) |
|||
|
|||
QUnit.test('should correctly position tooltips on transformed elements', function (assert) { |
|||
var styleProps = document.documentElement.style |
|||
if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) { |
|||
assert.expect(0) |
|||
return |
|||
} |
|||
assert.expect(2) |
|||
|
|||
var done = assert.async() |
|||
|
|||
var styles = '<style>' |
|||
+ '#qunit-fixture { top: 0; left: 0; }' |
|||
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' |
|||
+ '.tooltip { position: absolute; }' |
|||
+ '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }' |
|||
+ '#target { position: absolute; top: 100px; left: 50px; width: 100px; height: 200px; -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); }' |
|||
+ '</style>' |
|||
var $styles = $(styles).appendTo('head') |
|||
|
|||
var $element = $('<div id="target" title="1"/>').appendTo('#qunit-fixture') |
|||
|
|||
$element |
|||
.on('shown.bs.tooltip', function () { |
|||
var offset = $('.tooltip').offset() |
|||
$styles.remove() |
|||
assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location') |
|||
assert.ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location') |
|||
$element.bootstrapTooltip('hide') |
|||
done() |
|||
}) |
|||
.bootstrapTooltip({ |
|||
trigger: 'manual' |
|||
}) |
|||
|
|||
$element.bootstrapTooltip('show') |
|||
}) |
|||
|
|||
QUnit.test('should do nothing when an attempt is made to hide an uninitialized tooltip', function (assert) { |
|||
assert.expect(1) |
|||
|
|||
var $tooltip = $('<span data-toggle="tooltip" title="some tip">some text</span>') |
|||
.appendTo('#qunit-fixture') |
|||
.on('hidden.bs.tooltip shown.bs.tooltip', function () { |
|||
assert.ok(false, 'should not fire any tooltip events') |
|||
}) |
|||
.bootstrapTooltip('hide') |
|||
assert.strictEqual($tooltip.data('bs.tooltip'), undefined, 'should not initialize the tooltip') |
|||
}) |
|||
|
|||
QUnit.test('should not remove tooltip if multiple triggers are set and one is still active', function (assert) { |
|||
assert.expect(41) |
|||
var $el = $('<button>Trigger</button>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ trigger: 'click hover focus', animation: false }) |
|||
var tooltip = $el.data('bs.tooltip') |
|||
var $tooltip = $(tooltip.getTipElement()) |
|||
|
|||
function showingTooltip() { return $tooltip.hasClass('show') || tooltip._hoverState === 'show' } |
|||
|
|||
var tests = [ |
|||
['mouseenter', 'mouseleave'], |
|||
|
|||
['focusin', 'focusout'], |
|||
|
|||
['click', 'click'], |
|||
|
|||
['mouseenter', 'focusin', 'focusout', 'mouseleave'], |
|||
['mouseenter', 'focusin', 'mouseleave', 'focusout'], |
|||
|
|||
['focusin', 'mouseenter', 'mouseleave', 'focusout'], |
|||
['focusin', 'mouseenter', 'focusout', 'mouseleave'], |
|||
|
|||
['click', 'focusin', 'mouseenter', 'focusout', 'mouseleave', 'click'], |
|||
['mouseenter', 'click', 'focusin', 'focusout', 'mouseleave', 'click'], |
|||
['mouseenter', 'focusin', 'click', 'click', 'mouseleave', 'focusout'] |
|||
] |
|||
|
|||
assert.ok(!showingTooltip()) |
|||
|
|||
$.each(tests, function (idx, triggers) { |
|||
for (var i = 0, len = triggers.length; i < len; i++) { |
|||
$el.trigger(triggers[i]) |
|||
assert.equal(i < len - 1, showingTooltip()) |
|||
} |
|||
}) |
|||
}) |
|||
|
|||
QUnit.test('should show on first trigger after hide', function (assert) { |
|||
assert.expect(3) |
|||
var $el = $('<a href="#" rel="tooltip" title="Test tooltip"/>') |
|||
.appendTo('#qunit-fixture') |
|||
.bootstrapTooltip({ trigger: 'click hover focus', animation: false }) |
|||
|
|||
var tooltip = $el.data('bs.tooltip') |
|||
var $tooltip = $(tooltip.getTipElement()) |
|||
|
|||
function showingTooltip() { return $tooltip.hasClass('show') || tooltip._hoverState === 'show' } |
|||
|
|||
$el.trigger('click') |
|||
assert.ok(showingTooltip(), 'tooltip is faded in') |
|||
|
|||
$el.bootstrapTooltip('hide') |
|||
assert.ok(!showingTooltip(), 'tooltip was faded out') |
|||
|
|||
$el.trigger('click') |
|||
assert.ok(showingTooltip(), 'tooltip is faded in again') |
|||
}) |
|||
|
|||
QUnit.test('should hide tooltip when their containing modal is closed', function (assert) { |
|||
assert.expect(1) |
|||
var done = assert.async() |
|||
var templateHTML = '<div id="modal-test" class="modal">' + |
|||
'<div class="modal-dialog" role="document">' + |
|||
'<div class="modal-content">' + |
|||
'<div class="modal-body">' + |
|||
'<a id="tooltipTest" href="#" data-toggle="tooltip" title="Some tooltip text!">Tooltip</a>' + |
|||
'</div>' + |
|||
'</div>' + |
|||
'</div>' + |
|||
'</div>' |
|||
|
|||
$(templateHTML).appendTo('#qunit-fixture') |
|||
$('#tooltipTest') |
|||
.bootstrapTooltip({ trigger: 'manuel' }) |
|||
.on('shown.bs.tooltip', function () { |
|||
$('#modal-test').modal('hide') |
|||
}) |
|||
.on('hide.bs.tooltip', function () { |
|||
assert.ok(true, 'tooltip hide') |
|||
done() |
|||
}) |
|||
|
|||
$('#modal-test') |
|||
.on('shown.bs.modal', function () { |
|||
$('#tooltipTest').bootstrapTooltip('show') |
|||
}) |
|||
.modal('show') |
|||
}) |
|||
}) |
File diff suppressed because it is too large
@ -0,0 +1,143 @@ |
|||
{ |
|||
"_from": "bootstrap@4.0.0-alpha.6", |
|||
"_id": "bootstrap@4.0.0-alpha.6", |
|||
"_inBundle": false, |
|||
"_integrity": "sha1-T1TdM6wN6sOyhAe8LffsYIhpycg=", |
|||
"_location": "/bootstrap", |
|||
"_phantomChildren": {}, |
|||
"_requested": { |
|||
"type": "version", |
|||
"registry": true, |
|||
"raw": "bootstrap@4.0.0-alpha.6", |
|||
"name": "bootstrap", |
|||
"escapedName": "bootstrap", |
|||
"rawSpec": "4.0.0-alpha.6", |
|||
"saveSpec": null, |
|||
"fetchSpec": "4.0.0-alpha.6" |
|||
}, |
|||
"_requiredBy": [ |
|||
"#USER", |
|||
"/" |
|||
], |
|||
"_resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.0.0-alpha.6.tgz", |
|||
"_shasum": "4f54dd33ac0deac3b28407bc2df7ec608869c9c8", |
|||
"_spec": "bootstrap@4.0.0-alpha.6", |
|||
"_where": "/var/www/htdocs/ordine", |
|||
"author": { |
|||
"name": "The Bootstrap Authors", |
|||
"url": "https://github.com/twbs/bootstrap/graphs/contributors" |
|||
}, |
|||
"bugs": { |
|||
"url": "https://github.com/twbs/bootstrap/issues" |
|||
}, |
|||
"bundleDependencies": false, |
|||
"contributors": [ |
|||
{ |
|||
"name": "Twitter, Inc." |
|||
} |
|||
], |
|||
"dependencies": { |
|||
"jquery": ">=1.9.1", |
|||
"tether": "^1.4.0" |
|||
}, |
|||
"deprecated": false, |
|||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", |
|||
"devDependencies": { |
|||
"autoprefixer": "^6.6.1", |
|||
"babel-eslint": "^7.1.1", |
|||
"babel-plugin-transform-es2015-modules-strip": "^0.1.0", |
|||
"babel-preset-es2015": "^6.18.0", |
|||
"clean-css": "^3.4.23", |
|||
"eslint": "^3.12.2", |
|||
"grunt": "^1.0.1", |
|||
"grunt-babel": "^6.0.0", |
|||
"grunt-build-control": "^0.7.1", |
|||
"grunt-contrib-clean": "^1.0.0", |
|||
"grunt-contrib-compress": "^1.3.0", |
|||
"grunt-contrib-concat": "^1.0.1", |
|||
"grunt-contrib-connect": "^1.0.2", |
|||
"grunt-contrib-copy": "^1.0.0", |
|||
"grunt-contrib-qunit": "^1.2.0", |
|||
"grunt-contrib-watch": "^1.0.0", |
|||
"grunt-exec": "^1.0.1", |
|||
"grunt-html": "^8.1.0", |
|||
"grunt-jekyll": "^0.4.4", |
|||
"grunt-saucelabs": "^9.0.0", |
|||
"grunt-stamp": "^0.3.0", |
|||
"htmlhint": "^0.9.13", |
|||
"is-travis": "^1.0.0", |
|||
"load-grunt-tasks": "^3.5.2", |
|||
"node-sass": "^4.1.1", |
|||
"postcss-cli": "^2.6.0", |
|||
"postcss-flexbugs-fixes": "^2.1.0", |
|||
"shelljs": "^0.7.5", |
|||
"shx": "^0.2.1", |
|||
"time-grunt": "^1.4.0", |
|||
"uglify-js": "^2.7.5" |
|||
}, |
|||
"engines": { |
|||
"node": ">=4" |
|||
}, |
|||
"files": [ |
|||
"dist", |
|||
"grunt", |
|||
"js/**/*.js", |
|||
"scss/**/*.scss", |
|||
"Gruntfile.js", |
|||
"LICENSE" |
|||
], |
|||
"homepage": "https://getbootstrap.com", |
|||
"jspm": { |
|||
"main": "js/bootstrap", |
|||
"directories": { |
|||
"lib": "dist" |
|||
}, |
|||
"shim": { |
|||
"js/bootstrap": { |
|||
"deps": [ |
|||
"jquery" |
|||
], |
|||
"exports": "$" |
|||
} |
|||
}, |
|||
"dependencies": { |
|||
"jquery": "3" |
|||
} |
|||
}, |
|||
"keywords": [ |
|||
"css", |
|||
"sass", |
|||
"mobile-first", |
|||
"responsive", |
|||
"front-end", |
|||
"framework", |
|||
"web" |
|||
], |
|||
"license": "MIT", |
|||
"main": "dist/js/bootstrap", |
|||
"name": "bootstrap", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/twbs/bootstrap.git" |
|||
}, |
|||
"sass": "scss/bootstrap.scss", |
|||
"scripts": { |
|||
"change-version": "node grunt/change-version.js", |
|||
"clean-css": "cleancss --skip-advanced --source-map --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss --skip-advanced --source-map --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss --skip-advanced --source-map --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css", |
|||
"clean-css-docs": "cleancss --skip-advanced --source-map --output docs/assets/css/docs.min.css docs/assets/css/docs.min.css", |
|||
"eslint": "eslint --ignore-path .eslintignore js && eslint --config js/tests/.eslintrc.json --env node grunt Gruntfile.js && eslint --config js/tests/.eslintrc.json docs/assets/js/src docs/assets/js/ie-emulation-modes-warning.js docs/assets/js/ie10-viewport-bug-workaround.js", |
|||
"htmlhint": "htmlhint --config docs/.htmlhintrc _gh_pages/", |
|||
"postcss": "postcss --config grunt/postcss.js --replace dist/css/*.css", |
|||
"postcss-docs": "postcss --config grunt/postcss.js --no-map --replace docs/assets/css/docs.min.css && postcss --config grunt/postcss.js --no-map --replace docs/examples/**/*.css", |
|||
"sass": "node-sass --output-style expanded --source-map true --precision 6 scss/bootstrap.scss dist/css/bootstrap.css && node-sass --output-style expanded --source-map true --precision 6 scss/bootstrap-grid.scss dist/css/bootstrap-grid.css && node-sass --output-style expanded --source-map true --precision 6 scss/bootstrap-reboot.scss dist/css/bootstrap-reboot.css", |
|||
"sass-docs": "node-sass --output-style expanded --source-map true --precision 6 docs/assets/scss/docs.scss docs/assets/css/docs.min.css", |
|||
"scss-lint": "bundle exec scss-lint --config scss/.scss-lint.yml --exclude scss/_normalize.scss scss/*.scss", |
|||
"scss-lint-docs": "bundle exec scss-lint --config scss/.scss-lint.yml --exclude docs/assets/scss/docs.scss docs/assets/scss/*.scss", |
|||
"test": "npm run eslint && grunt test", |
|||
"uglify": "uglifyjs --compress warnings=false --mangle --comments '/^!/' --output dist/js/bootstrap.min.js dist/js/bootstrap.js", |
|||
"uglify-docs": "uglifyjs --compress warnings=false --mangle --comments '/^!/' --output docs/assets/js/docs.min.js docs/assets/js/vendor/*.js docs/assets/js/src/application.js", |
|||
"update-shrinkwrap": "npm shrinkwrap --dev && shx mv ./npm-shrinkwrap.json ./grunt/npm-shrinkwrap.json" |
|||
}, |
|||
"style": "dist/css/bootstrap.css", |
|||
"version": "4.0.0-alpha.6" |
|||
} |
@ -0,0 +1,55 @@ |
|||
// |
|||
// Base styles |
|||
// |
|||
|
|||
.alert { |
|||
padding: $alert-padding-y $alert-padding-x; |
|||
margin-bottom: $alert-margin-bottom; |
|||
border: $alert-border-width solid transparent; |
|||
@include border-radius($alert-border-radius); |
|||
} |
|||
|
|||
// Headings for larger alerts |
|||
.alert-heading { |
|||
// Specified to prevent conflicts of changing $headings-color |
|||
color: inherit; |
|||
} |
|||
|
|||
// Provide class for links that match alerts |
|||
.alert-link { |
|||
font-weight: $alert-link-font-weight; |
|||
} |
|||
|
|||
|
|||
// Dismissible alerts |
|||
// |
|||
// Expand the right padding and account for the close button's positioning. |
|||
|
|||
.alert-dismissible { |
|||
// Adjust close link position |
|||
.close { |
|||
position: relative; |
|||
top: -$alert-padding-y; |
|||
right: -$alert-padding-x; |
|||
padding: $alert-padding-y $alert-padding-x; |
|||
color: inherit; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Alternate styles |
|||
// |
|||
// Generate contextual modifier classes for colorizing the alert. |
|||
|
|||
.alert-success { |
|||
@include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); |
|||
} |
|||
.alert-info { |
|||
@include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); |
|||
} |
|||
.alert-warning { |
|||
@include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); |
|||
} |
|||
.alert-danger { |
|||
@include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); |
|||
} |
@ -0,0 +1,77 @@ |
|||
// Base class |
|||
// |
|||
// Requires one of the contextual, color modifier classes for `color` and |
|||
// `background-color`. |
|||
|
|||
.badge { |
|||
display: inline-block; |
|||
padding: $badge-padding-y $badge-padding-x; |
|||
font-size: $badge-font-size; |
|||
font-weight: $badge-font-weight; |
|||
line-height: 1; |
|||
color: $badge-color; |
|||
text-align: center; |
|||
white-space: nowrap; |
|||
vertical-align: baseline; |
|||
@include border-radius(); |
|||
|
|||
// Empty badges collapse automatically |
|||
&:empty { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
// Quick fix for badges in buttons |
|||
.btn .badge { |
|||
position: relative; |
|||
top: -1px; |
|||
} |
|||
|
|||
// scss-lint:disable QualifyingElement |
|||
// Add hover effects, but only for links |
|||
a.badge { |
|||
@include hover-focus { |
|||
color: $badge-link-hover-color; |
|||
text-decoration: none; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
// scss-lint:enable QualifyingElement |
|||
|
|||
// Pill badges |
|||
// |
|||
// Make them extra rounded with a modifier to replace v3's badges. |
|||
|
|||
.badge-pill { |
|||
padding-right: $badge-pill-padding-x; |
|||
padding-left: $badge-pill-padding-x; |
|||
@include border-radius($badge-pill-border-radius); |
|||
} |
|||
|
|||
// Colors |
|||
// |
|||
// Contextual variations (linked badges get darker on :hover). |
|||
|
|||
.badge-default { |
|||
@include badge-variant($badge-default-bg); |
|||
} |
|||
|
|||
.badge-primary { |
|||
@include badge-variant($badge-primary-bg); |
|||
} |
|||
|
|||
.badge-success { |
|||
@include badge-variant($badge-success-bg); |
|||
} |
|||
|
|||
.badge-info { |
|||
@include badge-variant($badge-info-bg); |
|||
} |
|||
|
|||
.badge-warning { |
|||
@include badge-variant($badge-warning-bg); |
|||
} |
|||
|
|||
.badge-danger { |
|||
@include badge-variant($badge-danger-bg); |
|||
} |
@ -0,0 +1,38 @@ |
|||
.breadcrumb { |
|||
padding: $breadcrumb-padding-y $breadcrumb-padding-x; |
|||
margin-bottom: $spacer-y; |
|||
list-style: none; |
|||
background-color: $breadcrumb-bg; |
|||
@include border-radius($border-radius); |
|||
@include clearfix; |
|||
} |
|||
|
|||
.breadcrumb-item { |
|||
float: left; |
|||
|
|||
// The separator between breadcrumbs (by default, a forward-slash: "/") |
|||
+ .breadcrumb-item::before { |
|||
display: inline-block; // Suppress underlining of the separator in modern browsers |
|||
padding-right: $breadcrumb-item-padding; |
|||
padding-left: $breadcrumb-item-padding; |
|||
color: $breadcrumb-divider-color; |
|||
content: "#{$breadcrumb-divider}"; |
|||
} |
|||
|
|||
// IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built |
|||
// without `<ul>`s. The `::before` pseudo-element generates an element |
|||
// *within* the .breadcrumb-item and thereby inherits the `text-decoration`. |
|||
// |
|||
// To trick IE into suppressing the underline, we give the pseudo-element an |
|||
// underline and then immediately remove it. |
|||
+ .breadcrumb-item:hover::before { |
|||
text-decoration: underline; |
|||
} |
|||
+ .breadcrumb-item:hover::before { |
|||
text-decoration: none; |
|||
} |
|||
|
|||
&.active { |
|||
color: $breadcrumb-active-color; |
|||
} |
|||
} |
@ -0,0 +1,202 @@ |
|||
// scss-lint:disable QualifyingElement |
|||
|
|||
// Make the div behave like a button |
|||
.btn-group, |
|||
.btn-group-vertical { |
|||
position: relative; |
|||
display: inline-flex; |
|||
vertical-align: middle; // match .btn alignment given font-size hack above |
|||
|
|||
> .btn { |
|||
position: relative; |
|||
flex: 0 1 auto; |
|||
|
|||
// Bring the hover, focused, and "active" buttons to the fron to overlay |
|||
// the borders properly |
|||
@include hover { |
|||
z-index: 2; |
|||
} |
|||
&:focus, |
|||
&:active, |
|||
&.active { |
|||
z-index: 2; |
|||
} |
|||
} |
|||
|
|||
// Prevent double borders when buttons are next to each other |
|||
.btn + .btn, |
|||
.btn + .btn-group, |
|||
.btn-group + .btn, |
|||
.btn-group + .btn-group { |
|||
margin-left: -$input-btn-border-width; |
|||
} |
|||
} |
|||
|
|||
// Optional: Group multiple button groups together for a toolbar |
|||
.btn-toolbar { |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
|
|||
.input-group { |
|||
width: auto; |
|||
} |
|||
} |
|||
|
|||
.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { |
|||
border-radius: 0; |
|||
} |
|||
|
|||
// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match |
|||
.btn-group > .btn:first-child { |
|||
margin-left: 0; |
|||
|
|||
&:not(:last-child):not(.dropdown-toggle) { |
|||
@include border-right-radius(0); |
|||
} |
|||
} |
|||
// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it |
|||
.btn-group > .btn:last-child:not(:first-child), |
|||
.btn-group > .dropdown-toggle:not(:first-child) { |
|||
@include border-left-radius(0); |
|||
} |
|||
|
|||
// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) |
|||
.btn-group > .btn-group { |
|||
float: left; |
|||
} |
|||
.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { |
|||
border-radius: 0; |
|||
} |
|||
.btn-group > .btn-group:first-child:not(:last-child) { |
|||
> .btn:last-child, |
|||
> .dropdown-toggle { |
|||
@include border-right-radius(0); |
|||
} |
|||
} |
|||
.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { |
|||
@include border-left-radius(0); |
|||
} |
|||
|
|||
// On active and open, don't show outline |
|||
.btn-group .dropdown-toggle:active, |
|||
.btn-group.open .dropdown-toggle { |
|||
outline: 0; |
|||
} |
|||
|
|||
|
|||
// Sizing |
|||
// |
|||
// Remix the default button sizing classes into new ones for easier manipulation. |
|||
|
|||
.btn-group-sm > .btn { @extend .btn-sm; } |
|||
.btn-group-lg > .btn { @extend .btn-lg; } |
|||
|
|||
|
|||
// |
|||
// Split button dropdowns |
|||
// |
|||
|
|||
.btn + .dropdown-toggle-split { |
|||
padding-right: $btn-padding-x * .75; |
|||
padding-left: $btn-padding-x * .75; |
|||
|
|||
&::after { |
|||
margin-left: 0; |
|||
} |
|||
} |
|||
|
|||
.btn-sm + .dropdown-toggle-split { |
|||
padding-right: $btn-padding-x-sm * .75; |
|||
padding-left: $btn-padding-x-sm * .75; |
|||
} |
|||
|
|||
.btn-lg + .dropdown-toggle-split { |
|||
padding-right: $btn-padding-x-lg * .75; |
|||
padding-left: $btn-padding-x-lg * .75; |
|||
} |
|||
|
|||
|
|||
// The clickable button for toggling the menu |
|||
// Remove the gradient and set the same inset shadow as the :active state |
|||
.btn-group.open .dropdown-toggle { |
|||
@include box-shadow($btn-active-box-shadow); |
|||
|
|||
// Show no shadow for `.btn-link` since it has no other button styles. |
|||
&.btn-link { |
|||
@include box-shadow(none); |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Vertical button groups |
|||
// |
|||
|
|||
.btn-group-vertical { |
|||
display: inline-flex; |
|||
flex-direction: column; |
|||
align-items: flex-start; |
|||
justify-content: center; |
|||
|
|||
.btn, |
|||
.btn-group { |
|||
width: 100%; |
|||
} |
|||
|
|||
> .btn + .btn, |
|||
> .btn + .btn-group, |
|||
> .btn-group + .btn, |
|||
> .btn-group + .btn-group { |
|||
margin-top: -$input-btn-border-width; |
|||
margin-left: 0; |
|||
} |
|||
} |
|||
|
|||
.btn-group-vertical > .btn { |
|||
&:not(:first-child):not(:last-child) { |
|||
border-radius: 0; |
|||
} |
|||
&:first-child:not(:last-child) { |
|||
@include border-bottom-radius(0); |
|||
} |
|||
&:last-child:not(:first-child) { |
|||
@include border-top-radius(0); |
|||
} |
|||
} |
|||
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { |
|||
border-radius: 0; |
|||
} |
|||
.btn-group-vertical > .btn-group:first-child:not(:last-child) { |
|||
> .btn:last-child, |
|||
> .dropdown-toggle { |
|||
@include border-bottom-radius(0); |
|||
} |
|||
} |
|||
.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { |
|||
@include border-top-radius(0); |
|||
} |
|||
|
|||
|
|||
// Checkbox and radio options |
|||
// |
|||
// In order to support the browser's form validation feedback, powered by the |
|||
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use |
|||
// `display: none;` or `visibility: hidden;` as that also hides the popover. |
|||
// Simply visually hiding the inputs via `opacity` would leave them clickable in |
|||
// certain cases which is prevented by using `clip` and `pointer-events`. |
|||
// This way, we ensure a DOM element is visible to position the popover from. |
|||
// |
|||
// See https://github.com/twbs/bootstrap/pull/12794 and |
|||
// https://github.com/twbs/bootstrap/pull/14559 for more information. |
|||
|
|||
[data-toggle="buttons"] { |
|||
> .btn, |
|||
> .btn-group > .btn { |
|||
input[type="radio"], |
|||
input[type="checkbox"] { |
|||
position: absolute; |
|||
clip: rect(0,0,0,0); |
|||
pointer-events: none; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,170 @@ |
|||
// scss-lint:disable QualifyingElement |
|||
|
|||
// |
|||
// Base styles |
|||
// |
|||
|
|||
.btn { |
|||
display: inline-block; |
|||
font-weight: $btn-font-weight; |
|||
line-height: $btn-line-height; |
|||
text-align: center; |
|||
white-space: nowrap; |
|||
vertical-align: middle; |
|||
user-select: none; |
|||
border: $input-btn-border-width solid transparent; |
|||
@include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-border-radius); |
|||
@include transition($btn-transition); |
|||
|
|||
// Share hover and focus styles |
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
&:focus, |
|||
&.focus { |
|||
outline: 0; |
|||
box-shadow: $btn-focus-box-shadow; |
|||
} |
|||
|
|||
// Disabled comes first so active can properly restyle |
|||
&.disabled, |
|||
&:disabled { |
|||
cursor: $cursor-disabled; |
|||
opacity: .65; |
|||
@include box-shadow(none); |
|||
} |
|||
|
|||
&:active, |
|||
&.active { |
|||
background-image: none; |
|||
@include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow); |
|||
} |
|||
} |
|||
|
|||
// Future-proof disabling of clicks on `<a>` elements |
|||
a.btn.disabled, |
|||
fieldset[disabled] a.btn { |
|||
pointer-events: none; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Alternate buttons |
|||
// |
|||
|
|||
.btn-primary { |
|||
@include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); |
|||
} |
|||
.btn-secondary { |
|||
@include button-variant($btn-secondary-color, $btn-secondary-bg, $btn-secondary-border); |
|||
} |
|||
.btn-info { |
|||
@include button-variant($btn-info-color, $btn-info-bg, $btn-info-border); |
|||
} |
|||
.btn-success { |
|||
@include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); |
|||
} |
|||
.btn-warning { |
|||
@include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border); |
|||
} |
|||
.btn-danger { |
|||
@include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border); |
|||
} |
|||
|
|||
// Remove all backgrounds |
|||
.btn-outline-primary { |
|||
@include button-outline-variant($btn-primary-bg); |
|||
} |
|||
.btn-outline-secondary { |
|||
@include button-outline-variant($btn-secondary-border); |
|||
} |
|||
.btn-outline-info { |
|||
@include button-outline-variant($btn-info-bg); |
|||
} |
|||
.btn-outline-success { |
|||
@include button-outline-variant($btn-success-bg); |
|||
} |
|||
.btn-outline-warning { |
|||
@include button-outline-variant($btn-warning-bg); |
|||
} |
|||
.btn-outline-danger { |
|||
@include button-outline-variant($btn-danger-bg); |
|||
} |
|||
|
|||
|
|||
// |
|||
// Link buttons |
|||
// |
|||
|
|||
// Make a button look and behave like a link |
|||
.btn-link { |
|||
font-weight: $font-weight-normal; |
|||
color: $link-color; |
|||
border-radius: 0; |
|||
|
|||
&, |
|||
&:active, |
|||
&.active, |
|||
&:disabled { |
|||
background-color: transparent; |
|||
@include box-shadow(none); |
|||
} |
|||
&, |
|||
&:focus, |
|||
&:active { |
|||
border-color: transparent; |
|||
} |
|||
@include hover { |
|||
border-color: transparent; |
|||
} |
|||
@include hover-focus { |
|||
color: $link-hover-color; |
|||
text-decoration: $link-hover-decoration; |
|||
background-color: transparent; |
|||
} |
|||
&:disabled { |
|||
color: $btn-link-disabled-color; |
|||
|
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Button Sizes |
|||
// |
|||
|
|||
.btn-lg { |
|||
// line-height: ensure even-numbered height of button next to large input |
|||
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-border-radius-lg); |
|||
} |
|||
.btn-sm { |
|||
// line-height: ensure proper height of button next to small input |
|||
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius-sm); |
|||
} |
|||
|
|||
|
|||
// |
|||
// Block button |
|||
// |
|||
|
|||
.btn-block { |
|||
display: block; |
|||
width: 100%; |
|||
} |
|||
|
|||
// Vertically space out multiple block buttons |
|||
.btn-block + .btn-block { |
|||
margin-top: $btn-block-spacing-y; |
|||
} |
|||
|
|||
// Specificity overrides |
|||
input[type="submit"], |
|||
input[type="reset"], |
|||
input[type="button"] { |
|||
&.btn-block { |
|||
width: 100%; |
|||
} |
|||
} |
@ -0,0 +1,276 @@ |
|||
// |
|||
// Base styles |
|||
// |
|||
|
|||
.card { |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background-color: $card-bg; |
|||
border: $card-border-width solid $card-border-color; |
|||
@include border-radius($card-border-radius); |
|||
} |
|||
|
|||
.card-block { |
|||
// Enable `flex-grow: 1` for decks and groups so that card blocks take up |
|||
// as much space as possible, ensuring footers are aligned to the bottom. |
|||
flex: 1 1 auto; |
|||
padding: $card-spacer-x; |
|||
} |
|||
|
|||
.card-title { |
|||
margin-bottom: $card-spacer-y; |
|||
} |
|||
|
|||
.card-subtitle { |
|||
margin-top: -($card-spacer-y / 2); |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.card-text:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.card-link { |
|||
@include hover { |
|||
text-decoration: none; |
|||
} |
|||
|
|||
+ .card-link { |
|||
margin-left: $card-spacer-x; |
|||
} |
|||
} |
|||
|
|||
.card { |
|||
> .list-group:first-child { |
|||
.list-group-item:first-child { |
|||
@include border-top-radius($card-border-radius); |
|||
} |
|||
} |
|||
|
|||
> .list-group:last-child { |
|||
.list-group-item:last-child { |
|||
@include border-bottom-radius($card-border-radius); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Optional textual caps |
|||
// |
|||
|
|||
.card-header { |
|||
padding: $card-spacer-y $card-spacer-x; |
|||
margin-bottom: 0; // Removes the default margin-bottom of <hN> |
|||
background-color: $card-cap-bg; |
|||
border-bottom: $card-border-width solid $card-border-color; |
|||
|
|||
&:first-child { |
|||
@include border-radius($card-border-radius-inner $card-border-radius-inner 0 0); |
|||
} |
|||
} |
|||
|
|||
.card-footer { |
|||
padding: $card-spacer-y $card-spacer-x; |
|||
background-color: $card-cap-bg; |
|||
border-top: $card-border-width solid $card-border-color; |
|||
|
|||
&:last-child { |
|||
@include border-radius(0 0 $card-border-radius-inner $card-border-radius-inner); |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Header navs |
|||
// |
|||
|
|||
.card-header-tabs { |
|||
margin-right: -($card-spacer-x / 2); |
|||
margin-bottom: -$card-spacer-y; |
|||
margin-left: -($card-spacer-x / 2); |
|||
border-bottom: 0; |
|||
} |
|||
|
|||
.card-header-pills { |
|||
margin-right: -($card-spacer-x / 2); |
|||
margin-left: -($card-spacer-x / 2); |
|||
} |
|||
|
|||
|
|||
// |
|||
// Background variations |
|||
// |
|||
|
|||
.card-primary { |
|||
@include card-variant($brand-primary, $brand-primary); |
|||
} |
|||
.card-success { |
|||
@include card-variant($brand-success, $brand-success); |
|||
} |
|||
.card-info { |
|||
@include card-variant($brand-info, $brand-info); |
|||
} |
|||
.card-warning { |
|||
@include card-variant($brand-warning, $brand-warning); |
|||
} |
|||
.card-danger { |
|||
@include card-variant($brand-danger, $brand-danger); |
|||
} |
|||
|
|||
// Remove all backgrounds |
|||
.card-outline-primary { |
|||
@include card-outline-variant($btn-primary-bg); |
|||
} |
|||
.card-outline-secondary { |
|||
@include card-outline-variant($btn-secondary-border); |
|||
} |
|||
.card-outline-info { |
|||
@include card-outline-variant($btn-info-bg); |
|||
} |
|||
.card-outline-success { |
|||
@include card-outline-variant($btn-success-bg); |
|||
} |
|||
.card-outline-warning { |
|||
@include card-outline-variant($btn-warning-bg); |
|||
} |
|||
.card-outline-danger { |
|||
@include card-outline-variant($btn-danger-bg); |
|||
} |
|||
|
|||
// |
|||
// Inverse text within a card for use with dark backgrounds |
|||
// |
|||
|
|||
.card-inverse { |
|||
@include card-inverse; |
|||
} |
|||
|
|||
// |
|||
// Blockquote |
|||
// |
|||
|
|||
.card-blockquote { |
|||
padding: 0; |
|||
margin-bottom: 0; |
|||
border-left: 0; |
|||
} |
|||
|
|||
// Card image |
|||
.card-img { |
|||
// margin: -1.325rem; |
|||
@include border-radius($card-border-radius-inner); |
|||
} |
|||
.card-img-overlay { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
padding: $card-img-overlay-padding; |
|||
} |
|||
|
|||
|
|||
|
|||
// Card image caps |
|||
.card-img-top { |
|||
@include border-top-radius($card-border-radius-inner); |
|||
} |
|||
.card-img-bottom { |
|||
@include border-bottom-radius($card-border-radius-inner); |
|||
} |
|||
|
|||
|
|||
// Card deck |
|||
|
|||
@include media-breakpoint-up(sm) { |
|||
.card-deck { |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
|
|||
.card { |
|||
display: flex; |
|||
flex: 1 0 0; |
|||
flex-direction: column; |
|||
|
|||
// Selectively apply horizontal margins to cards to avoid doing the |
|||
// negative margin dance like our grid. This differs from the grid |
|||
// due to the use of margins as gutters instead of padding. |
|||
&:not(:first-child) { margin-left: $card-deck-margin; } |
|||
&:not(:last-child) { margin-right: $card-deck-margin; } |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Card groups |
|||
// |
|||
|
|||
@include media-breakpoint-up(sm) { |
|||
.card-group { |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
|
|||
.card { |
|||
flex: 1 0 0; |
|||
|
|||
+ .card { |
|||
margin-left: 0; |
|||
border-left: 0; |
|||
} |
|||
|
|||
// Handle rounded corners |
|||
@if $enable-rounded { |
|||
&:first-child { |
|||
@include border-right-radius(0); |
|||
|
|||
.card-img-top { |
|||
border-top-right-radius: 0; |
|||
} |
|||
.card-img-bottom { |
|||
border-bottom-right-radius: 0; |
|||
} |
|||
} |
|||
&:last-child { |
|||
@include border-left-radius(0); |
|||
|
|||
.card-img-top { |
|||
border-top-left-radius: 0; |
|||
} |
|||
.card-img-bottom { |
|||
border-bottom-left-radius: 0; |
|||
} |
|||
} |
|||
|
|||
&:not(:first-child):not(:last-child) { |
|||
border-radius: 0; |
|||
|
|||
.card-img-top, |
|||
.card-img-bottom { |
|||
border-radius: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Columns |
|||
// |
|||
|
|||
@include media-breakpoint-up(sm) { |
|||
.card-columns { |
|||
column-count: $card-columns-count; |
|||
column-gap: $card-columns-gap; |
|||
|
|||
.card { |
|||
display: inline-block; // Don't let them vertically span multiple columns |
|||
width: 100%; // Don't let their width change |
|||
margin-bottom: $card-columns-margin; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,178 @@ |
|||
// Wrapper for the slide container and indicators |
|||
.carousel { |
|||
position: relative; |
|||
} |
|||
|
|||
.carousel-inner { |
|||
position: relative; |
|||
width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.carousel-item { |
|||
position: relative; |
|||
display: none; |
|||
width: 100%; |
|||
|
|||
@include if-supports-3d-transforms() { |
|||
@include transition($carousel-transition); |
|||
backface-visibility: hidden; |
|||
perspective: 1000px; |
|||
} |
|||
} |
|||
|
|||
.carousel-item.active, |
|||
.carousel-item-next, |
|||
.carousel-item-prev { |
|||
display: flex; |
|||
} |
|||
|
|||
.carousel-item-next, |
|||
.carousel-item-prev { |
|||
position: absolute; |
|||
top: 0; |
|||
} |
|||
|
|||
// CSS3 transforms when supported by the browser |
|||
@include if-supports-3d-transforms() { |
|||
.carousel-item-next.carousel-item-left, |
|||
.carousel-item-prev.carousel-item-right { |
|||
transform: translate3d(0, 0, 0); |
|||
} |
|||
|
|||
.carousel-item-next, |
|||
.active.carousel-item-right { |
|||
transform: translate3d(100%, 0, 0); |
|||
} |
|||
|
|||
.carousel-item-prev, |
|||
.active.carousel-item-left { |
|||
transform: translate3d(-100%, 0, 0); |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Left/right controls for nav |
|||
// |
|||
|
|||
.carousel-control-prev, |
|||
.carousel-control-next { |
|||
position: absolute; |
|||
top: 0; |
|||
bottom: 0; |
|||
// Use flex for alignment (1-3) |
|||
display: flex; // 1. allow flex styles |
|||
align-items: center; // 2. vertically center contents |
|||
justify-content: center; // 3. horizontally center contents |
|||
width: $carousel-control-width; |
|||
color: $carousel-control-color; |
|||
text-align: center; |
|||
opacity: $carousel-control-opacity; |
|||
// We can't have a transition here because WebKit cancels the carousel |
|||
// animation if you trip this while in the middle of another animation. |
|||
|
|||
// Hover/focus state |
|||
@include hover-focus { |
|||
color: $carousel-control-color; |
|||
text-decoration: none; |
|||
outline: 0; |
|||
opacity: .9; |
|||
} |
|||
} |
|||
.carousel-control-prev { |
|||
left: 0; |
|||
} |
|||
.carousel-control-next { |
|||
right: 0; |
|||
} |
|||
|
|||
// Icons for within |
|||
.carousel-control-prev-icon, |
|||
.carousel-control-next-icon { |
|||
display: inline-block; |
|||
width: $carousel-control-icon-width; |
|||
height: $carousel-control-icon-width; |
|||
background: transparent no-repeat center center; |
|||
background-size: 100% 100%; |
|||
} |
|||
.carousel-control-prev-icon { |
|||
background-image: $carousel-control-prev-icon-bg; |
|||
} |
|||
.carousel-control-next-icon { |
|||
background-image: $carousel-control-next-icon-bg; |
|||
} |
|||
|
|||
|
|||
// Optional indicator pips |
|||
// |
|||
// Add an ordered list with the following class and add a list item for each |
|||
// slide your carousel holds. |
|||
|
|||
.carousel-indicators { |
|||
position: absolute; |
|||
right: 0; |
|||
bottom: 10px; |
|||
left: 0; |
|||
z-index: 15; |
|||
display: flex; |
|||
justify-content: center; |
|||
padding-left: 0; // override <ol> default |
|||
// Use the .carousel-control's width as margin so we don't overlay those |
|||
margin-right: $carousel-control-width; |
|||
margin-left: $carousel-control-width; |
|||
list-style: none; |
|||
|
|||
li { |
|||
position: relative; |
|||
flex: 1 0 auto; |
|||
max-width: $carousel-indicator-width; |
|||
height: $carousel-indicator-height; |
|||
margin-right: $carousel-indicator-spacer; |
|||
margin-left: $carousel-indicator-spacer; |
|||
text-indent: -999px; |
|||
cursor: pointer; |
|||
background-color: rgba($carousel-indicator-active-bg, .5); |
|||
|
|||
// Use pseudo classes to increase the hit area by 10px on top and bottom. |
|||
&::before { |
|||
position: absolute; |
|||
top: -10px; |
|||
left: 0; |
|||
display: inline-block; |
|||
width: 100%; |
|||
height: 10px; |
|||
content: ""; |
|||
} |
|||
&::after { |
|||
position: absolute; |
|||
bottom: -10px; |
|||
left: 0; |
|||
display: inline-block; |
|||
width: 100%; |
|||
height: 10px; |
|||
content: ""; |
|||
} |
|||
} |
|||
|
|||
.active { |
|||
background-color: $carousel-indicator-active-bg; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Optional captions |
|||
// |
|||
// |
|||
|
|||
.carousel-caption { |
|||
position: absolute; |
|||
right: ((100% - $carousel-caption-width) / 2); |
|||
bottom: 20px; |
|||
left: ((100% - $carousel-caption-width) / 2); |
|||
z-index: 10; |
|||
padding-top: 20px; |
|||
padding-bottom: 20px; |
|||
color: $carousel-caption-color; |
|||
text-align: center; |
|||
} |
@ -0,0 +1,31 @@ |
|||
.close { |
|||
float: right; |
|||
font-size: $close-font-size; |
|||
font-weight: $close-font-weight; |
|||
line-height: 1; |
|||
color: $close-color; |
|||
text-shadow: $close-text-shadow; |
|||
opacity: .5; |
|||
|
|||
@include hover-focus { |
|||
color: $close-color; |
|||
text-decoration: none; |
|||
cursor: pointer; |
|||
opacity: .75; |
|||
} |
|||
} |
|||
|
|||
// Additional properties for button version |
|||
// iOS requires the button element instead of an anchor tag. |
|||
// If you want the anchor version, it requires `href="#"`. |
|||
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile |
|||
|
|||
// scss-lint:disable QualifyingElement |
|||
button.close { |
|||
padding: 0; |
|||
cursor: pointer; |
|||
background: transparent; |
|||
border: 0; |
|||
-webkit-appearance: none; |
|||
} |
|||
// scss-lint:enable QualifyingElement |
@ -0,0 +1,64 @@ |
|||
// Inline and block code styles |
|||
code, |
|||
kbd, |
|||
pre, |
|||
samp { |
|||
font-family: $font-family-monospace; |
|||
} |
|||
|
|||
// Inline code |
|||
code { |
|||
padding: $code-padding-y $code-padding-x; |
|||
font-size: $code-font-size; |
|||
color: $code-color; |
|||
background-color: $code-bg; |
|||
@include border-radius($border-radius); |
|||
|
|||
// Streamline the style when inside anchors to avoid broken underline and more |
|||
a > & { |
|||
padding: 0; |
|||
color: inherit; |
|||
background-color: inherit; |
|||
} |
|||
} |
|||
|
|||
// User input typically entered via keyboard |
|||
kbd { |
|||
padding: $code-padding-y $code-padding-x; |
|||
font-size: $code-font-size; |
|||
color: $kbd-color; |
|||
background-color: $kbd-bg; |
|||
@include border-radius($border-radius-sm); |
|||
@include box-shadow($kbd-box-shadow); |
|||
|
|||
kbd { |
|||
padding: 0; |
|||
font-size: 100%; |
|||
font-weight: $nested-kbd-font-weight; |
|||
@include box-shadow(none); |
|||
} |
|||
} |
|||
|
|||
// Blocks of code |
|||
pre { |
|||
display: block; |
|||
margin-top: 0; |
|||
margin-bottom: 1rem; |
|||
font-size: $code-font-size; |
|||
color: $pre-color; |
|||
|
|||
// Account for some code outputs that place code tags in pre tags |
|||
code { |
|||
padding: 0; |
|||
font-size: inherit; |
|||
color: inherit; |
|||
background-color: transparent; |
|||
border-radius: 0; |
|||
} |
|||
} |
|||
|
|||
// Enable scrollable blocks of code |
|||
.pre-scrollable { |
|||
max-height: $pre-scrollable-max-height; |
|||
overflow-y: scroll; |
|||
} |
@ -0,0 +1,263 @@ |
|||
// scss-lint:disable PropertyCount |
|||
|
|||
// Embedded icons from Open Iconic. |
|||
// Released under MIT and copyright 2014 Waybury. |
|||
// https://useiconic.com/open |
|||
|
|||
|
|||
// Checkboxes and radios |
|||
// |
|||
// Base class takes care of all the key behavioral aspects. |
|||
|
|||
.custom-control { |
|||
position: relative; |
|||
display: inline-flex; |
|||
min-height: (1rem * $line-height-base); |
|||
padding-left: $custom-control-gutter; |
|||
margin-right: $custom-control-spacer-x; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.custom-control-input { |
|||
position: absolute; |
|||
z-index: -1; // Put the input behind the label so it doesn't overlay text |
|||
opacity: 0; |
|||
|
|||
&:checked ~ .custom-control-indicator { |
|||
color: $custom-control-checked-indicator-color; |
|||
background-color: $custom-control-checked-indicator-bg; |
|||
@include box-shadow($custom-control-checked-indicator-box-shadow); |
|||
} |
|||
|
|||
&:focus ~ .custom-control-indicator { |
|||
// the mixin is not used here to make sure there is feedback |
|||
box-shadow: $custom-control-focus-indicator-box-shadow; |
|||
} |
|||
|
|||
&:active ~ .custom-control-indicator { |
|||
color: $custom-control-active-indicator-color; |
|||
background-color: $custom-control-active-indicator-bg; |
|||
@include box-shadow($custom-control-active-indicator-box-shadow); |
|||
} |
|||
|
|||
&:disabled { |
|||
~ .custom-control-indicator { |
|||
cursor: $custom-control-disabled-cursor; |
|||
background-color: $custom-control-disabled-indicator-bg; |
|||
} |
|||
|
|||
~ .custom-control-description { |
|||
color: $custom-control-disabled-description-color; |
|||
cursor: $custom-control-disabled-cursor; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Custom indicator |
|||
// |
|||
// Generates a shadow element to create our makeshift checkbox/radio background. |
|||
|
|||
.custom-control-indicator { |
|||
position: absolute; |
|||
top: (($line-height-base - $custom-control-indicator-size) / 2); |
|||
left: 0; |
|||
display: block; |
|||
width: $custom-control-indicator-size; |
|||
height: $custom-control-indicator-size; |
|||
pointer-events: none; |
|||
user-select: none; |
|||
background-color: $custom-control-indicator-bg; |
|||
background-repeat: no-repeat; |
|||
background-position: center center; |
|||
background-size: $custom-control-indicator-bg-size; |
|||
@include box-shadow($custom-control-indicator-box-shadow); |
|||
} |
|||
|
|||
// Checkboxes |
|||
// |
|||
// Tweak just a few things for checkboxes. |
|||
|
|||
.custom-checkbox { |
|||
.custom-control-indicator { |
|||
@include border-radius($custom-checkbox-radius); |
|||
} |
|||
|
|||
.custom-control-input:checked ~ .custom-control-indicator { |
|||
background-image: $custom-checkbox-checked-icon; |
|||
} |
|||
|
|||
.custom-control-input:indeterminate ~ .custom-control-indicator { |
|||
background-color: $custom-checkbox-indeterminate-bg; |
|||
background-image: $custom-checkbox-indeterminate-icon; |
|||
@include box-shadow($custom-checkbox-indeterminate-box-shadow); |
|||
} |
|||
} |
|||
|
|||
// Radios |
|||
// |
|||
// Tweak just a few things for radios. |
|||
|
|||
.custom-radio { |
|||
.custom-control-indicator { |
|||
border-radius: $custom-radio-radius; |
|||
} |
|||
|
|||
.custom-control-input:checked ~ .custom-control-indicator { |
|||
background-image: $custom-radio-checked-icon; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Layout options |
|||
// |
|||
// By default radios and checkboxes are `inline-block` with no additional spacing |
|||
// set. Use these optional classes to tweak the layout. |
|||
|
|||
.custom-controls-stacked { |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.custom-control { |
|||
margin-bottom: $custom-control-spacer-y; |
|||
|
|||
+ .custom-control { |
|||
margin-left: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Select |
|||
// |
|||
// Replaces the browser default select with a custom one, mostly pulled from |
|||
// http://primercss.io. |
|||
// |
|||
|
|||
.custom-select { |
|||
display: inline-block; |
|||
max-width: 100%; |
|||
$select-border-width: ($border-width * 2); |
|||
height: calc(#{$input-height} + #{$select-border-width}); |
|||
padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x; |
|||
line-height: $custom-select-line-height; |
|||
color: $custom-select-color; |
|||
vertical-align: middle; |
|||
background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center; |
|||
background-size: $custom-select-bg-size; |
|||
border: $custom-select-border-width solid $custom-select-border-color; |
|||
@include border-radius($custom-select-border-radius); |
|||
// Use vendor prefixes as `appearance` isn't part of the CSS spec. |
|||
-moz-appearance: none; |
|||
-webkit-appearance: none; |
|||
|
|||
&:focus { |
|||
border-color: $custom-select-focus-border-color; |
|||
outline: none; |
|||
@include box-shadow($custom-select-focus-box-shadow); |
|||
|
|||
&::-ms-value { |
|||
// For visual consistency with other platforms/browsers, |
|||
// supress the default white text on blue background highlight given to |
|||
// the selected option text when the (still closed) <select> receives focus |
|||
// in IE and (under certain conditions) Edge. |
|||
// See https://github.com/twbs/bootstrap/issues/19398. |
|||
color: $input-color; |
|||
background-color: $input-bg; |
|||
} |
|||
} |
|||
|
|||
&:disabled { |
|||
color: $custom-select-disabled-color; |
|||
cursor: $cursor-disabled; |
|||
background-color: $custom-select-disabled-bg; |
|||
} |
|||
|
|||
// Hides the default caret in IE11 |
|||
&::-ms-expand { |
|||
opacity: 0; |
|||
} |
|||
} |
|||
|
|||
.custom-select-sm { |
|||
padding-top: $custom-select-padding-y; |
|||
padding-bottom: $custom-select-padding-y; |
|||
font-size: $custom-select-sm-font-size; |
|||
|
|||
// &:not([multiple]) { |
|||
// height: 26px; |
|||
// min-height: 26px; |
|||
// } |
|||
} |
|||
|
|||
|
|||
// File |
|||
// |
|||
// Custom file input. |
|||
|
|||
.custom-file { |
|||
position: relative; |
|||
display: inline-block; |
|||
max-width: 100%; |
|||
height: $custom-file-height; |
|||
margin-bottom: 0; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.custom-file-input { |
|||
min-width: $custom-file-width; |
|||
max-width: 100%; |
|||
height: $custom-file-height; |
|||
margin: 0; |
|||
filter: alpha(opacity = 0); |
|||
opacity: 0; |
|||
|
|||
&:focus ~ .custom-file-control { |
|||
@include box-shadow($custom-file-focus-box-shadow); |
|||
} |
|||
} |
|||
|
|||
.custom-file-control { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
left: 0; |
|||
z-index: 5; |
|||
height: $custom-file-height; |
|||
padding: $custom-file-padding-x $custom-file-padding-y; |
|||
line-height: $custom-file-line-height; |
|||
color: $custom-file-color; |
|||
pointer-events: none; |
|||
user-select: none; |
|||
background-color: $custom-file-bg; |
|||
border: $custom-file-border-width solid $custom-file-border-color; |
|||
@include border-radius($custom-file-border-radius); |
|||
@include box-shadow($custom-file-box-shadow); |
|||
|
|||
@each $lang, $text in map-get($custom-file-text, placeholder) { |
|||
&:lang(#{$lang})::after { |
|||
content: $text; |
|||
} |
|||
} |
|||
|
|||
&::before { |
|||
position: absolute; |
|||
top: -$custom-file-border-width; |
|||
right: -$custom-file-border-width; |
|||
bottom: -$custom-file-border-width; |
|||
z-index: 6; |
|||
display: block; |
|||
height: $custom-file-height; |
|||
padding: $custom-file-padding-x $custom-file-padding-y; |
|||
line-height: $custom-file-line-height; |
|||
color: $custom-file-button-color; |
|||
background-color: $custom-file-button-bg; |
|||
border: $custom-file-border-width solid $custom-file-border-color; |
|||
@include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0); |
|||
} |
|||
|
|||
@each $lang, $text in map-get($custom-file-text, button-label) { |
|||
&:lang(#{$lang})::before { |
|||
content: $text; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
// Bootstrap overrides |
|||
// |
|||
// Copy variables from `_variables.scss` to this file to override default values |
|||
// without modifying source files. |
@ -0,0 +1,161 @@ |
|||
// The dropdown wrapper (`<div>`) |
|||
.dropup, |
|||
.dropdown { |
|||
position: relative; |
|||
} |
|||
|
|||
.dropdown-toggle { |
|||
// Generate the caret automatically |
|||
&::after { |
|||
display: inline-block; |
|||
width: 0; |
|||
height: 0; |
|||
margin-left: $caret-width; |
|||
vertical-align: middle; |
|||
content: ""; |
|||
border-top: $caret-width solid; |
|||
border-right: $caret-width solid transparent; |
|||
border-left: $caret-width solid transparent; |
|||
} |
|||
|
|||
// Prevent the focus on the dropdown toggle when closing dropdowns |
|||
&:focus { |
|||
outline: 0; |
|||
} |
|||
} |
|||
|
|||
.dropup { |
|||
.dropdown-toggle { |
|||
&::after { |
|||
border-top: 0; |
|||
border-bottom: $caret-width solid; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// The dropdown menu |
|||
.dropdown-menu { |
|||
position: absolute; |
|||
top: 100%; |
|||
left: 0; |
|||
z-index: $zindex-dropdown; |
|||
display: none; // none by default, but block on "open" of the menu |
|||
float: left; |
|||
min-width: $dropdown-min-width; |
|||
padding: $dropdown-padding-y 0; |
|||
margin: $dropdown-margin-top 0 0; // override default ul |
|||
font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues |
|||
color: $body-color; |
|||
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) |
|||
list-style: none; |
|||
background-color: $dropdown-bg; |
|||
background-clip: padding-box; |
|||
border: $dropdown-border-width solid $dropdown-border-color; |
|||
@include border-radius($border-radius); |
|||
@include box-shadow($dropdown-box-shadow); |
|||
} |
|||
|
|||
// Dividers (basically an `<hr>`) within the dropdown |
|||
.dropdown-divider { |
|||
@include nav-divider($dropdown-divider-bg); |
|||
} |
|||
|
|||
// Links, buttons, and more within the dropdown menu |
|||
// |
|||
// `<button>`-specific styles are denoted with `// For <button>s` |
|||
.dropdown-item { |
|||
display: block; |
|||
width: 100%; // For `<button>`s |
|||
padding: 3px $dropdown-item-padding-x; |
|||
clear: both; |
|||
font-weight: $font-weight-normal; |
|||
color: $dropdown-link-color; |
|||
text-align: inherit; // For `<button>`s |
|||
white-space: nowrap; // prevent links from randomly breaking onto new lines |
|||
background: none; // For `<button>`s |
|||
border: 0; // For `<button>`s |
|||
|
|||
@include hover-focus { |
|||
color: $dropdown-link-hover-color; |
|||
text-decoration: none; |
|||
background-color: $dropdown-link-hover-bg; |
|||
} |
|||
|
|||
&.active, |
|||
&:active { |
|||
color: $dropdown-link-active-color; |
|||
text-decoration: none; |
|||
background-color: $dropdown-link-active-bg; |
|||
} |
|||
|
|||
&.disabled, |
|||
&:disabled { |
|||
color: $dropdown-link-disabled-color; |
|||
cursor: $cursor-disabled; |
|||
background-color: transparent; |
|||
// Remove CSS gradients if they're enabled |
|||
@if $enable-gradients { |
|||
background-image: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Open state for the dropdown |
|||
.show { |
|||
// Show the menu |
|||
> .dropdown-menu { |
|||
display: block; |
|||
} |
|||
|
|||
// Remove the outline when :focus is triggered |
|||
> a { |
|||
outline: 0; |
|||
} |
|||
} |
|||
|
|||
// Menu positioning |
|||
// |
|||
// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown |
|||
// menu with the parent. |
|||
.dropdown-menu-right { |
|||
right: 0; |
|||
left: auto; // Reset the default from `.dropdown-menu` |
|||
} |
|||
|
|||
.dropdown-menu-left { |
|||
right: auto; |
|||
left: 0; |
|||
} |
|||
|
|||
// Dropdown section headers |
|||
.dropdown-header { |
|||
display: block; |
|||
padding: $dropdown-padding-y $dropdown-item-padding-x; |
|||
margin-bottom: 0; // for use with heading elements |
|||
font-size: $font-size-sm; |
|||
color: $dropdown-header-color; |
|||
white-space: nowrap; // as with > li > a |
|||
} |
|||
|
|||
// Backdrop to catch body clicks on mobile, etc. |
|||
.dropdown-backdrop { |
|||
position: fixed; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: $zindex-dropdown-backdrop; |
|||
} |
|||
|
|||
// Allow for dropdowns to go bottom up (aka, dropup-menu) |
|||
// |
|||
// Just add .dropup after the standard .dropdown class and you're set. |
|||
|
|||
.dropup { |
|||
// Different positioning for bottom up menu |
|||
.dropdown-menu { |
|||
top: auto; |
|||
bottom: 100%; |
|||
margin-bottom: $dropdown-margin-top; |
|||
} |
|||
} |
@ -0,0 +1,388 @@ |
|||
// scss-lint:disable QualifyingElement |
|||
|
|||
// |
|||
// Textual form controls |
|||
// |
|||
|
|||
.form-control { |
|||
display: block; |
|||
width: 100%; |
|||
// // Make inputs at least the height of their button counterpart (base line-height + padding + border) |
|||
// height: $input-height; |
|||
padding: $input-padding-y $input-padding-x; |
|||
font-size: $font-size-base; |
|||
line-height: $input-line-height; |
|||
color: $input-color; |
|||
background-color: $input-bg; |
|||
// Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214. |
|||
background-image: none; |
|||
background-clip: padding-box; |
|||
border: $input-btn-border-width solid $input-border-color; |
|||
|
|||
// Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS. |
|||
@if $enable-rounded { |
|||
// Manually use the if/else instead of the mixin to account for iOS override |
|||
border-radius: $input-border-radius; |
|||
} @else { |
|||
// Otherwise undo the iOS default |
|||
border-radius: 0; |
|||
} |
|||
|
|||
@include box-shadow($input-box-shadow); |
|||
@include transition($input-transition); |
|||
|
|||
// Unstyle the caret on `<select>`s in IE10+. |
|||
&::-ms-expand { |
|||
background-color: transparent; |
|||
border: 0; |
|||
} |
|||
|
|||
// Customize the `:focus` state to imitate native WebKit styles. |
|||
@include form-control-focus(); |
|||
|
|||
// Placeholder |
|||
&::placeholder { |
|||
color: $input-color-placeholder; |
|||
// Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526. |
|||
opacity: 1; |
|||
} |
|||
|
|||
// Disabled and read-only inputs |
|||
// |
|||
// HTML5 says that controls under a fieldset > legend:first-child won't be |
|||
// disabled if the fieldset is disabled. Due to implementation difficulty, we |
|||
// don't honor that edge case; we style them as disabled anyway. |
|||
&:disabled, |
|||
&[readonly] { |
|||
background-color: $input-bg-disabled; |
|||
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655. |
|||
opacity: 1; |
|||
} |
|||
|
|||
&:disabled { |
|||
cursor: $cursor-disabled; |
|||
} |
|||
} |
|||
|
|||
select.form-control { |
|||
&:not([size]):not([multiple]) { |
|||
$select-border-width: ($border-width * 2); |
|||
height: calc(#{$input-height} + #{$select-border-width}); |
|||
} |
|||
|
|||
&:focus::-ms-value { |
|||
// Suppress the nested default white text on blue background highlight given to |
|||
// the selected option text when the (still closed) <select> receives focus |
|||
// in IE and (under certain conditions) Edge, as it looks bad and cannot be made to |
|||
// match the appearance of the native widget. |
|||
// See https://github.com/twbs/bootstrap/issues/19398. |
|||
color: $input-color; |
|||
background-color: $input-bg; |
|||
} |
|||
} |
|||
|
|||
// Make file inputs better match text inputs by forcing them to new lines. |
|||
.form-control-file, |
|||
.form-control-range { |
|||
display: block; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Labels |
|||
// |
|||
|
|||
// For use with horizontal and inline forms, when you need the label text to |
|||
// align with the form controls. |
|||
.col-form-label { |
|||
padding-top: calc(#{$input-padding-y} - #{$input-btn-border-width} * 2); |
|||
padding-bottom: calc(#{$input-padding-y} - #{$input-btn-border-width} * 2); |
|||
margin-bottom: 0; // Override the `<label>` default |
|||
} |
|||
|
|||
.col-form-label-lg { |
|||
padding-top: calc(#{$input-padding-y-lg} - #{$input-btn-border-width} * 2); |
|||
padding-bottom: calc(#{$input-padding-y-lg} - #{$input-btn-border-width} * 2); |
|||
font-size: $font-size-lg; |
|||
} |
|||
|
|||
.col-form-label-sm { |
|||
padding-top: calc(#{$input-padding-y-sm} - #{$input-btn-border-width} * 2); |
|||
padding-bottom: calc(#{$input-padding-y-sm} - #{$input-btn-border-width} * 2); |
|||
font-size: $font-size-sm; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Legends |
|||
// |
|||
|
|||
// For use with horizontal and inline forms, when you need the legend text to |
|||
// be the same size as regular labels, and to align with the form controls. |
|||
.col-form-legend { |
|||
padding-top: $input-padding-y; |
|||
padding-bottom: $input-padding-y; |
|||
margin-bottom: 0; |
|||
font-size: $font-size-base; |
|||
} |
|||
|
|||
|
|||
// Static form control text |
|||
// |
|||
// Apply class to an element to make any string of text align with labels in a |
|||
// horizontal form layout. |
|||
|
|||
.form-control-static { |
|||
padding-top: $input-padding-y; |
|||
padding-bottom: $input-padding-y; |
|||
margin-bottom: 0; // match inputs if this class comes on inputs with default margins |
|||
line-height: $input-line-height; |
|||
border: solid transparent; |
|||
border-width: $input-btn-border-width 0; |
|||
|
|||
&.form-control-sm, |
|||
&.form-control-lg { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Form control sizing |
|||
// |
|||
// Build on `.form-control` with modifier classes to decrease or increase the |
|||
// height and font-size of form controls. |
|||
// |
|||
// The `.form-group-* form-control` variations are sadly duplicated to avoid the |
|||
// issue documented in https://github.com/twbs/bootstrap/issues/15074. |
|||
|
|||
.form-control-sm { |
|||
padding: $input-padding-y-sm $input-padding-x-sm; |
|||
font-size: $font-size-sm; |
|||
@include border-radius($input-border-radius-sm); |
|||
} |
|||
|
|||
select.form-control-sm { |
|||
&:not([size]):not([multiple]) { |
|||
height: $input-height-sm; |
|||
} |
|||
} |
|||
|
|||
.form-control-lg { |
|||
padding: $input-padding-y-lg $input-padding-x-lg; |
|||
font-size: $font-size-lg; |
|||
@include border-radius($input-border-radius-lg); |
|||
} |
|||
|
|||
select.form-control-lg { |
|||
&:not([size]):not([multiple]) { |
|||
height: $input-height-lg; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Form groups |
|||
// |
|||
// Designed to help with the organization and spacing of vertical forms. For |
|||
// horizontal forms, use the predefined grid classes. |
|||
|
|||
.form-group { |
|||
margin-bottom: $form-group-margin-bottom; |
|||
} |
|||
|
|||
.form-text { |
|||
display: block; |
|||
margin-top: $form-text-margin-top; |
|||
} |
|||
|
|||
|
|||
// Checkboxes and radios |
|||
// |
|||
// Indent the labels to position radios/checkboxes as hanging controls. |
|||
|
|||
.form-check { |
|||
position: relative; |
|||
display: block; |
|||
margin-bottom: $form-check-margin-bottom; |
|||
|
|||
&.disabled { |
|||
.form-check-label { |
|||
color: $text-muted; |
|||
cursor: $cursor-disabled; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.form-check-label { |
|||
padding-left: $form-check-input-gutter; |
|||
margin-bottom: 0; // Override default `<label>` bottom margin |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.form-check-input { |
|||
position: absolute; |
|||
margin-top: $form-check-input-margin-y; |
|||
margin-left: -$form-check-input-gutter; |
|||
|
|||
&:only-child { |
|||
position: static; |
|||
} |
|||
} |
|||
|
|||
// Radios and checkboxes on same line |
|||
.form-check-inline { |
|||
display: inline-block; |
|||
|
|||
.form-check-label { |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
+ .form-check-inline { |
|||
margin-left: $form-check-inline-margin-x; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Form control feedback states |
|||
// |
|||
// Apply contextual and semantic states to individual form controls. |
|||
|
|||
.form-control-feedback { |
|||
margin-top: $form-feedback-margin-top; |
|||
} |
|||
|
|||
.form-control-success, |
|||
.form-control-warning, |
|||
.form-control-danger { |
|||
padding-right: ($input-padding-x * 3); |
|||
background-repeat: no-repeat; |
|||
background-position: center right ($input-height / 4); |
|||
background-size: ($input-height / 2) ($input-height / 2); |
|||
} |
|||
|
|||
// Form validation states |
|||
.has-success { |
|||
@include form-control-validation($brand-success); |
|||
|
|||
.form-control-success { |
|||
background-image: $form-icon-success; |
|||
} |
|||
} |
|||
|
|||
.has-warning { |
|||
@include form-control-validation($brand-warning); |
|||
|
|||
.form-control-warning { |
|||
background-image: $form-icon-warning; |
|||
} |
|||
} |
|||
|
|||
.has-danger { |
|||
@include form-control-validation($brand-danger); |
|||
|
|||
.form-control-danger { |
|||
background-image: $form-icon-danger; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Inline forms |
|||
// |
|||
// Make forms appear inline(-block) by adding the `.form-inline` class. Inline |
|||
// forms begin stacked on extra small (mobile) devices and then go inline when |
|||
// viewports reach <768px. |
|||
// |
|||
// Requires wrapping inputs and labels with `.form-group` for proper display of |
|||
// default HTML form controls and our custom form controls (e.g., input groups). |
|||
|
|||
.form-inline { |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height) |
|||
|
|||
// Because we use flex, the initial sizing of checkboxes is collapsed and |
|||
// doesn't occupy the full-width (which is what we want for xs grid tier), |
|||
// so we force that here. |
|||
.form-check { |
|||
width: 100%; |
|||
} |
|||
|
|||
// Kick in the inline |
|||
@include media-breakpoint-up(sm) { |
|||
label { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
// Inline-block all the things for "inline" |
|||
.form-group { |
|||
display: flex; |
|||
flex: 0 0 auto; |
|||
flex-flow: row wrap; |
|||
align-items: center; |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
// Allow folks to *not* use `.form-group` |
|||
.form-control { |
|||
display: inline-block; |
|||
width: auto; // Prevent labels from stacking above inputs in `.form-group` |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
// Make static controls behave like regular ones |
|||
.form-control-static { |
|||
display: inline-block; |
|||
} |
|||
|
|||
.input-group { |
|||
width: auto; |
|||
} |
|||
|
|||
.form-control-label { |
|||
margin-bottom: 0; |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
// Remove default margin on radios/checkboxes that were used for stacking, and |
|||
// then undo the floating of radios and checkboxes to match. |
|||
.form-check { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
width: auto; |
|||
margin-top: 0; |
|||
margin-bottom: 0; |
|||
} |
|||
.form-check-label { |
|||
padding-left: 0; |
|||
} |
|||
.form-check-input { |
|||
position: relative; |
|||
margin-top: 0; |
|||
margin-right: $form-check-input-margin-x; |
|||
margin-left: 0; |
|||
} |
|||
|
|||
// Custom form controls |
|||
.custom-control { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding-left: 0; |
|||
} |
|||
.custom-control-indicator { |
|||
position: static; |
|||
display: inline-block; |
|||
margin-right: $form-check-input-margin-x; // Flexbox alignment means we lose our HTML space here, so we compensate. |
|||
vertical-align: text-bottom; |
|||
} |
|||
|
|||
// Re-override the feedback icon. |
|||
.has-feedback .form-control-feedback { |
|||
top: 0; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
// Container widths |
|||
// |
|||
// Set the container width, and override it for fixed navbars in media queries. |
|||
|
|||
@if $enable-grid-classes { |
|||
.container { |
|||
@include make-container(); |
|||
@include make-container-max-widths(); |
|||
} |
|||
} |
|||
|
|||
// Fluid container |
|||
// |
|||
// Utilizes the mixin meant for fixed width containers, but without any defined |
|||
// width for fluid, full width layouts. |
|||
|
|||
@if $enable-grid-classes { |
|||
.container-fluid { |
|||
@include make-container(); |
|||
} |
|||
} |
|||
|
|||
// Row |
|||
// |
|||
// Rows contain and clear the floats of your columns. |
|||
|
|||
@if $enable-grid-classes { |
|||
.row { |
|||
@include make-row(); |
|||
} |
|||
|
|||
// Remove the negative margin from default .row, then the horizontal padding |
|||
// from all immediate children columns (to prevent runaway style inheritance). |
|||
.no-gutters { |
|||
margin-right: 0; |
|||
margin-left: 0; |
|||
|
|||
> .col, |
|||
> [class*="col-"] { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Columns |
|||
// |
|||
// Common styles for small and large grid columns |
|||
|
|||
@if $enable-grid-classes { |
|||
@include make-grid-columns(); |
|||
} |
@ -0,0 +1,43 @@ |
|||
// Responsive images (ensure images don't scale beyond their parents) |
|||
// |
|||
// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s. |
|||
// We previously tried the "images are responsive by default" approach in Bootstrap v2, |
|||
// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps) |
|||
// which weren't expecting the images within themselves to be involuntarily resized. |
|||
// See also https://github.com/twbs/bootstrap/issues/18178 |
|||
.img-fluid { |
|||
@include img-fluid; |
|||
} |
|||
|
|||
|
|||
// Image thumbnails |
|||
.img-thumbnail { |
|||
padding: $thumbnail-padding; |
|||
background-color: $thumbnail-bg; |
|||
border: $thumbnail-border-width solid $thumbnail-border-color; |
|||
@include border-radius($thumbnail-border-radius); |
|||
@include transition($thumbnail-transition); |
|||
@include box-shadow($thumbnail-box-shadow); |
|||
|
|||
// Keep them at most 100% wide |
|||
@include img-fluid; |
|||
} |
|||
|
|||
// |
|||
// Figures |
|||
// |
|||
|
|||
.figure { |
|||
// Ensures the caption's text aligns with the image. |
|||
display: inline-block; |
|||
} |
|||
|
|||
.figure-img { |
|||
margin-bottom: ($spacer-y / 2); |
|||
line-height: 1; |
|||
} |
|||
|
|||
.figure-caption { |
|||
font-size: $figure-caption-font-size; |
|||
color: $figure-caption-color; |
|||
} |
@ -0,0 +1,178 @@ |
|||
// |
|||
// Base styles |
|||
// |
|||
|
|||
.input-group { |
|||
position: relative; |
|||
display: flex; |
|||
width: 100%; |
|||
|
|||
.form-control { |
|||
// Ensure that the input is always above the *appended* addon button for |
|||
// proper border colors. |
|||
position: relative; |
|||
z-index: 2; |
|||
flex: 1 1 auto; |
|||
// Add width 1% and flex-basis auto to ensure that button will not wrap out |
|||
// the column. Applies to IE Edge+ and Firefox. Chrome does not require this. |
|||
width: 1%; |
|||
margin-bottom: 0; |
|||
|
|||
// Bring the "active" form control to the front |
|||
@include hover-focus-active { |
|||
z-index: 3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.input-group-addon, |
|||
.input-group-btn, |
|||
.input-group .form-control { |
|||
// Vertically centers the content of the addons within the input group |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
|
|||
&:not(:first-child):not(:last-child) { |
|||
@include border-radius(0); |
|||
} |
|||
} |
|||
|
|||
.input-group-addon, |
|||
.input-group-btn { |
|||
white-space: nowrap; |
|||
vertical-align: middle; // Match the inputs |
|||
} |
|||
|
|||
|
|||
// Sizing options |
|||
// |
|||
// Remix the default form control sizing classes into new ones for easier |
|||
// manipulation. |
|||
|
|||
.input-group-lg > .form-control, |
|||
.input-group-lg > .input-group-addon, |
|||
.input-group-lg > .input-group-btn > .btn { |
|||
@extend .form-control-lg; |
|||
} |
|||
.input-group-sm > .form-control, |
|||
.input-group-sm > .input-group-addon, |
|||
.input-group-sm > .input-group-btn > .btn { |
|||
@extend .form-control-sm; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Text input groups |
|||
// |
|||
|
|||
.input-group-addon { |
|||
padding: $input-padding-y $input-padding-x; |
|||
margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom |
|||
font-size: $font-size-base; // Match inputs |
|||
font-weight: $font-weight-normal; |
|||
line-height: $input-line-height; |
|||
color: $input-color; |
|||
text-align: center; |
|||
background-color: $input-group-addon-bg; |
|||
border: $input-btn-border-width solid $input-group-addon-border-color; |
|||
@include border-radius($input-border-radius); |
|||
|
|||
// Sizing |
|||
&.form-control-sm { |
|||
padding: $input-padding-y-sm $input-padding-x-sm; |
|||
font-size: $font-size-sm; |
|||
@include border-radius($input-border-radius-sm); |
|||
} |
|||
&.form-control-lg { |
|||
padding: $input-padding-y-lg $input-padding-x-lg; |
|||
font-size: $font-size-lg; |
|||
@include border-radius($input-border-radius-lg); |
|||
} |
|||
|
|||
// scss-lint:disable QualifyingElement |
|||
// Nuke default margins from checkboxes and radios to vertically center within. |
|||
input[type="radio"], |
|||
input[type="checkbox"] { |
|||
margin-top: 0; |
|||
} |
|||
// scss-lint:enable QualifyingElement |
|||
} |
|||
|
|||
|
|||
// |
|||
// Reset rounded corners |
|||
// |
|||
|
|||
.input-group .form-control:not(:last-child), |
|||
.input-group-addon:not(:last-child), |
|||
.input-group-btn:not(:last-child) > .btn, |
|||
.input-group-btn:not(:last-child) > .btn-group > .btn, |
|||
.input-group-btn:not(:last-child) > .dropdown-toggle, |
|||
.input-group-btn:not(:first-child) > .btn:not(:last-child):not(.dropdown-toggle), |
|||
.input-group-btn:not(:first-child) > .btn-group:not(:last-child) > .btn { |
|||
@include border-right-radius(0); |
|||
} |
|||
.input-group-addon:not(:last-child) { |
|||
border-right: 0; |
|||
} |
|||
.input-group .form-control:not(:first-child), |
|||
.input-group-addon:not(:first-child), |
|||
.input-group-btn:not(:first-child) > .btn, |
|||
.input-group-btn:not(:first-child) > .btn-group > .btn, |
|||
.input-group-btn:not(:first-child) > .dropdown-toggle, |
|||
.input-group-btn:not(:last-child) > .btn:not(:first-child), |
|||
.input-group-btn:not(:last-child) > .btn-group:not(:first-child) > .btn { |
|||
@include border-left-radius(0); |
|||
} |
|||
.form-control + .input-group-addon:not(:first-child) { |
|||
border-left: 0; |
|||
} |
|||
|
|||
// |
|||
// Button input groups |
|||
// |
|||
|
|||
.input-group-btn { |
|||
position: relative; |
|||
// Jankily prevent input button groups from wrapping with `white-space` and |
|||
// `font-size` in combination with `inline-block` on buttons. |
|||
font-size: 0; |
|||
white-space: nowrap; |
|||
|
|||
// Negative margin for spacing, position for bringing hovered/focused/actived |
|||
// element above the siblings. |
|||
> .btn { |
|||
position: relative; |
|||
// Vertically stretch the button and center its content |
|||
flex: 1; |
|||
|
|||
+ .btn { |
|||
margin-left: (-$input-btn-border-width); |
|||
} |
|||
|
|||
// Bring the "active" button to the front |
|||
@include hover-focus-active { |
|||
z-index: 3; |
|||
} |
|||
} |
|||
|
|||
// Negative margin to only have a single, shared border between the two |
|||
&:not(:last-child) { |
|||
> .btn, |
|||
> .btn-group { |
|||
margin-right: (-$input-btn-border-width); |
|||
} |
|||
} |
|||
&:not(:first-child) { |
|||
> .btn, |
|||
> .btn-group { |
|||
z-index: 2; |
|||
margin-left: (-$input-btn-border-width); |
|||
// Because specificity |
|||
@include hover-focus-active { |
|||
z-index: 3; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
.jumbotron { |
|||
padding: $jumbotron-padding ($jumbotron-padding / 2); |
|||
margin-bottom: $jumbotron-padding; |
|||
background-color: $jumbotron-bg; |
|||
@include border-radius($border-radius-lg); |
|||
|
|||
@include media-breakpoint-up(sm) { |
|||
padding: ($jumbotron-padding * 2) $jumbotron-padding; |
|||
} |
|||
} |
|||
|
|||
.jumbotron-hr { |
|||
border-top-color: darken($jumbotron-bg, 10%); |
|||
} |
|||
|
|||
.jumbotron-fluid { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
@include border-radius(0); |
|||
} |
@ -0,0 +1,141 @@ |
|||
// Base class |
|||
// |
|||
// Easily usable on <ul>, <ol>, or <div>. |
|||
|
|||
.list-group { |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
// No need to set list-style: none; since .list-group-item is block level |
|||
padding-left: 0; // reset padding because ul and ol |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
|
|||
// Interactive list items |
|||
// |
|||
// Use anchor or button elements instead of `li`s or `div`s to create interactive |
|||
// list items. Includes an extra `.active` modifier class for selected items. |
|||
|
|||
.list-group-item-action { |
|||
width: 100%; // For `<button>`s (anchors become 100% by default though) |
|||
color: $list-group-link-color; |
|||
text-align: inherit; // For `<button>`s (anchors inherit) |
|||
|
|||
.list-group-item-heading { |
|||
color: $list-group-link-heading-color; |
|||
} |
|||
|
|||
// Hover state |
|||
@include hover-focus { |
|||
color: $list-group-link-hover-color; |
|||
text-decoration: none; |
|||
background-color: $list-group-hover-bg; |
|||
} |
|||
|
|||
&:active { |
|||
color: $list-group-link-active-color; |
|||
background-color: $list-group-link-active-bg; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Individual list items |
|||
// |
|||
// Use on `li`s or `div`s within the `.list-group` parent. |
|||
|
|||
.list-group-item { |
|||
position: relative; |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
align-items: center; |
|||
padding: $list-group-item-padding-y $list-group-item-padding-x; |
|||
// Place the border on the list items and negative margin up for better styling |
|||
margin-bottom: -$list-group-border-width; |
|||
background-color: $list-group-bg; |
|||
border: $list-group-border-width solid $list-group-border-color; |
|||
|
|||
&:first-child { |
|||
@include border-top-radius($list-group-border-radius); |
|||
} |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
@include border-bottom-radius($list-group-border-radius); |
|||
} |
|||
|
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
|
|||
&.disabled, |
|||
&:disabled { |
|||
color: $list-group-disabled-color; |
|||
cursor: $cursor-disabled; |
|||
background-color: $list-group-disabled-bg; |
|||
|
|||
// Force color to inherit for custom content |
|||
.list-group-item-heading { |
|||
color: inherit; |
|||
} |
|||
.list-group-item-text { |
|||
color: $list-group-disabled-text-color; |
|||
} |
|||
} |
|||
|
|||
// Include both here for `<a>`s and `<button>`s |
|||
&.active { |
|||
z-index: 2; // Place active items above their siblings for proper border styling |
|||
color: $list-group-active-color; |
|||
background-color: $list-group-active-bg; |
|||
border-color: $list-group-active-border; |
|||
|
|||
// Force color to inherit for custom content |
|||
.list-group-item-heading, |
|||
.list-group-item-heading > small, |
|||
.list-group-item-heading > .small { |
|||
color: inherit; |
|||
} |
|||
|
|||
.list-group-item-text { |
|||
color: $list-group-active-text-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Flush list items |
|||
// |
|||
// Remove borders and border-radius to keep list group items edge-to-edge. Most |
|||
// useful within other components (e.g., cards). |
|||
|
|||
.list-group-flush { |
|||
.list-group-item { |
|||
border-right: 0; |
|||
border-left: 0; |
|||
border-radius: 0; |
|||
} |
|||
|
|||
&:first-child { |
|||
.list-group-item:first-child { |
|||
border-top: 0; |
|||
} |
|||
} |
|||
|
|||
&:last-child { |
|||
.list-group-item:last-child { |
|||
border-bottom: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Contextual variants |
|||
// |
|||
// Add modifier classes to change text and background color on individual items. |
|||
// Organizationally, this must come after the `:hover` states. |
|||
|
|||
@include list-group-item-variant(success, $state-success-bg, $state-success-text); |
|||
@include list-group-item-variant(info, $state-info-bg, $state-info-text); |
|||
@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text); |
|||
@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text); |
@ -0,0 +1,8 @@ |
|||
.media { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
} |
|||
|
|||
.media-body { |
|||
flex: 1; |
|||
} |
@ -0,0 +1,57 @@ |
|||
// Toggles |
|||
// |
|||
// Used in conjunction with global variables to enable certain theme features. |
|||
|
|||
@mixin box-shadow($shadow...) { |
|||
@if $enable-shadows { |
|||
box-shadow: $shadow; |
|||
} |
|||
} |
|||
|
|||
@mixin transition($transition...) { |
|||
@if $enable-transitions { |
|||
@if length($transition) == 0 { |
|||
transition: $transition-base; |
|||
} @else { |
|||
transition: $transition; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Utilities |
|||
@import "mixins/breakpoints"; |
|||
@import "mixins/hover"; |
|||
@import "mixins/image"; |
|||
@import "mixins/badge"; |
|||
@import "mixins/resize"; |
|||
@import "mixins/screen-reader"; |
|||
@import "mixins/size"; |
|||
@import "mixins/reset-text"; |
|||
@import "mixins/text-emphasis"; |
|||
@import "mixins/text-hide"; |
|||
@import "mixins/text-truncate"; |
|||
@import "mixins/transforms"; |
|||
@import "mixins/visibility"; |
|||
|
|||
// // Components |
|||
@import "mixins/alert"; |
|||
@import "mixins/buttons"; |
|||
@import "mixins/cards"; |
|||
@import "mixins/pagination"; |
|||
@import "mixins/lists"; |
|||
@import "mixins/list-group"; |
|||
@import "mixins/nav-divider"; |
|||
@import "mixins/forms"; |
|||
@import "mixins/table-row"; |
|||
|
|||
// // Skins |
|||
@import "mixins/background-variant"; |
|||
@import "mixins/border-radius"; |
|||
@import "mixins/gradients"; |
|||
|
|||
// // Layout |
|||
@import "mixins/clearfix"; |
|||
// @import "mixins/navbar-align"; |
|||
@import "mixins/grid-framework"; |
|||
@import "mixins/grid"; |
|||
@import "mixins/float"; |
@ -0,0 +1,142 @@ |
|||
// .modal-open - body class for killing the scroll |
|||
// .modal - container to scroll within |
|||
// .modal-dialog - positioning shell for the actual modal |
|||
// .modal-content - actual modal w/ bg and corners and stuff |
|||
|
|||
|
|||
// Kill the scroll on the body |
|||
.modal-open { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
// Container that the modal scrolls within |
|||
.modal { |
|||
position: fixed; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: $zindex-modal; |
|||
display: none; |
|||
overflow: hidden; |
|||
// Prevent Chrome on Windows from adding a focus outline. For details, see |
|||
// https://github.com/twbs/bootstrap/pull/10951. |
|||
outline: 0; |
|||
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a |
|||
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342 |
|||
// See also https://github.com/twbs/bootstrap/issues/17695 |
|||
|
|||
// When fading in the modal, animate it to slide down |
|||
&.fade .modal-dialog { |
|||
@include transition($modal-transition); |
|||
transform: translate(0, -25%); |
|||
} |
|||
&.show .modal-dialog { transform: translate(0, 0); } |
|||
} |
|||
.modal-open .modal { |
|||
overflow-x: hidden; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
// Shell div to position the modal with bottom padding |
|||
.modal-dialog { |
|||
position: relative; |
|||
width: auto; |
|||
margin: $modal-dialog-margin; |
|||
} |
|||
|
|||
// Actual modal |
|||
.modal-content { |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background-color: $modal-content-bg; |
|||
background-clip: padding-box; |
|||
border: $modal-content-border-width solid $modal-content-border-color; |
|||
@include border-radius($border-radius-lg); |
|||
@include box-shadow($modal-content-xs-box-shadow); |
|||
// Remove focus outline from opened modal |
|||
outline: 0; |
|||
} |
|||
|
|||
// Modal background |
|||
.modal-backdrop { |
|||
position: fixed; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: $zindex-modal-backdrop; |
|||
background-color: $modal-backdrop-bg; |
|||
|
|||
// Fade for backdrop |
|||
&.fade { opacity: 0; } |
|||
&.show { opacity: $modal-backdrop-opacity; } |
|||
} |
|||
|
|||
// Modal header |
|||
// Top section of the modal w/ title and dismiss |
|||
.modal-header { |
|||
display: flex; |
|||
align-items: center; // vertically center it |
|||
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends |
|||
padding: $modal-header-padding; |
|||
border-bottom: $modal-header-border-width solid $modal-header-border-color; |
|||
} |
|||
|
|||
// Title text within header |
|||
.modal-title { |
|||
margin-bottom: 0; |
|||
line-height: $modal-title-line-height; |
|||
} |
|||
|
|||
// Modal body |
|||
// Where all modal content resides (sibling of .modal-header and .modal-footer) |
|||
.modal-body { |
|||
position: relative; |
|||
// Enable `flex-grow: 1` so that the body take up as much space as possible |
|||
// when should there be a fixed height on `.modal-dialog`. |
|||
flex: 1 1 auto; |
|||
padding: $modal-inner-padding; |
|||
} |
|||
|
|||
// Footer (for actions) |
|||
.modal-footer { |
|||
display: flex; |
|||
align-items: center; // vertically center |
|||
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items |
|||
padding: $modal-inner-padding; |
|||
border-top: $modal-footer-border-width solid $modal-footer-border-color; |
|||
|
|||
// Easily place margin between footer elements |
|||
> :not(:first-child) { margin-left: .25rem; } |
|||
> :not(:last-child) { margin-right: .25rem; } |
|||
} |
|||
|
|||
// Measure scrollbar width for padding body during modal show/hide |
|||
.modal-scrollbar-measure { |
|||
position: absolute; |
|||
top: -9999px; |
|||
width: 50px; |
|||
height: 50px; |
|||
overflow: scroll; |
|||
} |
|||
|
|||
// Scale up the modal |
|||
@include media-breakpoint-up(sm) { |
|||
// Automatically set modal's width for larger viewports |
|||
.modal-dialog { |
|||
max-width: $modal-md; |
|||
margin: $modal-dialog-sm-up-margin-y auto; |
|||
} |
|||
|
|||
.modal-content { |
|||
@include box-shadow($modal-content-sm-up-box-shadow); |
|||
} |
|||
|
|||
.modal-sm { max-width: $modal-sm; } |
|||
} |
|||
|
|||
@include media-breakpoint-up(lg) { |
|||
.modal-lg { max-width: $modal-lg; } |
|||
} |
@ -0,0 +1,119 @@ |
|||
// Base class |
|||
// |
|||
// Kickstart any navigation component with a set of style resets. Works with |
|||
// `<nav>`s or `<ul>`s. |
|||
|
|||
.nav { |
|||
display: flex; |
|||
padding-left: 0; |
|||
margin-bottom: 0; |
|||
list-style: none; |
|||
} |
|||
|
|||
.nav-link { |
|||
display: block; |
|||
padding: $nav-link-padding; |
|||
|
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
|
|||
// Disabled state lightens text and removes hover/tab effects |
|||
&.disabled { |
|||
color: $nav-disabled-link-color; |
|||
cursor: $cursor-disabled; |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Tabs |
|||
// |
|||
|
|||
.nav-tabs { |
|||
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color; |
|||
|
|||
.nav-item { |
|||
margin-bottom: -$nav-tabs-border-width; |
|||
} |
|||
|
|||
.nav-link { |
|||
border: $nav-tabs-border-width solid transparent; |
|||
@include border-top-radius($nav-tabs-border-radius); |
|||
|
|||
@include hover-focus { |
|||
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color; |
|||
} |
|||
|
|||
&.disabled { |
|||
color: $nav-disabled-link-color; |
|||
background-color: transparent; |
|||
border-color: transparent; |
|||
} |
|||
} |
|||
|
|||
.nav-link.active, |
|||
.nav-item.show .nav-link { |
|||
color: $nav-tabs-active-link-hover-color; |
|||
background-color: $nav-tabs-active-link-hover-bg; |
|||
border-color: $nav-tabs-active-link-hover-border-color $nav-tabs-active-link-hover-border-color $nav-tabs-active-link-hover-bg; |
|||
} |
|||
|
|||
.dropdown-menu { |
|||
// Make dropdown border overlap tab border |
|||
margin-top: -$nav-tabs-border-width; |
|||
// Remove the top rounded corners here since there is a hard edge above the menu |
|||
@include border-top-radius(0); |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Pills |
|||
// |
|||
|
|||
.nav-pills { |
|||
.nav-link { |
|||
@include border-radius($nav-pills-border-radius); |
|||
} |
|||
|
|||
.nav-link.active, |
|||
.nav-item.show .nav-link { |
|||
color: $nav-pills-active-link-color; |
|||
cursor: default; |
|||
background-color: $nav-pills-active-link-bg; |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Justified variants |
|||
// |
|||
|
|||
.nav-fill { |
|||
.nav-item { |
|||
flex: 1 1 auto; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.nav-justified { |
|||
.nav-item { |
|||
flex: 1 1 100%; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Tabbable tabs |
|||
// |
|||
// Hide tabbable panes to start, show them when `.active` |
|||
|
|||
.tab-content { |
|||
> .tab-pane { |
|||
display: none; |
|||
} |
|||
> .active { |
|||
display: block; |
|||
} |
|||
} |
@ -0,0 +1,268 @@ |
|||
// Contents |
|||
// |
|||
// Navbar |
|||
// Navbar brand |
|||
// Navbar nav |
|||
// Navbar text |
|||
// Navbar divider |
|||
// Responsive navbar |
|||
// Navbar position |
|||
// Navbar themes |
|||
|
|||
|
|||
// Navbar |
|||
// |
|||
// Provide a static navbar from which we expand to create full-width, fixed, and |
|||
// other navbar variations. |
|||
|
|||
.navbar { |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: $navbar-padding-y $navbar-padding-x; |
|||
} |
|||
|
|||
|
|||
// Navbar brand |
|||
// |
|||
// Used for brand, project, or site names. |
|||
|
|||
.navbar-brand { |
|||
display: inline-block; |
|||
padding-top: .25rem; |
|||
padding-bottom: .25rem; |
|||
margin-right: $navbar-padding-x; |
|||
font-size: $font-size-lg; |
|||
line-height: inherit; |
|||
white-space: nowrap; |
|||
|
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Navbar nav |
|||
// |
|||
// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`). |
|||
|
|||
.navbar-nav { |
|||
display: flex; |
|||
flex-direction: column; // cannot use `inherit` to get the `.navbar`s value |
|||
padding-left: 0; |
|||
margin-bottom: 0; |
|||
list-style: none; |
|||
|
|||
.nav-link { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Navbar text |
|||
// |
|||
// |
|||
|
|||
.navbar-text { |
|||
display: inline-block; |
|||
padding-top: .425rem; |
|||
padding-bottom: .425rem; |
|||
} |
|||
|
|||
|
|||
// Responsive navbar |
|||
// |
|||
// Custom styles for responsive collapsing and toggling of navbar contents. |
|||
// Powered by the collapse Bootstrap JavaScript plugin. |
|||
|
|||
// Button for toggling the navbar when in its collapsed state |
|||
.navbar-toggler { |
|||
align-self: flex-start; // Prevent toggler from growing to full width when it's the only visible navbar child |
|||
padding: $navbar-toggler-padding-y $navbar-toggler-padding-x; |
|||
font-size: $navbar-toggler-font-size; |
|||
line-height: 1; |
|||
background: transparent; // remove default button style |
|||
border: $border-width solid transparent; // remove default button style |
|||
@include border-radius($navbar-toggler-border-radius); |
|||
|
|||
@include hover-focus { |
|||
text-decoration: none; |
|||
} |
|||
} |
|||
|
|||
// Keep as a separate element so folks can easily override it with another icon |
|||
// or image file as needed. |
|||
.navbar-toggler-icon { |
|||
display: inline-block; |
|||
width: 1.5em; |
|||
height: 1.5em; |
|||
vertical-align: middle; |
|||
content: ""; |
|||
background: no-repeat center center; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
// Use `position` on the toggler to prevent it from being auto placed as a flex |
|||
// item and allow easy placement. |
|||
.navbar-toggler-left { |
|||
position: absolute; |
|||
left: $navbar-padding-x; |
|||
} |
|||
.navbar-toggler-right { |
|||
position: absolute; |
|||
right: $navbar-padding-x; |
|||
} |
|||
|
|||
// Generate series of `.navbar-toggleable-*` responsive classes for configuring |
|||
// where your navbar collapses. |
|||
.navbar-toggleable { |
|||
@each $breakpoint in map-keys($grid-breakpoints) { |
|||
$next: breakpoint-next($breakpoint, $grid-breakpoints); |
|||
$infix: breakpoint-infix($breakpoint, $grid-breakpoints); |
|||
|
|||
&#{$infix} { |
|||
@include media-breakpoint-down($breakpoint) { |
|||
.navbar-nav { |
|||
.dropdown-menu { |
|||
position: static; |
|||
float: none; |
|||
} |
|||
} |
|||
|
|||
> .container { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
|
|||
@include media-breakpoint-up($next) { |
|||
flex-direction: row; |
|||
flex-wrap: nowrap; |
|||
align-items: center; |
|||
|
|||
.navbar-nav { |
|||
flex-direction: row; |
|||
|
|||
.nav-link { |
|||
padding-right: .5rem; |
|||
padding-left: .5rem; |
|||
} |
|||
} |
|||
|
|||
// For nesting containers, have to redeclare for alignment purposes |
|||
> .container { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
align-items: center; |
|||
} |
|||
|
|||
// scss-lint:disable ImportantRule |
|||
.navbar-collapse { |
|||
display: flex !important; |
|||
width: 100%; |
|||
} |
|||
// scss-lint:enable ImportantRule |
|||
|
|||
.navbar-toggler { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Navbar themes |
|||
// |
|||
// Styles for switching between navbars with light or dark background. |
|||
|
|||
// Dark links against a light background |
|||
.navbar-light { |
|||
.navbar-brand, |
|||
.navbar-toggler { |
|||
color: $navbar-light-active-color; |
|||
|
|||
@include hover-focus { |
|||
color: $navbar-light-active-color; |
|||
} |
|||
} |
|||
|
|||
.navbar-nav { |
|||
.nav-link { |
|||
color: $navbar-light-color; |
|||
|
|||
@include hover-focus { |
|||
color: $navbar-light-hover-color; |
|||
} |
|||
|
|||
&.disabled { |
|||
color: $navbar-light-disabled-color; |
|||
} |
|||
} |
|||
|
|||
.open > .nav-link, |
|||
.active > .nav-link, |
|||
.nav-link.open, |
|||
.nav-link.active { |
|||
color: $navbar-light-active-color; |
|||
} |
|||
} |
|||
|
|||
.navbar-toggler { |
|||
border-color: $navbar-light-toggler-border; |
|||
} |
|||
|
|||
.navbar-toggler-icon { |
|||
background-image: $navbar-light-toggler-bg; |
|||
} |
|||
|
|||
.navbar-text { |
|||
color: $navbar-light-color; |
|||
} |
|||
} |
|||
|
|||
// White links against a dark background |
|||
.navbar-inverse { |
|||
.navbar-brand, |
|||
.navbar-toggler { |
|||
color: $navbar-inverse-active-color; |
|||
|
|||
@include hover-focus { |
|||
color: $navbar-inverse-active-color; |
|||
} |
|||
} |
|||
|
|||
.navbar-nav { |
|||
.nav-link { |
|||
color: $navbar-inverse-color; |
|||
|
|||
@include hover-focus { |
|||
color: $navbar-inverse-hover-color; |
|||
} |
|||
|
|||
&.disabled { |
|||
color: $navbar-inverse-disabled-color; |
|||
} |
|||
} |
|||
|
|||
.open > .nav-link, |
|||
.active > .nav-link, |
|||
.nav-link.open, |
|||
.nav-link.active { |
|||
color: $navbar-inverse-active-color; |
|||
} |
|||
} |
|||
|
|||
.navbar-toggler { |
|||
border-color: $navbar-inverse-toggler-border; |
|||
} |
|||
|
|||
.navbar-toggler-icon { |
|||
background-image: $navbar-inverse-toggler-bg; |
|||
} |
|||
|
|||
.navbar-text { |
|||
color: $navbar-inverse-color; |
|||
} |
|||
} |
@ -0,0 +1,461 @@ |
|||
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ |
|||
|
|||
// |
|||
// 1. Change the default font family in all browsers (opinionated). |
|||
// 2. Correct the line height in all browsers. |
|||
// 3. Prevent adjustments of font size after orientation changes in |
|||
// IE on Windows Phone and in iOS. |
|||
// |
|||
|
|||
// Document |
|||
// ========================================================================== |
|||
|
|||
html { |
|||
font-family: sans-serif; // 1 |
|||
line-height: 1.15; // 2 |
|||
-ms-text-size-adjust: 100%; // 3 |
|||
-webkit-text-size-adjust: 100%; // 3 |
|||
} |
|||
|
|||
// Sections |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Remove the margin in all browsers (opinionated). |
|||
// |
|||
|
|||
body { |
|||
margin: 0; |
|||
} |
|||
|
|||
// |
|||
// Add the correct display in IE 9-. |
|||
// |
|||
|
|||
article, |
|||
aside, |
|||
footer, |
|||
header, |
|||
nav, |
|||
section { |
|||
display: block; |
|||
} |
|||
|
|||
// |
|||
// Correct the font size and margin on `h1` elements within `section` and |
|||
// `article` contexts in Chrome, Firefox, and Safari. |
|||
// |
|||
|
|||
h1 { |
|||
font-size: 2em; |
|||
margin: 0.67em 0; |
|||
} |
|||
|
|||
// Grouping content |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Add the correct display in IE 9-. |
|||
// 1. Add the correct display in IE. |
|||
// |
|||
|
|||
figcaption, |
|||
figure, |
|||
main { // 1 |
|||
display: block; |
|||
} |
|||
|
|||
// |
|||
// Add the correct margin in IE 8. |
|||
// |
|||
|
|||
figure { |
|||
margin: 1em 40px; |
|||
} |
|||
|
|||
// |
|||
// 1. Add the correct box sizing in Firefox. |
|||
// 2. Show the overflow in Edge and IE. |
|||
// |
|||
|
|||
hr { |
|||
box-sizing: content-box; // 1 |
|||
height: 0; // 1 |
|||
overflow: visible; // 2 |
|||
} |
|||
|
|||
// |
|||
// 1. Correct the inheritance and scaling of font size in all browsers. |
|||
// 2. Correct the odd `em` font sizing in all browsers. |
|||
// |
|||
|
|||
pre { |
|||
font-family: monospace, monospace; // 1 |
|||
font-size: 1em; // 2 |
|||
} |
|||
|
|||
// Text-level semantics |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// 1. Remove the gray background on active links in IE 10. |
|||
// 2. Remove gaps in links underline in iOS 8+ and Safari 8+. |
|||
// |
|||
|
|||
a { |
|||
background-color: transparent; // 1 |
|||
-webkit-text-decoration-skip: objects; // 2 |
|||
} |
|||
|
|||
// |
|||
// Remove the outline on focused links when they are also active or hovered |
|||
// in all browsers (opinionated). |
|||
// |
|||
|
|||
a:active, |
|||
a:hover { |
|||
outline-width: 0; |
|||
} |
|||
|
|||
// |
|||
// 1. Remove the bottom border in Firefox 39-. |
|||
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. |
|||
// |
|||
|
|||
abbr[title] { |
|||
border-bottom: none; // 1 |
|||
text-decoration: underline; // 2 |
|||
text-decoration: underline dotted; // 2 |
|||
} |
|||
|
|||
// |
|||
// Prevent the duplicate application of `bolder` by the next rule in Safari 6. |
|||
// |
|||
|
|||
b, |
|||
strong { |
|||
font-weight: inherit; |
|||
} |
|||
|
|||
// |
|||
// Add the correct font weight in Chrome, Edge, and Safari. |
|||
// |
|||
|
|||
b, |
|||
strong { |
|||
font-weight: bolder; |
|||
} |
|||
|
|||
// |
|||
// 1. Correct the inheritance and scaling of font size in all browsers. |
|||
// 2. Correct the odd `em` font sizing in all browsers. |
|||
// |
|||
|
|||
code, |
|||
kbd, |
|||
samp { |
|||
font-family: monospace, monospace; // 1 |
|||
font-size: 1em; // 2 |
|||
} |
|||
|
|||
// |
|||
// Add the correct font style in Android 4.3-. |
|||
// |
|||
|
|||
dfn { |
|||
font-style: italic; |
|||
} |
|||
|
|||
// |
|||
// Add the correct background and color in IE 9-. |
|||
// |
|||
|
|||
mark { |
|||
background-color: #ff0; |
|||
color: #000; |
|||
} |
|||
|
|||
// |
|||
// Add the correct font size in all browsers. |
|||
// |
|||
|
|||
small { |
|||
font-size: 80%; |
|||
} |
|||
|
|||
// |
|||
// Prevent `sub` and `sup` elements from affecting the line height in |
|||
// all browsers. |
|||
// |
|||
|
|||
sub, |
|||
sup { |
|||
font-size: 75%; |
|||
line-height: 0; |
|||
position: relative; |
|||
vertical-align: baseline; |
|||
} |
|||
|
|||
sub { |
|||
bottom: -0.25em; |
|||
} |
|||
|
|||
sup { |
|||
top: -0.5em; |
|||
} |
|||
|
|||
// Embedded content |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Add the correct display in IE 9-. |
|||
// |
|||
|
|||
audio, |
|||
video { |
|||
display: inline-block; |
|||
} |
|||
|
|||
// |
|||
// Add the correct display in iOS 4-7. |
|||
// |
|||
|
|||
audio:not([controls]) { |
|||
display: none; |
|||
height: 0; |
|||
} |
|||
|
|||
// |
|||
// Remove the border on images inside links in IE 10-. |
|||
// |
|||
|
|||
img { |
|||
border-style: none; |
|||
} |
|||
|
|||
// |
|||
// Hide the overflow in IE. |
|||
// |
|||
|
|||
svg:not(:root) { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
// Forms |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// 1. Change the font styles in all browsers (opinionated). |
|||
// 2. Remove the margin in Firefox and Safari. |
|||
// |
|||
|
|||
button, |
|||
input, |
|||
optgroup, |
|||
select, |
|||
textarea { |
|||
font-family: sans-serif; // 1 |
|||
font-size: 100%; // 1 |
|||
line-height: 1.15; // 1 |
|||
margin: 0; // 2 |
|||
} |
|||
|
|||
// |
|||
// Show the overflow in IE. |
|||
// 1. Show the overflow in Edge. |
|||
// |
|||
|
|||
button, |
|||
input { // 1 |
|||
overflow: visible; |
|||
} |
|||
|
|||
// |
|||
// Remove the inheritance of text transform in Edge, Firefox, and IE. |
|||
// 1. Remove the inheritance of text transform in Firefox. |
|||
// |
|||
|
|||
button, |
|||
select { // 1 |
|||
text-transform: none; |
|||
} |
|||
|
|||
// |
|||
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` |
|||
// controls in Android 4. |
|||
// 2. Correct the inability to style clickable types in iOS and Safari. |
|||
// |
|||
|
|||
button, |
|||
html [type="button"], // 1 |
|||
[type="reset"], |
|||
[type="submit"] { |
|||
-webkit-appearance: button; // 2 |
|||
} |
|||
|
|||
// |
|||
// Remove the inner border and padding in Firefox. |
|||
// |
|||
|
|||
button::-moz-focus-inner, |
|||
[type="button"]::-moz-focus-inner, |
|||
[type="reset"]::-moz-focus-inner, |
|||
[type="submit"]::-moz-focus-inner { |
|||
border-style: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
// |
|||
// Restore the focus styles unset by the previous rule. |
|||
// |
|||
|
|||
button:-moz-focusring, |
|||
[type="button"]:-moz-focusring, |
|||
[type="reset"]:-moz-focusring, |
|||
[type="submit"]:-moz-focusring { |
|||
outline: 1px dotted ButtonText; |
|||
} |
|||
|
|||
// |
|||
// Change the border, margin, and padding in all browsers (opinionated). |
|||
// |
|||
|
|||
fieldset { |
|||
border: 1px solid #c0c0c0; |
|||
margin: 0 2px; |
|||
padding: 0.35em 0.625em 0.75em; |
|||
} |
|||
|
|||
// |
|||
// 1. Correct the text wrapping in Edge and IE. |
|||
// 2. Correct the color inheritance from `fieldset` elements in IE. |
|||
// 3. Remove the padding so developers are not caught out when they zero out |
|||
// `fieldset` elements in all browsers. |
|||
// |
|||
|
|||
legend { |
|||
box-sizing: border-box; // 1 |
|||
color: inherit; // 2 |
|||
display: table; // 1 |
|||
max-width: 100%; // 1 |
|||
padding: 0; // 3 |
|||
white-space: normal; // 1 |
|||
} |
|||
|
|||
// |
|||
// 1. Add the correct display in IE 9-. |
|||
// 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. |
|||
// |
|||
|
|||
progress { |
|||
display: inline-block; // 1 |
|||
vertical-align: baseline; // 2 |
|||
} |
|||
|
|||
// |
|||
// Remove the default vertical scrollbar in IE. |
|||
// |
|||
|
|||
textarea { |
|||
overflow: auto; |
|||
} |
|||
|
|||
// |
|||
// 1. Add the correct box sizing in IE 10-. |
|||
// 2. Remove the padding in IE 10-. |
|||
// |
|||
|
|||
[type="checkbox"], |
|||
[type="radio"] { |
|||
box-sizing: border-box; // 1 |
|||
padding: 0; // 2 |
|||
} |
|||
|
|||
// |
|||
// Correct the cursor style of increment and decrement buttons in Chrome. |
|||
// |
|||
|
|||
[type="number"]::-webkit-inner-spin-button, |
|||
[type="number"]::-webkit-outer-spin-button { |
|||
height: auto; |
|||
} |
|||
|
|||
// |
|||
// 1. Correct the odd appearance in Chrome and Safari. |
|||
// 2. Correct the outline style in Safari. |
|||
// |
|||
|
|||
[type="search"] { |
|||
-webkit-appearance: textfield; // 1 |
|||
outline-offset: -2px; // 2 |
|||
} |
|||
|
|||
// |
|||
// Remove the inner padding and cancel buttons in Chrome and Safari on macOS. |
|||
// |
|||
|
|||
[type="search"]::-webkit-search-cancel-button, |
|||
[type="search"]::-webkit-search-decoration { |
|||
-webkit-appearance: none; |
|||
} |
|||
|
|||
// |
|||
// 1. Correct the inability to style clickable types in iOS and Safari. |
|||
// 2. Change font properties to `inherit` in Safari. |
|||
// |
|||
|
|||
::-webkit-file-upload-button { |
|||
-webkit-appearance: button; // 1 |
|||
font: inherit; // 2 |
|||
} |
|||
|
|||
// Interactive |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Add the correct display in IE 9-. |
|||
// 1. Add the correct display in Edge, IE, and Firefox. |
|||
// |
|||
|
|||
details, // 1 |
|||
menu { |
|||
display: block; |
|||
} |
|||
|
|||
// |
|||
// Add the correct display in all browsers. |
|||
// |
|||
|
|||
summary { |
|||
display: list-item; |
|||
} |
|||
|
|||
// Scripting |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Add the correct display in IE 9-. |
|||
// |
|||
|
|||
canvas { |
|||
display: inline-block; |
|||
} |
|||
|
|||
// |
|||
// Add the correct display in IE. |
|||
// |
|||
|
|||
template { |
|||
display: none; |
|||
} |
|||
|
|||
// Hidden |
|||
// ========================================================================== |
|||
|
|||
// |
|||
// Add the correct display in IE 10-. |
|||
// |
|||
|
|||
[hidden] { |
|||
display: none; |
|||
} |
@ -0,0 +1,67 @@ |
|||
.pagination { |
|||
display: flex; |
|||
// 1-2: Disable browser default list styles |
|||
padding-left: 0; // 1 |
|||
list-style: none; // 2 |
|||
@include border-radius(); |
|||
} |
|||
|
|||
.page-item { |
|||
&:first-child { |
|||
.page-link { |
|||
margin-left: 0; |
|||
@include border-left-radius($border-radius); |
|||
} |
|||
} |
|||
&:last-child { |
|||
.page-link { |
|||
@include border-right-radius($border-radius); |
|||
} |
|||
} |
|||
|
|||
&.active .page-link { |
|||
z-index: 2; |
|||
color: $pagination-active-color; |
|||
background-color: $pagination-active-bg; |
|||
border-color: $pagination-active-border; |
|||
} |
|||
|
|||
&.disabled .page-link { |
|||
color: $pagination-disabled-color; |
|||
pointer-events: none; |
|||
cursor: $cursor-disabled; // While `pointer-events: none` removes the cursor in modern browsers, we provide a disabled cursor as a fallback. |
|||
background-color: $pagination-disabled-bg; |
|||
border-color: $pagination-disabled-border; |
|||
} |
|||
} |
|||
|
|||
.page-link { |
|||
position: relative; |
|||
display: block; |
|||
padding: $pagination-padding-y $pagination-padding-x; |
|||
margin-left: -1px; |
|||
line-height: $pagination-line-height; |
|||
color: $pagination-color; |
|||
background-color: $pagination-bg; |
|||
border: $pagination-border-width solid $pagination-border-color; |
|||
|
|||
@include hover-focus { |
|||
color: $pagination-hover-color; |
|||
text-decoration: none; |
|||
background-color: $pagination-hover-bg; |
|||
border-color: $pagination-hover-border; |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Sizing |
|||
// |
|||
|
|||
.pagination-lg { |
|||
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg); |
|||
} |
|||
|
|||
.pagination-sm { |
|||
@include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm); |
|||
} |
@ -0,0 +1,171 @@ |
|||
.popover { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: $zindex-popover; |
|||
display: block; |
|||
max-width: $popover-max-width; |
|||
padding: $popover-inner-padding; |
|||
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. |
|||
// So reset our font and text properties to avoid inheriting weird values. |
|||
@include reset-text(); |
|||
font-size: $font-size-sm; |
|||
// Allow breaking very long words so they don't overflow the popover's bounds |
|||
word-wrap: break-word; |
|||
background-color: $popover-bg; |
|||
background-clip: padding-box; |
|||
border: $popover-border-width solid $popover-border-color; |
|||
@include border-radius($border-radius-lg); |
|||
@include box-shadow($popover-box-shadow); |
|||
|
|||
|
|||
// Popover directions |
|||
|
|||
&.popover-top, |
|||
&.bs-tether-element-attached-bottom { |
|||
margin-top: -$popover-arrow-width; |
|||
|
|||
&::before, |
|||
&::after { |
|||
left: 50%; |
|||
border-bottom-width: 0; |
|||
} |
|||
|
|||
&::before { |
|||
bottom: -$popover-arrow-outer-width; |
|||
margin-left: -$popover-arrow-outer-width; |
|||
border-top-color: $popover-arrow-outer-color; |
|||
} |
|||
|
|||
&::after { |
|||
bottom: -($popover-arrow-outer-width - 1); |
|||
margin-left: -$popover-arrow-width; |
|||
border-top-color: $popover-arrow-color; |
|||
} |
|||
} |
|||
|
|||
&.popover-right, |
|||
&.bs-tether-element-attached-left { |
|||
margin-left: $popover-arrow-width; |
|||
|
|||
&::before, |
|||
&::after { |
|||
top: 50%; |
|||
border-left-width: 0; |
|||
} |
|||
|
|||
&::before { |
|||
left: -$popover-arrow-outer-width; |
|||
margin-top: -$popover-arrow-outer-width; |
|||
border-right-color: $popover-arrow-outer-color; |
|||
} |
|||
|
|||
&::after { |
|||
left: -($popover-arrow-outer-width - 1); |
|||
margin-top: -($popover-arrow-outer-width - 1); |
|||
border-right-color: $popover-arrow-color; |
|||
} |
|||
} |
|||
|
|||
&.popover-bottom, |
|||
&.bs-tether-element-attached-top { |
|||
margin-top: $popover-arrow-width; |
|||
|
|||
&::before, |
|||
&::after { |
|||
left: 50%; |
|||
border-top-width: 0; |
|||
} |
|||
|
|||
&::before { |
|||
top: -$popover-arrow-outer-width; |
|||
margin-left: -$popover-arrow-outer-width; |
|||
border-bottom-color: $popover-arrow-outer-color; |
|||
} |
|||
|
|||
&::after { |
|||
top: -($popover-arrow-outer-width - 1); |
|||
margin-left: -$popover-arrow-width; |
|||
border-bottom-color: $popover-title-bg; |
|||
} |
|||
|
|||
// This will remove the popover-title's border just below the arrow |
|||
.popover-title::before { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 50%; |
|||
display: block; |
|||
width: 20px; |
|||
margin-left: -10px; |
|||
content: ""; |
|||
border-bottom: 1px solid $popover-title-bg; |
|||
} |
|||
} |
|||
|
|||
&.popover-left, |
|||
&.bs-tether-element-attached-right { |
|||
margin-left: -$popover-arrow-width; |
|||
|
|||
&::before, |
|||
&::after { |
|||
top: 50%; |
|||
border-right-width: 0; |
|||
} |
|||
|
|||
&::before { |
|||
right: -$popover-arrow-outer-width; |
|||
margin-top: -$popover-arrow-outer-width; |
|||
border-left-color: $popover-arrow-outer-color; |
|||
} |
|||
|
|||
&::after { |
|||
right: -($popover-arrow-outer-width - 1); |
|||
margin-top: -($popover-arrow-outer-width - 1); |
|||
border-left-color: $popover-arrow-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Offset the popover to account for the popover arrow |
|||
.popover-title { |
|||
padding: $popover-title-padding-y $popover-title-padding-x; |
|||
margin-bottom: 0; // Reset the default from Reboot |
|||
font-size: $font-size-base; |
|||
background-color: $popover-title-bg; |
|||
border-bottom: $popover-border-width solid darken($popover-title-bg, 5%); |
|||
$offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width}); |
|||
@include border-top-radius($offset-border-width); |
|||
|
|||
&:empty { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
.popover-content { |
|||
padding: $popover-content-padding-y $popover-content-padding-x; |
|||
} |
|||
|
|||
|
|||
// Arrows |
|||
// |
|||
// .popover-arrow is outer, .popover-arrow::after is inner |
|||
|
|||
.popover::before, |
|||
.popover::after { |
|||
position: absolute; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
} |
|||
|
|||
.popover::before { |
|||
content: ""; |
|||
border-width: $popover-arrow-outer-width; |
|||
} |
|||
.popover::after { |
|||
content: ""; |
|||
border-width: $popover-arrow-width; |
|||
} |
@ -0,0 +1,119 @@ |
|||
// scss-lint:disable QualifyingElement |
|||
|
|||
// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css |
|||
|
|||
// ========================================================================== |
|||
// Print styles. |
|||
// Inlined to avoid the additional HTTP request: |
|||
// http://www.phpied.com/delay-loading-your-print-css/ |
|||
// ========================================================================== |
|||
|
|||
@if $enable-print-styles { |
|||
@media print { |
|||
*, |
|||
*::before, |
|||
*::after, |
|||
p::first-letter, |
|||
div::first-letter, |
|||
blockquote::first-letter, |
|||
li::first-letter, |
|||
p::first-line, |
|||
div::first-line, |
|||
blockquote::first-line, |
|||
li::first-line { |
|||
// Bootstrap specific; comment out `color` and `background` |
|||
//color: #000 !important; // Black prints faster: |
|||
// http://www.sanbeiji.com/archives/953 |
|||
text-shadow: none !important; |
|||
//background: transparent !important; |
|||
box-shadow: none !important; |
|||
} |
|||
|
|||
a, |
|||
a:visited { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
// Bootstrap specific; comment the following selector out |
|||
//a[href]::after { |
|||
// content: " (" attr(href) ")"; |
|||
//} |
|||
|
|||
abbr[title]::after { |
|||
content: " (" attr(title) ")"; |
|||
} |
|||
|
|||
// Bootstrap specific; comment the following selector out |
|||
// |
|||
// Don't show links that are fragment identifiers, |
|||
// or use the `javascript:` pseudo protocol |
|||
// |
|||
|
|||
//a[href^="#"]::after, |
|||
//a[href^="javascript:"]::after { |
|||
// content: ""; |
|||
//} |
|||
|
|||
pre { |
|||
white-space: pre-wrap !important; |
|||
} |
|||
pre, |
|||
blockquote { |
|||
border: $border-width solid #999; // Bootstrap custom code; using `$border-width` instead of 1px |
|||
page-break-inside: avoid; |
|||
} |
|||
|
|||
// |
|||
// Printing Tables: |
|||
// http://css-discuss.incutio.com/wiki/Printing_Tables |
|||
// |
|||
|
|||
thead { |
|||
display: table-header-group; |
|||
} |
|||
|
|||
tr, |
|||
img { |
|||
page-break-inside: avoid; |
|||
} |
|||
|
|||
p, |
|||
h2, |
|||
h3 { |
|||
orphans: 3; |
|||
widows: 3; |
|||
} |
|||
|
|||
h2, |
|||
h3 { |
|||
page-break-after: avoid; |
|||
} |
|||
|
|||
// Bootstrap specific changes start |
|||
|
|||
// Bootstrap components |
|||
.navbar { |
|||
display: none; |
|||
} |
|||
.badge { |
|||
border: $border-width solid #000; |
|||
} |
|||
|
|||
.table { |
|||
border-collapse: collapse !important; |
|||
|
|||
td, |
|||
th { |
|||
background-color: #fff !important; |
|||
} |
|||
} |
|||
.table-bordered { |
|||
th, |
|||
td { |
|||
border: 1px solid #ddd !important; |
|||
} |
|||
} |
|||
|
|||
// Bootstrap specific changes end |
|||
} |
|||
} |
@ -0,0 +1,32 @@ |
|||
// Progress animations |
|||
@keyframes progress-bar-stripes { |
|||
from { background-position: $progress-height 0; } |
|||
to { background-position: 0 0; } |
|||
} |
|||
|
|||
// Basic progress bar |
|||
.progress { |
|||
display: flex; |
|||
overflow: hidden; // force rounded corners by cropping it |
|||
font-size: $progress-font-size; |
|||
line-height: $progress-height; |
|||
text-align: center; |
|||
background-color: $progress-bg; |
|||
@include border-radius($progress-border-radius); |
|||
} |
|||
.progress-bar { |
|||
height: $progress-height; |
|||
color: $progress-bar-color; |
|||
background-color: $progress-bar-bg; |
|||
} |
|||
|
|||
// Striped |
|||
.progress-bar-striped { |
|||
@include gradient-striped(); |
|||
background-size: $progress-height $progress-height; |
|||
} |
|||
|
|||
// Animated |
|||
.progress-bar-animated { |
|||
animation: progress-bar-stripes $progress-bar-animation-timing; |
|||
} |
@ -0,0 +1,389 @@ |
|||
// scss-lint:disable QualifyingElement, DuplicateProperty |
|||
|
|||
// Reboot |
|||
// |
|||
// Global resets to common HTML elements and more for easier usage by Bootstrap. |
|||
// Adds additional rules on top of Normalize.css, including several overrides. |
|||
|
|||
|
|||
// Reset the box-sizing |
|||
// |
|||
// Change from `box-sizing: content-box` to `border-box` so that when you add |
|||
// `padding` or `border`s to an element, the overall declared `width` does not |
|||
// change. For example, `width: 100px;` will always be `100px` despite the |
|||
// `border: 10px solid red;` and `padding: 20px;`. |
|||
// |
|||
// Heads up! This reset may cause conflicts with some third-party widgets. For |
|||
// recommendations on resolving such conflicts, see |
|||
// https://getbootstrap.com/getting-started/#third-box-sizing. |
|||
// |
|||
// Credit: https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ |
|||
|
|||
html { |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
*, |
|||
*::before, |
|||
*::after { |
|||
box-sizing: inherit; |
|||
} |
|||
|
|||
|
|||
// Make viewport responsive |
|||
// |
|||
// @viewport is needed because IE 10+ doesn't honor <meta name="viewport"> in |
|||
// some cases. See https://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/. |
|||
// Eventually @viewport will replace <meta name="viewport">. |
|||
// |
|||
// However, `device-width` is broken on IE 10 on Windows (Phone) 8, |
|||
// (see https://timkadlec.com/2013/01/windows-phone-8-and-device-width/ and https://github.com/twbs/bootstrap/issues/10497) |
|||
// and the fix for that involves a snippet of JavaScript to sniff the user agent |
|||
// and apply some conditional CSS. |
|||
// |
|||
// See https://getbootstrap.com/getting-started/#support-ie10-width for the relevant hack. |
|||
// |
|||
// Wrap `@viewport` with `@at-root` for when folks do a nested import (e.g., |
|||
// `.class-name { @import "bootstrap"; }`). |
|||
@at-root { |
|||
@-ms-viewport { width: device-width; } |
|||
} |
|||
|
|||
|
|||
// |
|||
// Reset HTML, body, and more |
|||
// |
|||
|
|||
html { |
|||
// We assume no initial pixel `font-size` for accessibility reasons. This |
|||
// allows web visitors to customize their browser default font-size, making |
|||
// your project more inclusive and accessible to everyone. |
|||
|
|||
// As a side-effect of setting the @viewport above, |
|||
// IE11 & Edge make the scrollbar overlap the content and automatically hide itself when not in use. |
|||
// Unfortunately, the auto-showing of the scrollbar is sometimes too sensitive, |
|||
// thus making it hard to click on stuff near the right edge of the page. |
|||
// So we add this style to force IE11 & Edge to use a "normal", non-overlapping, non-auto-hiding scrollbar. |
|||
// See https://github.com/twbs/bootstrap/issues/18543 |
|||
// and https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7165383/ |
|||
-ms-overflow-style: scrollbar; |
|||
|
|||
// Changes the default tap highlight to be completely transparent in iOS. |
|||
-webkit-tap-highlight-color: rgba(0,0,0,0); |
|||
} |
|||
|
|||
body { |
|||
font-family: $font-family-base; |
|||
font-size: $font-size-base; |
|||
font-weight: $font-weight-base; |
|||
line-height: $line-height-base; |
|||
// Go easy on the eyes and use something other than `#000` for text |
|||
color: $body-color; |
|||
// By default, `<body>` has no `background-color` so we set one as a best practice. |
|||
background-color: $body-bg; |
|||
} |
|||
|
|||
// Suppress the focus outline on elements that cannot be accessed via keyboard. |
|||
// This prevents an unwanted focus outline from appearing around elements that |
|||
// might still respond to pointer events. |
|||
// |
|||
// Credit: https://github.com/suitcss/base |
|||
[tabindex="-1"]:focus { |
|||
outline: none !important; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Typography |
|||
// |
|||
|
|||
// Remove top margins from headings |
|||
// |
|||
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top |
|||
// margin for easier control within type scales as it avoids margin collapsing. |
|||
h1, h2, h3, h4, h5, h6 { |
|||
margin-top: 0; |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
// Reset margins on paragraphs |
|||
// |
|||
// Similarly, the top margin on `<p>`s get reset. However, we also reset the |
|||
// bottom margin to use `rem` units instead of `em`. |
|||
p { |
|||
margin-top: 0; |
|||
margin-bottom: 1rem; |
|||
} |
|||
|
|||
// Abbreviations |
|||
abbr[title], |
|||
// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 |
|||
abbr[data-original-title] { |
|||
cursor: help; |
|||
} |
|||
|
|||
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: $dt-font-weight; |
|||
} |
|||
|
|||
dd { |
|||
margin-bottom: .5rem; |
|||
margin-left: 0; // Undo browser default |
|||
} |
|||
|
|||
blockquote { |
|||
margin: 0 0 1rem; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Links |
|||
// |
|||
|
|||
a { |
|||
color: $link-color; |
|||
text-decoration: $link-decoration; |
|||
|
|||
@include hover-focus { |
|||
color: $link-hover-color; |
|||
text-decoration: $link-hover-decoration; |
|||
} |
|||
} |
|||
|
|||
// And undo these styles for placeholder links/named anchors (without href) |
|||
// which have not been made explicitly keyboard-focusable (without tabindex). |
|||
// It would be more straightforward to just use a[href] in previous block, but that |
|||
// causes specificity issues in many other styles that are too complex to fix. |
|||
// See https://github.com/twbs/bootstrap/issues/19402 |
|||
|
|||
a:not([href]):not([tabindex]) { |
|||
color: inherit; |
|||
text-decoration: none; |
|||
|
|||
@include hover-focus { |
|||
color: inherit; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
&:focus { |
|||
outline: 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Code |
|||
// |
|||
|
|||
pre { |
|||
// Remove browser default top margin |
|||
margin-top: 0; |
|||
// Reset browser default of `1em` to use `rem`s |
|||
margin-bottom: 1rem; |
|||
// Normalize v4 removed this property, causing `<pre>` content to break out of wrapping code snippets |
|||
overflow: auto; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Figures |
|||
// |
|||
|
|||
figure { |
|||
// Normalize adds `margin` to `figure`s as browsers apply it inconsistently. |
|||
// We reset that to create a better flow in-page. |
|||
margin: 0 0 1rem; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Images |
|||
// |
|||
|
|||
img { |
|||
// By default, `<img>`s are `inline-block`. This assumes that, and vertically |
|||
// centers them. This won't apply should you reset them to `block` level. |
|||
vertical-align: middle; |
|||
// Note: `<img>`s are deliberately not made responsive by default. |
|||
// For the rationale behind this, see the comments on the `.img-fluid` class. |
|||
} |
|||
|
|||
|
|||
// iOS "clickable elements" fix for role="button" |
|||
// |
|||
// Fixes "clickability" issue (and more generally, the firing of events such as focus as well) |
|||
// for traditionally non-focusable elements with role="button" |
|||
// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile |
|||
|
|||
[role="button"] { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
|
|||
// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property. |
|||
// |
|||
// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11 |
|||
// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present. |
|||
// However, they DO support removing the click delay via `touch-action: manipulation`. |
|||
// See: |
|||
// * https://v4-alpha.getbootstrap.com/content/reboot/#click-delay-optimization-for-touch |
|||
// * http://caniuse.com/#feat=css-touch-action |
|||
// * https://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay |
|||
|
|||
a, |
|||
area, |
|||
button, |
|||
[role="button"], |
|||
input, |
|||
label, |
|||
select, |
|||
summary, |
|||
textarea { |
|||
touch-action: manipulation; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Tables |
|||
// |
|||
|
|||
table { |
|||
// No longer part of Normalize since v4 |
|||
border-collapse: collapse; |
|||
// Reset for nesting within parents with `background-color`. |
|||
background-color: $table-bg; |
|||
} |
|||
|
|||
caption { |
|||
padding-top: $table-cell-padding; |
|||
padding-bottom: $table-cell-padding; |
|||
color: $text-muted; |
|||
text-align: left; |
|||
caption-side: bottom; |
|||
} |
|||
|
|||
th { |
|||
// Centered by default, but left-align-ed to match the `td`s below. |
|||
text-align: left; |
|||
} |
|||
|
|||
|
|||
// |
|||
// Forms |
|||
// |
|||
|
|||
label { |
|||
// Allow labels to use `margin` for spacing. |
|||
display: inline-block; |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
// Work around a Firefox/IE bug where the transparent `button` background |
|||
// results in a loss of the default `button` focus styles. |
|||
// |
|||
// Credit: https://github.com/suitcss/base/ |
|||
button:focus { |
|||
outline: 1px dotted; |
|||
outline: 5px auto -webkit-focus-ring-color; |
|||
} |
|||
|
|||
input, |
|||
button, |
|||
select, |
|||
textarea { |
|||
// Normalize includes `font: inherit;`, so `font-family`. `font-size`, etc are |
|||
// properly inherited. However, `line-height` isn't inherited there. |
|||
line-height: inherit; |
|||
} |
|||
|
|||
input[type="radio"], |
|||
input[type="checkbox"] { |
|||
// Apply a disabled cursor for radios and checkboxes. |
|||
// |
|||
// Note: Neither radios nor checkboxes can be readonly. |
|||
&:disabled { |
|||
cursor: $cursor-disabled; |
|||
} |
|||
} |
|||
|
|||
|
|||
input[type="date"], |
|||
input[type="time"], |
|||
input[type="datetime-local"], |
|||
input[type="month"] { |
|||
// Remove the default appearance of temporal inputs to avoid a Mobile Safari |
|||
// bug where setting a custom line-height prevents text from being vertically |
|||
// centered within the input. |
|||
// See https://bugs.webkit.org/show_bug.cgi?id=139848 |
|||
// and https://github.com/twbs/bootstrap/issues/11266 |
|||
-webkit-appearance: listbox; |
|||
} |
|||
|
|||
textarea { |
|||
// Textareas should really only resize vertically so they don't break their (horizontal) containers. |
|||
resize: vertical; |
|||
} |
|||
|
|||
fieldset { |
|||
// Browsers set a default `min-width: min-content;` on fieldsets, |
|||
// unlike e.g. `<div>`s, which have `min-width: 0;` by default. |
|||
// So we reset that to ensure fieldsets behave more like a standard block element. |
|||
// See https://github.com/twbs/bootstrap/issues/12359 |
|||
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements |
|||
min-width: 0; |
|||
// Reset the default outline behavior of fieldsets so they don't affect page layout. |
|||
padding: 0; |
|||
margin: 0; |
|||
border: 0; |
|||
} |
|||
|
|||
legend { |
|||
// Reset the entire legend element to match the `fieldset` |
|||
display: block; |
|||
width: 100%; |
|||
padding: 0; |
|||
margin-bottom: .5rem; |
|||
font-size: 1.5rem; |
|||
line-height: inherit; |
|||
} |
|||
|
|||
input[type="search"] { |
|||
// This overrides the extra rounded corners on search inputs in iOS so that our |
|||
// `.form-control` class can properly style them. Note that this cannot simply |
|||
// be added to `.form-control` as it's not specific enough. For details, see |
|||
// https://github.com/twbs/bootstrap/issues/11586. |
|||
-webkit-appearance: none; |
|||
} |
|||
|
|||
// todo: needed? |
|||
output { |
|||
display: inline-block; |
|||
// font-size: $font-size-base; |
|||
// line-height: $line-height; |
|||
// color: $input-color; |
|||
} |
|||
|
|||
// Always hide an element with the `hidden` HTML attribute (from PureCSS). |
|||
[hidden] { |
|||
display: none !important; |
|||
} |
@ -0,0 +1,52 @@ |
|||
// Credit: Nicolas Gallagher and SUIT CSS. |
|||
|
|||
.embed-responsive { |
|||
position: relative; |
|||
display: block; |
|||
width: 100%; |
|||
padding: 0; |
|||
overflow: hidden; |
|||
|
|||
&::before { |
|||
display: block; |
|||
content: ""; |
|||
} |
|||
|
|||
.embed-responsive-item, |
|||
iframe, |
|||
embed, |
|||
object, |
|||
video { |
|||
position: absolute; |
|||
top: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
border: 0; |
|||
} |
|||
} |
|||
|
|||
.embed-responsive-21by9 { |
|||
&::before { |
|||
padding-top: percentage(9 / 21); |
|||
} |
|||
} |
|||
|
|||
.embed-responsive-16by9 { |
|||
&::before { |
|||
padding-top: percentage(9 / 16); |
|||
} |
|||
} |
|||
|
|||
.embed-responsive-4by3 { |
|||
&::before { |
|||
padding-top: percentage(3 / 4); |
|||
} |
|||
} |
|||
|
|||
.embed-responsive-1by1 { |
|||
&::before { |
|||
padding-top: percentage(1 / 1); |
|||
} |
|||
} |
@ -0,0 +1,153 @@ |
|||
// |
|||
// Basic Bootstrap table |
|||
// |
|||
|
|||
.table { |
|||
width: 100%; |
|||
max-width: 100%; |
|||
margin-bottom: $spacer; |
|||
|
|||
th, |
|||
td { |
|||
padding: $table-cell-padding; |
|||
vertical-align: top; |
|||
border-top: $table-border-width solid $table-border-color; |
|||
} |
|||
|
|||
thead th { |
|||
vertical-align: bottom; |
|||
border-bottom: (2 * $table-border-width) solid $table-border-color; |
|||
} |
|||
|
|||
tbody + tbody { |
|||
border-top: (2 * $table-border-width) solid $table-border-color; |
|||
} |
|||
|
|||
.table { |
|||
background-color: $body-bg; |
|||
} |
|||
} |
|||
|
|||
|
|||
// |
|||
// Condensed table w/ half padding |
|||
// |
|||
|
|||
.table-sm { |
|||
th, |
|||
td { |
|||
padding: $table-sm-cell-padding; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Bordered version |
|||
// |
|||
// Add borders all around the table and between all the columns. |
|||
|
|||
.table-bordered { |
|||
border: $table-border-width solid $table-border-color; |
|||
|
|||
th, |
|||
td { |
|||
border: $table-border-width solid $table-border-color; |
|||
} |
|||
|
|||
thead { |
|||
th, |
|||
td { |
|||
border-bottom-width: (2 * $table-border-width); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Zebra-striping |
|||
// |
|||
// Default zebra-stripe styles (alternating gray and transparent backgrounds) |
|||
|
|||
.table-striped { |
|||
tbody tr:nth-of-type(odd) { |
|||
background-color: $table-bg-accent; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Hover effect |
|||
// |
|||
// Placed here since it has to come after the potential zebra striping |
|||
|
|||
.table-hover { |
|||
tbody tr { |
|||
@include hover { |
|||
background-color: $table-bg-hover; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// Table backgrounds |
|||
// |
|||
// Exact selectors below required to override `.table-striped` and prevent |
|||
// inheritance to nested tables. |
|||
|
|||
// Generate the contextual variants |
|||
@include table-row-variant(active, $table-bg-active); |
|||
@include table-row-variant(success, $state-success-bg); |
|||
@include table-row-variant(info, $state-info-bg); |
|||
@include table-row-variant(warning, $state-warning-bg); |
|||
@include table-row-variant(danger, $state-danger-bg); |
|||
|
|||
|
|||
// Inverse styles |
|||
// |
|||
// Same table markup, but inverted color scheme: dark background and light text. |
|||
|
|||
.thead-inverse { |
|||
th { |
|||
color: $table-inverse-color; |
|||
background-color: $table-inverse-bg; |
|||
} |
|||
} |
|||
|
|||
.thead-default { |
|||
th { |
|||
color: $table-head-color; |
|||
background-color: $table-head-bg; |
|||
} |
|||
} |
|||
|
|||
.table-inverse { |
|||
color: $table-inverse-color; |
|||
background-color: $table-inverse-bg; |
|||
|
|||
th, |
|||
td, |
|||
thead th { |
|||
border-color: $body-bg; |
|||
} |
|||
|
|||
&.table-bordered { |
|||
border: 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
// Responsive tables |
|||
// |
|||
// Add `.table-responsive` to `.table`s and we'll make them mobile friendly by |
|||
// enabling horizontal scrolling. Only applies <768px. Everything above that |
|||
// will display normally. |
|||
|
|||
.table-responsive { |
|||
display: block; |
|||
width: 100%; |
|||
overflow-x: auto; |
|||
-ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057 |
|||
|
|||
// Prevent double border on horizontal scroll due to use of `display: block;` |
|||
&.table-bordered { |
|||
border: 0; |
|||
} |
|||
} |
@ -0,0 +1,90 @@ |
|||
// Base class |
|||
.tooltip { |
|||
position: absolute; |
|||
z-index: $zindex-tooltip; |
|||
display: block; |
|||
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. |
|||
// So reset our font and text properties to avoid inheriting weird values. |
|||
@include reset-text(); |
|||
font-size: $font-size-sm; |
|||
// Allow breaking very long words so they don't overflow the tooltip's bounds |
|||
word-wrap: break-word; |
|||
opacity: 0; |
|||
|
|||
&.show { opacity: $tooltip-opacity; } |
|||
|
|||
&.tooltip-top, |
|||
&.bs-tether-element-attached-bottom { |
|||
padding: $tooltip-arrow-width 0; |
|||
margin-top: -$tooltip-margin; |
|||
|
|||
.tooltip-inner::before { |
|||
bottom: 0; |
|||
left: 50%; |
|||
margin-left: -$tooltip-arrow-width; |
|||
content: ""; |
|||
border-width: $tooltip-arrow-width $tooltip-arrow-width 0; |
|||
border-top-color: $tooltip-arrow-color; |
|||
} |
|||
} |
|||
&.tooltip-right, |
|||
&.bs-tether-element-attached-left { |
|||
padding: 0 $tooltip-arrow-width; |
|||
margin-left: $tooltip-margin; |
|||
|
|||
.tooltip-inner::before { |
|||
top: 50%; |
|||
left: 0; |
|||
margin-top: -$tooltip-arrow-width; |
|||
content: ""; |
|||
border-width: $tooltip-arrow-width $tooltip-arrow-width $tooltip-arrow-width 0; |
|||
border-right-color: $tooltip-arrow-color; |
|||
} |
|||
} |
|||
&.tooltip-bottom, |
|||
&.bs-tether-element-attached-top { |
|||
padding: $tooltip-arrow-width 0; |
|||
margin-top: $tooltip-margin; |
|||
|
|||
.tooltip-inner::before { |
|||
top: 0; |
|||
left: 50%; |
|||
margin-left: -$tooltip-arrow-width; |
|||
content: ""; |
|||
border-width: 0 $tooltip-arrow-width $tooltip-arrow-width; |
|||
border-bottom-color: $tooltip-arrow-color; |
|||
} |
|||
} |
|||
&.tooltip-left, |
|||
&.bs-tether-element-attached-right { |
|||
padding: 0 $tooltip-arrow-width; |
|||
margin-left: -$tooltip-margin; |
|||
|
|||
.tooltip-inner::before { |
|||
top: 50%; |
|||
right: 0; |
|||
margin-top: -$tooltip-arrow-width; |
|||
content: ""; |
|||
border-width: $tooltip-arrow-width 0 $tooltip-arrow-width $tooltip-arrow-width; |
|||
border-left-color: $tooltip-arrow-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Wrapper for the tooltip content |
|||
.tooltip-inner { |
|||
max-width: $tooltip-max-width; |
|||
padding: $tooltip-padding-y $tooltip-padding-x; |
|||
color: $tooltip-color; |
|||
text-align: center; |
|||
background-color: $tooltip-bg; |
|||
@include border-radius($border-radius); |
|||
|
|||
&::before { |
|||
position: absolute; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
.fade { |
|||
opacity: 0; |
|||
@include transition($transition-fade); |
|||
|
|||
&.show { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
|
|||
.collapse { |
|||
display: none; |
|||
&.show { |
|||
display: block; |
|||
} |
|||
} |
|||
|
|||
tr { |
|||
&.collapse.show { |
|||
display: table-row; |
|||
} |
|||
} |
|||
|
|||
tbody { |
|||
&.collapse.show { |
|||
display: table-row-group; |
|||
} |
|||
} |
|||
|
|||
.collapsing { |
|||
position: relative; |
|||
height: 0; |
|||
overflow: hidden; |
|||
@include transition($transition-collapse); |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue