[Pkg-javascript-commits] [node-bootstrap-tour] 01/04: Imported Upstream version 0.11.0+dfsg
Julien Puydt
julien.puydt at laposte.net
Thu Aug 11 04:21:59 UTC 2016
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-bootstrap-tour.
commit 03dd58d0451173743480ba4af5b7e568e11c6809
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Thu Aug 11 06:06:47 2016 +0200
Imported Upstream version 0.11.0+dfsg
---
bower.json | 4 +-
build/css/bootstrap-tour-standalone.css | 317 +++++++++++++++++--------
build/css/bootstrap-tour.css | 3 +-
build/js/bootstrap-tour-standalone.js | 399 +++++++++++++++++++++-----------
gulpfile.coffee | 2 +-
package.json | 6 +-
smart.json | 2 +-
src/coffee/bootstrap-tour.coffee | 180 +++++++++-----
src/coffee/bootstrap-tour.spec.coffee | 277 +++++++++++++++++++---
src/docs/api.html | 2 +-
src/docs/assets/css/bootstrap-tour.css | 3 +-
src/less/bootstrap-tour.less | 3 +-
test/bootstrap-tour.js | 209 +++++++++++------
13 files changed, 988 insertions(+), 419 deletions(-)
diff --git a/bower.json b/bower.json
index eb25999..9b7b907 100644
--- a/bower.json
+++ b/bower.json
@@ -13,7 +13,7 @@
"devDependencies": {
"html5shiv": "~3.7.2",
"jquery": "~2.1.1",
- "bootstrap": "~3.2.0",
- "blueimp-md5": "~1.1.0"
+ "bootstrap": "~3.3.7",
+ "blueimp-md5": "~2.3.0"
}
}
diff --git a/build/css/bootstrap-tour-standalone.css b/build/css/bootstrap-tour-standalone.css
index 7de1e5c..23a0e4f 100644
--- a/build/css/bootstrap-tour-standalone.css
+++ b/build/css/bootstrap-tour-standalone.css
@@ -1,15 +1,15 @@
/* ========================================================================
- * bootstrap-tour - v0.10.2
+ * bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
- * Copyright 2012-2013 Ulrich Sossou
+ * Copyright 2012-2015 Ulrich Sossou
*
* ========================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -30,6 +30,7 @@
font-weight: normal;
text-align: center;
vertical-align: middle;
+ touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
@@ -45,13 +46,16 @@
}
.btn:focus,
.btn:active:focus,
-.btn.active:focus {
- outline: thin dotted;
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.btn:hover,
-.btn:focus {
+.btn:focus,
+.btn.focus {
color: #333333;
text-decoration: none;
}
@@ -66,19 +70,31 @@
.btn[disabled],
fieldset[disabled] .btn {
cursor: not-allowed;
- pointer-events: none;
opacity: 0.65;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
}
+a.btn.disabled,
+fieldset[disabled] a.btn {
+ pointer-events: none;
+}
.btn-default {
color: #333333;
background-color: #ffffff;
border-color: #cccccc;
}
-.btn-default:hover,
.btn-default:focus,
+.btn-default.focus {
+ color: #333333;
+ background-color: #e6e6e6;
+ border-color: #8c8c8c;
+}
+.btn-default:hover {
+ color: #333333;
+ background-color: #e6e6e6;
+ border-color: #adadad;
+}
.btn-default:active,
.btn-default.active,
.open > .dropdown-toggle.btn-default {
@@ -86,26 +102,33 @@ fieldset[disabled] .btn {
background-color: #e6e6e6;
border-color: #adadad;
}
+.btn-default:active:hover,
+.btn-default.active:hover,
+.open > .dropdown-toggle.btn-default:hover,
+.btn-default:active:focus,
+.btn-default.active:focus,
+.open > .dropdown-toggle.btn-default:focus,
+.btn-default:active.focus,
+.btn-default.active.focus,
+.open > .dropdown-toggle.btn-default.focus {
+ color: #333333;
+ background-color: #d4d4d4;
+ border-color: #8c8c8c;
+}
.btn-default:active,
.btn-default.active,
.open > .dropdown-toggle.btn-default {
background-image: none;
}
-.btn-default.disabled,
-.btn-default[disabled],
-fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
-.btn-default.disabled:active,
-.btn-default[disabled]:active,
-fieldset[disabled] .btn-default:active,
-.btn-default.disabled.active,
-.btn-default[disabled].active,
-fieldset[disabled] .btn-default.active {
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus {
background-color: #ffffff;
border-color: #cccccc;
}
@@ -115,43 +138,59 @@ fieldset[disabled] .btn-default.active {
}
.btn-primary {
color: #ffffff;
- background-color: #428bca;
- border-color: #357ebd;
+ background-color: #337ab7;
+ border-color: #2e6da4;
}
-.btn-primary:hover,
.btn-primary:focus,
+.btn-primary.focus {
+ color: #ffffff;
+ background-color: #286090;
+ border-color: #122b40;
+}
+.btn-primary:hover {
+ color: #ffffff;
+ background-color: #286090;
+ border-color: #204d74;
+}
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary {
color: #ffffff;
- background-color: #3071a9;
- border-color: #285e8e;
+ background-color: #286090;
+ border-color: #204d74;
+}
+.btn-primary:active:hover,
+.btn-primary.active:hover,
+.open > .dropdown-toggle.btn-primary:hover,
+.btn-primary:active:focus,
+.btn-primary.active:focus,
+.open > .dropdown-toggle.btn-primary:focus,
+.btn-primary:active.focus,
+.btn-primary.active.focus,
+.open > .dropdown-toggle.btn-primary.focus {
+ color: #ffffff;
+ background-color: #204d74;
+ border-color: #122b40;
}
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary {
background-image: none;
}
-.btn-primary.disabled,
-.btn-primary[disabled],
-fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
-.btn-primary.disabled:active,
-.btn-primary[disabled]:active,
-fieldset[disabled] .btn-primary:active,
-.btn-primary.disabled.active,
-.btn-primary[disabled].active,
-fieldset[disabled] .btn-primary.active {
- background-color: #428bca;
- border-color: #357ebd;
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus {
+ background-color: #337ab7;
+ border-color: #2e6da4;
}
.btn-primary .badge {
- color: #428bca;
+ color: #337ab7;
background-color: #ffffff;
}
.btn-success {
@@ -159,8 +198,17 @@ fieldset[disabled] .btn-primary.active {
background-color: #5cb85c;
border-color: #4cae4c;
}
-.btn-success:hover,
.btn-success:focus,
+.btn-success.focus {
+ color: #ffffff;
+ background-color: #449d44;
+ border-color: #255625;
+}
+.btn-success:hover {
+ color: #ffffff;
+ background-color: #449d44;
+ border-color: #398439;
+}
.btn-success:active,
.btn-success.active,
.open > .dropdown-toggle.btn-success {
@@ -168,26 +216,33 @@ fieldset[disabled] .btn-primary.active {
background-color: #449d44;
border-color: #398439;
}
+.btn-success:active:hover,
+.btn-success.active:hover,
+.open > .dropdown-toggle.btn-success:hover,
+.btn-success:active:focus,
+.btn-success.active:focus,
+.open > .dropdown-toggle.btn-success:focus,
+.btn-success:active.focus,
+.btn-success.active.focus,
+.open > .dropdown-toggle.btn-success.focus {
+ color: #ffffff;
+ background-color: #398439;
+ border-color: #255625;
+}
.btn-success:active,
.btn-success.active,
.open > .dropdown-toggle.btn-success {
background-image: none;
}
-.btn-success.disabled,
-.btn-success[disabled],
-fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
-.btn-success.disabled:active,
-.btn-success[disabled]:active,
-fieldset[disabled] .btn-success:active,
-.btn-success.disabled.active,
-.btn-success[disabled].active,
-fieldset[disabled] .btn-success.active {
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus {
background-color: #5cb85c;
border-color: #4cae4c;
}
@@ -200,8 +255,17 @@ fieldset[disabled] .btn-success.active {
background-color: #5bc0de;
border-color: #46b8da;
}
-.btn-info:hover,
.btn-info:focus,
+.btn-info.focus {
+ color: #ffffff;
+ background-color: #31b0d5;
+ border-color: #1b6d85;
+}
+.btn-info:hover {
+ color: #ffffff;
+ background-color: #31b0d5;
+ border-color: #269abc;
+}
.btn-info:active,
.btn-info.active,
.open > .dropdown-toggle.btn-info {
@@ -209,26 +273,33 @@ fieldset[disabled] .btn-success.active {
background-color: #31b0d5;
border-color: #269abc;
}
+.btn-info:active:hover,
+.btn-info.active:hover,
+.open > .dropdown-toggle.btn-info:hover,
+.btn-info:active:focus,
+.btn-info.active:focus,
+.open > .dropdown-toggle.btn-info:focus,
+.btn-info:active.focus,
+.btn-info.active.focus,
+.open > .dropdown-toggle.btn-info.focus {
+ color: #ffffff;
+ background-color: #269abc;
+ border-color: #1b6d85;
+}
.btn-info:active,
.btn-info.active,
.open > .dropdown-toggle.btn-info {
background-image: none;
}
-.btn-info.disabled,
-.btn-info[disabled],
-fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
-.btn-info.disabled:active,
-.btn-info[disabled]:active,
-fieldset[disabled] .btn-info:active,
-.btn-info.disabled.active,
-.btn-info[disabled].active,
-fieldset[disabled] .btn-info.active {
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus {
background-color: #5bc0de;
border-color: #46b8da;
}
@@ -241,8 +312,17 @@ fieldset[disabled] .btn-info.active {
background-color: #f0ad4e;
border-color: #eea236;
}
-.btn-warning:hover,
.btn-warning:focus,
+.btn-warning.focus {
+ color: #ffffff;
+ background-color: #ec971f;
+ border-color: #985f0d;
+}
+.btn-warning:hover {
+ color: #ffffff;
+ background-color: #ec971f;
+ border-color: #d58512;
+}
.btn-warning:active,
.btn-warning.active,
.open > .dropdown-toggle.btn-warning {
@@ -250,26 +330,33 @@ fieldset[disabled] .btn-info.active {
background-color: #ec971f;
border-color: #d58512;
}
+.btn-warning:active:hover,
+.btn-warning.active:hover,
+.open > .dropdown-toggle.btn-warning:hover,
+.btn-warning:active:focus,
+.btn-warning.active:focus,
+.open > .dropdown-toggle.btn-warning:focus,
+.btn-warning:active.focus,
+.btn-warning.active.focus,
+.open > .dropdown-toggle.btn-warning.focus {
+ color: #ffffff;
+ background-color: #d58512;
+ border-color: #985f0d;
+}
.btn-warning:active,
.btn-warning.active,
.open > .dropdown-toggle.btn-warning {
background-image: none;
}
-.btn-warning.disabled,
-.btn-warning[disabled],
-fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
-.btn-warning.disabled:active,
-.btn-warning[disabled]:active,
-fieldset[disabled] .btn-warning:active,
-.btn-warning.disabled.active,
-.btn-warning[disabled].active,
-fieldset[disabled] .btn-warning.active {
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus {
background-color: #f0ad4e;
border-color: #eea236;
}
@@ -282,8 +369,17 @@ fieldset[disabled] .btn-warning.active {
background-color: #d9534f;
border-color: #d43f3a;
}
-.btn-danger:hover,
.btn-danger:focus,
+.btn-danger.focus {
+ color: #ffffff;
+ background-color: #c9302c;
+ border-color: #761c19;
+}
+.btn-danger:hover {
+ color: #ffffff;
+ background-color: #c9302c;
+ border-color: #ac2925;
+}
.btn-danger:active,
.btn-danger.active,
.open > .dropdown-toggle.btn-danger {
@@ -291,26 +387,33 @@ fieldset[disabled] .btn-warning.active {
background-color: #c9302c;
border-color: #ac2925;
}
+.btn-danger:active:hover,
+.btn-danger.active:hover,
+.open > .dropdown-toggle.btn-danger:hover,
+.btn-danger:active:focus,
+.btn-danger.active:focus,
+.open > .dropdown-toggle.btn-danger:focus,
+.btn-danger:active.focus,
+.btn-danger.active.focus,
+.open > .dropdown-toggle.btn-danger.focus {
+ color: #ffffff;
+ background-color: #ac2925;
+ border-color: #761c19;
+}
.btn-danger:active,
.btn-danger.active,
.open > .dropdown-toggle.btn-danger {
background-image: none;
}
-.btn-danger.disabled,
-.btn-danger[disabled],
-fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
-.btn-danger.disabled:active,
-.btn-danger[disabled]:active,
-fieldset[disabled] .btn-danger:active,
-.btn-danger.disabled.active,
-.btn-danger[disabled].active,
-fieldset[disabled] .btn-danger.active {
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus {
background-color: #d9534f;
border-color: #d43f3a;
}
@@ -319,13 +422,13 @@ fieldset[disabled] .btn-danger.active {
background-color: #ffffff;
}
.btn-link {
- color: #428bca;
+ color: #337ab7;
font-weight: normal;
- cursor: pointer;
border-radius: 0;
}
.btn-link,
.btn-link:active,
+.btn-link.active,
.btn-link[disabled],
fieldset[disabled] .btn-link {
background-color: transparent;
@@ -340,7 +443,7 @@ fieldset[disabled] .btn-link {
}
.btn-link:hover,
.btn-link:focus {
- color: #2a6496;
+ color: #23527c;
text-decoration: underline;
background-color: transparent;
}
@@ -355,7 +458,7 @@ fieldset[disabled] .btn-link:focus {
.btn-group-lg > .btn {
padding: 10px 16px;
font-size: 18px;
- line-height: 1.33;
+ line-height: 1.3333333;
border-radius: 6px;
}
.btn-sm,
@@ -405,10 +508,6 @@ input[type="button"].btn-block {
.btn-group-vertical > .btn.active {
z-index: 2;
}
-.btn-group > .btn:focus,
-.btn-group-vertical > .btn:focus {
- outline: 0;
-}
.btn-group .btn + .btn,
.btn-group .btn + .btn-group,
.btn-group .btn-group + .btn,
@@ -418,6 +517,7 @@ input[type="button"].btn-block {
.btn-toolbar {
margin-left: -5px;
}
+.btn-toolbar .btn,
.btn-toolbar .btn-group,
.btn-toolbar .input-group {
float: left;
@@ -448,12 +548,12 @@ input[type="button"].btn-block {
.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
-.btn-group > .btn-group:first-child > .btn:last-child,
-.btn-group > .btn-group:first-child > .dropdown-toggle {
+.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
-.btn-group > .btn-group:last-child > .btn:first-child {
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
@@ -510,13 +610,15 @@ input[type="button"].btn-block {
}
.btn-group-vertical > .btn:first-child:not(:last-child) {
border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.btn-group-vertical > .btn:last-child:not(:first-child) {
- border-bottom-left-radius: 4px;
border-top-right-radius: 0;
border-top-left-radius: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
}
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
@@ -548,12 +650,13 @@ input[type="button"].btn-block {
.btn-group-justified > .btn-group .dropdown-menu {
left: auto;
}
-[data-toggle="buttons"] > .btn > input[type="radio"],
-[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+[data-toggle="buttons"] > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
position: absolute;
- z-index: -1;
- opacity: 0;
- filter: alpha(opacity=0);
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
}
.popover {
position: absolute;
@@ -563,7 +666,22 @@ input[type="button"].btn-block {
display: none;
max-width: 276px;
padding: 1px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ letter-spacing: normal;
+ line-break: auto;
+ line-height: 1.42857143;
text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ white-space: normal;
+ word-break: normal;
+ word-spacing: normal;
+ word-wrap: normal;
+ font-size: 14px;
background-color: #ffffff;
background-clip: padding-box;
border: 1px solid #cccccc;
@@ -571,7 +689,6 @@ input[type="button"].btn-block {
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- white-space: normal;
}
.popover.top {
margin-top: -10px;
@@ -589,8 +706,6 @@ input[type="button"].btn-block {
margin: 0;
padding: 8px 14px;
font-size: 14px;
- font-weight: normal;
- line-height: 18px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
border-radius: 5px 5px 0 0;
@@ -699,9 +814,12 @@ tbody.collapse.in {
position: relative;
height: 0;
overflow: hidden;
- -webkit-transition: height 0.35s ease;
- -o-transition: height 0.35s ease;
- transition: height 0.35s ease;
+ -webkit-transition-property: height, visibility;
+ transition-property: height, visibility;
+ -webkit-transition-duration: 0.35s;
+ transition-duration: 0.35s;
+ -webkit-transition-timing-function: ease;
+ transition-timing-function: ease;
}
.tour-backdrop {
position: fixed;
@@ -729,10 +847,11 @@ tbody.collapse.in {
border-radius: 6px;
}
.popover[class*="tour-"] {
- z-index: 1100;
+ z-index: 1102;
}
.popover[class*="tour-"] .popover-navigation {
padding: 9px 14px;
+ overflow: hidden;
}
.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
float: right;
diff --git a/build/css/bootstrap-tour.css b/build/css/bootstrap-tour.css
index 1b35295..659fa92 100644
--- a/build/css/bootstrap-tour.css
+++ b/build/css/bootstrap-tour.css
@@ -1,5 +1,5 @@
/* ========================================================================
- * bootstrap-tour - v0.10.2
+ * bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
* Copyright 2012-2015 Ulrich Sossou
@@ -49,6 +49,7 @@
}
.popover[class*="tour-"] .popover-navigation {
padding: 9px 14px;
+ overflow: hidden;
}
.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
float: right;
diff --git a/build/js/bootstrap-tour-standalone.js b/build/js/bootstrap-tour-standalone.js
index 39ecb23..39de5cb 100644
--- a/build/js/bootstrap-tour-standalone.js
+++ b/build/js/bootstrap-tour-standalone.js
@@ -1,5 +1,5 @@
/* ========================================================================
- * bootstrap-tour - v0.10.2
+ * bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
* Copyright 2012-2015 Ulrich Sossou
@@ -20,10 +20,10 @@
*/
/* ========================================================================
- * Bootstrap: transition.js v3.2.0
+ * Bootstrap: transition.js v3.3.7
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
+ * Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -80,11 +80,11 @@
}(jQuery);
/* ========================================================================
- * Bootstrap: tooltip.js v3.2.0
+ * Bootstrap: tooltip.js v3.3.7
* http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
+ * Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -96,17 +96,20 @@
// ===============================
var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
+ this.type = null
+ this.options = null
+ this.enabled = null
+ this.timeout = null
+ this.hoverState = null
this.$element = null
+ this.inState = null
this.init('tooltip', element, options)
}
- Tooltip.VERSION = '3.2.0'
+ Tooltip.VERSION = '3.3.7'
+
+ Tooltip.TRANSITION_DURATION = 150
Tooltip.DEFAULTS = {
animation: true,
@@ -129,7 +132,12 @@
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
- this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+ this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
+ this.inState = { click: false, hover: false, focus: false }
+
+ if (this.$element[0] instanceof document.constructor && !this.options.selector) {
+ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
+ }
var triggers = this.options.trigger.split(' ')
@@ -189,6 +197,15 @@
$(obj.currentTarget).data('bs.' + this.type, self)
}
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
+ }
+
+ if (self.tip().hasClass('in') || self.hoverState == 'in') {
+ self.hoverState = 'in'
+ return
+ }
+
clearTimeout(self.timeout)
self.hoverState = 'in'
@@ -200,6 +217,14 @@
}, self.options.delay.show)
}
+ Tooltip.prototype.isInStateTrue = function () {
+ for (var key in this.inState) {
+ if (this.inState[key]) return true
+ }
+
+ return false
+ }
+
Tooltip.prototype.leave = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget).data('bs.' + this.type)
@@ -209,6 +234,12 @@
$(obj.currentTarget).data('bs.' + this.type, self)
}
+ if (obj instanceof $.Event) {
+ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
+ }
+
+ if (self.isInStateTrue()) return
+
clearTimeout(self.timeout)
self.hoverState = 'out'
@@ -226,7 +257,7 @@
if (this.hasContent() && this.enabled) {
this.$element.trigger(e)
- var inDom = $.contains(document.documentElement, this.$element[0])
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
if (e.isDefaultPrevented() || !inDom) return
var that = this
@@ -255,6 +286,7 @@
.data('bs.' + this.type, this)
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+ this.$element.trigger('inserted.bs.' + this.type)
var pos = this.getPosition()
var actualWidth = $tip[0].offsetWidth
@@ -262,13 +294,12 @@
if (autoPlace) {
var orgPlacement = placement
- var $parent = this.$element.parent()
- var parentDim = this.getPosition($parent)
+ var viewportDim = this.getPosition(this.$viewport)
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
- placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
- placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
+ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
+ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
placement
$tip
@@ -281,14 +312,17 @@
this.applyPlacement(calculatedOffset, placement)
var complete = function () {
+ var prevHoverState = that.hoverState
that.$element.trigger('shown.bs.' + that.type)
that.hoverState = null
+
+ if (prevHoverState == 'out') that.leave(that)
}
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
- .emulateTransitionEnd(150) :
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
complete()
}
}
@@ -306,8 +340,8 @@
if (isNaN(marginTop)) marginTop = 0
if (isNaN(marginLeft)) marginLeft = 0
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
+ offset.top += marginTop
+ offset.left += marginLeft
// $.fn.offset doesn't round pixel values
// so we use setOffset directly with our own function B-0
@@ -335,16 +369,18 @@
if (delta.left) offset.left += delta.left
else offset.top += delta.top
- var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
- var arrowPosition = delta.left ? 'left' : 'top'
- var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
+ var isVertical = /top|bottom/.test(placement)
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
$tip.offset(offset)
- this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
}
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
+ this.arrow()
+ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+ .css(isVertical ? 'top' : 'left', '')
}
Tooltip.prototype.setContent = function () {
@@ -355,16 +391,19 @@
$tip.removeClass('fade in top bottom left right')
}
- Tooltip.prototype.hide = function () {
+ Tooltip.prototype.hide = function (callback) {
var that = this
- var $tip = this.tip()
+ var $tip = $(this.$tip)
var e = $.Event('hide.bs.' + this.type)
- this.$element.removeAttr('aria-describedby')
-
function complete() {
if (that.hoverState != 'in') $tip.detach()
- that.$element.trigger('hidden.bs.' + that.type)
+ if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
+ that.$element
+ .removeAttr('aria-describedby')
+ .trigger('hidden.bs.' + that.type)
+ }
+ callback && callback()
}
this.$element.trigger(e)
@@ -373,10 +412,10 @@
$tip.removeClass('in')
- $.support.transition && this.$tip.hasClass('fade') ?
+ $.support.transition && $tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
- .emulateTransitionEnd(150) :
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
complete()
this.hoverState = null
@@ -386,7 +425,7 @@
Tooltip.prototype.fixTitle = function () {
var $e = this.$element
- if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
+ if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
}
}
@@ -397,20 +436,30 @@
Tooltip.prototype.getPosition = function ($element) {
$element = $element || this.$element
+
var el = $element[0]
var isBody = el.tagName == 'BODY'
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
- scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
- width: isBody ? $(window).width() : $element.outerWidth(),
- height: isBody ? $(window).height() : $element.outerHeight()
- }, isBody ? { top: 0, left: 0 } : $element.offset())
+
+ var elRect = el.getBoundingClientRect()
+ if (elRect.width == null) {
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+ }
+ var isSvg = window.SVGElement && el instanceof window.SVGElement
+ // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
+ // See https://github.com/twbs/bootstrap/issues/20280
+ var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
+
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
}
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
}
@@ -434,7 +483,7 @@
var rightEdgeOffset = pos.left + viewportPadding + actualWidth
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
delta.left = viewportDimensions.left - leftEdgeOffset
- } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
}
}
@@ -460,21 +509,19 @@
}
Tooltip.prototype.tip = function () {
- return (this.$tip = this.$tip || $(this.options.template))
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ if (this.$tip.length != 1) {
+ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
+ }
+ }
+ return this.$tip
}
Tooltip.prototype.arrow = function () {
return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
}
- Tooltip.prototype.validate = function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
Tooltip.prototype.enable = function () {
this.enabled = true
}
@@ -497,12 +544,28 @@
}
}
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ if (e) {
+ self.inState.click = !self.inState.click
+ if (self.isInStateTrue()) self.enter(self)
+ else self.leave(self)
+ } else {
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
}
Tooltip.prototype.destroy = function () {
+ var that = this
clearTimeout(this.timeout)
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+ this.hide(function () {
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
+ if (that.$tip) {
+ that.$tip.detach()
+ }
+ that.$tip = null
+ that.$arrow = null
+ that.$viewport = null
+ that.$element = null
+ })
}
@@ -515,7 +578,7 @@
var data = $this.data('bs.tooltip')
var options = typeof option == 'object' && option
- if (!data && option == 'destroy') return
+ if (!data && /destroy|hide/.test(option)) return
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
@@ -538,10 +601,10 @@
}(jQuery);
/* ========================================================================
- * Bootstrap: popover.js v3.2.0
+ * Bootstrap: popover.js v3.3.7
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
+ * Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
@@ -558,7 +621,7 @@
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
- Popover.VERSION = '3.2.0'
+ Popover.VERSION = '3.3.7'
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
placement: 'right',
@@ -585,7 +648,7 @@
var content = this.getContent()
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events
+ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
](content)
@@ -614,11 +677,6 @@
return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
}
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
-
// POPOVER PLUGIN DEFINITION
// =========================
@@ -629,7 +687,7 @@
var data = $this.data('bs.popover')
var options = typeof option == 'object' && option
- if (!data && option == 'destroy') return
+ if (!data && /destroy|hide/.test(option)) return
if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
@@ -651,7 +709,17 @@
}(jQuery);
-(function($, window) {
+(function(window, factory) {
+ if (typeof define === 'function' && define.amd) {
+ return define(['jquery'], function(jQuery) {
+ return window.Tour = factory(jQuery);
+ });
+ } else if (typeof exports === 'object') {
+ return module.exports = factory(require('jQuery'));
+ } else {
+ return window.Tour = factory(window.jQuery);
+ }
+})(window, function($) {
var Tour, document;
document = window.document;
Tour = (function() {
@@ -740,6 +808,7 @@
backdropPadding: this._options.backdropPadding,
redirect: this._options.redirect,
reflexElement: this._options.steps[i].element,
+ backdropElement: this._options.steps[i].element,
orphan: this._options.orphan,
duration: this._options.duration,
delay: this._options.delay,
@@ -795,19 +864,19 @@
Tour.prototype.next = function() {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, this._current + 1);
return this._callOnPromiseDone(promise, this._showNextStep);
};
Tour.prototype.prev = function() {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, this._current - 1);
return this._callOnPromiseDone(promise, this._showPrevStep);
};
Tour.prototype.goTo = function(i) {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, i);
return this._callOnPromiseDone(promise, this.showStep, i);
};
@@ -881,8 +950,8 @@
}
};
- Tour.prototype.hideStep = function(i) {
- var hideStepHelper, promise, step;
+ Tour.prototype.hideStep = function(i, iNext) {
+ var hideDelay, hideStepHelper, promise, step;
step = this.getStep(i);
if (!step) {
return;
@@ -891,30 +960,42 @@
promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
hideStepHelper = (function(_this) {
return function(e) {
- var $element;
+ var $element, next_step;
$element = $(step.element);
if (!($element.data('bs.popover') || $element.data('popover'))) {
$element = $('body');
}
- $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element");
- $element.removeData('bs.popover');
+ $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element").removeData('bs.popover').focus();
if (step.reflex) {
$(step.reflexElement).removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
}
if (step.backdrop) {
- _this._hideBackdrop();
+ next_step = (iNext != null) && _this.getStep(iNext);
+ if (!next_step || !next_step.backdrop || next_step.backdropElement !== step.backdropElement) {
+ _this._hideBackdrop();
+ }
}
if (step.onHidden != null) {
return step.onHidden(_this);
}
};
})(this);
- this._callOnPromiseDone(promise, hideStepHelper);
+ hideDelay = step.delay.hide || step.delay;
+ if ({}.toString.call(hideDelay) === '[object Number]' && hideDelay > 0) {
+ this._debug("Wait " + hideDelay + " milliseconds to hide the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, hideStepHelper);
+ };
+ })(this), hideDelay);
+ } else {
+ this._callOnPromiseDone(promise, hideStepHelper);
+ }
return promise;
};
Tour.prototype.showStep = function(i) {
- var promise, showStepHelper, skipToPrevious, step;
+ var path, promise, showDelay, showStepHelper, skipToPrevious, step;
if (this.ended()) {
this._debug('Tour ended, showStep prevented.');
return this;
@@ -925,26 +1006,26 @@
}
skipToPrevious = i < this._current;
promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ this.setCurrentStep(i);
+ path = (function() {
+ switch ({}.toString.call(step.path)) {
+ case '[object Function]':
+ return step.path();
+ case '[object String]':
+ return this._options.basePath + step.path;
+ default:
+ return step.path;
+ }
+ }).call(this);
+ if (step.redirect && this._isRedirect(step.host, path, document.location)) {
+ this._redirect(step, i, path);
+ if (!this._isJustPathHashDifferent(step.host, path, document.location)) {
+ return;
+ }
+ }
showStepHelper = (function(_this) {
return function(e) {
- var path, showPopoverAndOverlay;
- _this.setCurrentStep(i);
- path = (function() {
- switch ({}.toString.call(step.path)) {
- case '[object Function]':
- return step.path();
- case '[object String]':
- return this._options.basePath + step.path;
- default:
- return step.path;
- }
- }).call(_this);
- if (_this._isRedirect(step.host, path, document.location)) {
- _this._redirect(step, i, path);
- if (!_this._isJustPathHashDifferent(step.host, path, document.location)) {
- return;
- }
- }
+ var showPopoverAndOverlay;
if (_this._isOrphan(step)) {
if (step.orphan === false) {
_this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
@@ -965,7 +1046,7 @@
return;
}
if ((step.element != null) && step.backdrop) {
- _this._showOverlayElement(step);
+ _this._showOverlayElement(step, true);
}
_this._showPopover(step, i);
if (step.onShown != null) {
@@ -974,7 +1055,7 @@
return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
};
if (step.autoscroll) {
- _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ _this._scrollIntoView(step, showPopoverAndOverlay);
} else {
showPopoverAndOverlay();
}
@@ -983,13 +1064,14 @@
}
};
})(this);
- if (step.delay) {
- this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ showDelay = step.delay.show || step.delay;
+ if ({}.toString.call(showDelay) === '[object Number]' && showDelay > 0) {
+ this._debug("Wait " + showDelay + " milliseconds to show the step " + (this._current + 1));
window.setTimeout((function(_this) {
return function() {
return _this._callOnPromiseDone(promise, showStepHelper);
};
- })(this), step.delay);
+ })(this), showDelay);
} else {
this._callOnPromiseDone(promise, showStepHelper);
}
@@ -1098,17 +1180,22 @@
Tour.prototype._isRedirect = function(host, path, location) {
var currentPath;
- if (host !== '') {
- if (this._isHostDifferent(host, location.href)) {
- return true;
- }
+ if ((host != null) && host !== '' && (({}.toString.call(host) === '[object RegExp]' && !host.test(location.origin)) || ({}.toString.call(host) === '[object String]' && this._isHostDifferent(host, location)))) {
+ return true;
}
currentPath = [location.pathname, location.search, location.hash].join('');
return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath)));
};
- Tour.prototype._isHostDifferent = function(host, currentURL) {
- return this._getProtocol(host) !== this._getProtocol(currentURL) || this._getHost(host) !== this._getHost(currentURL);
+ Tour.prototype._isHostDifferent = function(host, location) {
+ switch ({}.toString.call(host)) {
+ case '[object RegExp]':
+ return !host.test(location.origin);
+ case '[object String]':
+ return this._getProtocol(host) !== this._getProtocol(location.href) || this._getHost(host) !== this._getHost(location.href);
+ default:
+ return true;
+ }
};
Tour.prototype._isPathDifferent = function(path, currentPath) {
@@ -1117,8 +1204,8 @@
Tour.prototype._isJustPathHashDifferent = function(host, path, location) {
var currentPath;
- if (host !== '') {
- if (this._isHostDifferent(host, location.href)) {
+ if ((host != null) && host !== '') {
+ if (this._isHostDifferent(host, location)) {
return false;
}
}
@@ -1130,10 +1217,12 @@
};
Tour.prototype._redirect = function(step, i, path) {
+ var href;
if ($.isFunction(step.redirect)) {
return step.redirect.call(this, path);
- } else if (step.redirect === true) {
- this._debug("Redirect to " + step.host + path);
+ } else {
+ href = {}.toString.call(step.host) === '[object String]' ? "" + step.host + path : path;
+ this._debug("Redirect to " + href);
if (this._getState('redirect_to') === ("" + i)) {
this._debug("Error redirection loop to " + path);
this._removeState('redirect_to');
@@ -1142,7 +1231,7 @@
}
} else {
this._setState('redirect_to', "" + i);
- return document.location.href = "" + step.host + path;
+ return document.location.href = href;
}
}
};
@@ -1195,6 +1284,7 @@
}).popover('show');
$tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
$tip.attr('id', step.id);
+ this._focus($tip, $element, step.next < 0);
this._reposition($tip, step);
if (isOrphan) {
return this._center($tip);
@@ -1220,12 +1310,10 @@
$template.addClass("tour-" + this._options.name + "-reflex");
}
if (step.prev < 0) {
- $prev.addClass('disabled');
- $prev.prop('disabled', true);
+ $prev.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
}
if (step.next < 0) {
- $next.addClass('disabled');
- $next.prop('disabled', true);
+ $next.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
}
if (!step.duration) {
$resume.remove();
@@ -1241,6 +1329,15 @@
}
};
+ Tour.prototype._focus = function($tip, $element, end) {
+ var $next, role;
+ role = end ? 'end' : 'next';
+ $next = $tip.find("[data-role='" + role + "']");
+ return $element.on('shown.bs.popover', function() {
+ return $next.focus();
+ });
+ };
+
Tour.prototype._reposition = function($tip, step) {
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
offsetWidth = $tip[0].offsetWidth;
@@ -1282,16 +1379,28 @@
return $tip.find('.arrow').css(position, delta ? 50 * (1 - delta / dimension) + '%' : '');
};
- Tour.prototype._scrollIntoView = function(element, callback) {
- var $element, $window, counter, offsetTop, scrollTop, windowHeight;
- $element = $(element);
+ Tour.prototype._scrollIntoView = function(step, callback) {
+ var $element, $window, counter, height, offsetTop, scrollTop, windowHeight;
+ $element = $(step.element);
if (!$element.length) {
return callback();
}
$window = $(window);
offsetTop = $element.offset().top;
+ height = $element.outerHeight();
windowHeight = $window.height();
- scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ scrollTop = 0;
+ switch (step.placement) {
+ case 'top':
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ break;
+ case 'left':
+ case 'right':
+ scrollTop = Math.max(0, (offsetTop + height / 2) - (windowHeight / 2));
+ break;
+ case 'bottom':
+ scrollTop = Math.max(0, (offsetTop + height) - (windowHeight / 2));
+ }
this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
counter = 0;
return $('body, html').stop(true, true).animate({
@@ -1324,7 +1433,9 @@
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']", (function(_this) {
return function(e) {
e.preventDefault();
- return _this.prev();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
};
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
return function(e) {
@@ -1367,10 +1478,6 @@
if (_this._current > 0) {
return _this.prev();
}
- break;
- case 27:
- e.preventDefault();
- return _this.end();
}
};
})(this));
@@ -1413,7 +1520,7 @@
};
Tour.prototype._hideBackground = function() {
- if (this.backdrop) {
+ if (this.backdrop && this.backdrop.remove) {
this.backdrop.remove();
this.backdrop.overlay = null;
return this.backdrop.backgroundShown = false;
@@ -1421,13 +1528,14 @@
};
Tour.prototype._showOverlayElement = function(step, force) {
- var $element, elementData;
+ var $backdropElement, $element, elementData;
$element = $(step.element);
+ $backdropElement = $(step.backdropElement);
if (!$element || $element.length === 0 || this.backdrop.overlayElementShown && !force) {
return;
}
if (!this.backdrop.overlayElementShown) {
- this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$element = $backdropElement.addClass('tour-step-backdrop');
this.backdrop.$background = $('<div>', {
"class": 'tour-step-background'
});
@@ -1435,9 +1543,9 @@
this.backdrop.overlayElementShown = true;
}
elementData = {
- width: $element.innerWidth(),
- height: $element.innerHeight(),
- offset: $element.offset()
+ width: $backdropElement.innerWidth(),
+ height: $backdropElement.innerHeight(),
+ offset: $backdropElement.offset()
};
if (step.backdropPadding) {
elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
@@ -1533,27 +1641,38 @@
};
Tour.prototype._equal = function(obj1, obj2) {
- var k, v;
+ var k, obj1Keys, obj2Keys, v, _i, _len;
if ({}.toString.call(obj1) === '[object Object]' && {}.toString.call(obj2) === '[object Object]') {
+ obj1Keys = Object.keys(obj1);
+ obj2Keys = Object.keys(obj2);
+ if (obj1Keys.length !== obj2Keys.length) {
+ return false;
+ }
for (k in obj1) {
v = obj1[k];
- if (obj2[k] !== v) {
+ if (!this._equal(obj2[k], v)) {
return false;
}
}
- for (k in obj2) {
- v = obj2[k];
- if (obj1[k] !== v) {
+ return true;
+ } else if ({}.toString.call(obj1) === '[object Array]' && {}.toString.call(obj2) === '[object Array]') {
+ if (obj1.length !== obj2.length) {
+ return false;
+ }
+ for (k = _i = 0, _len = obj1.length; _i < _len; k = ++_i) {
+ v = obj1[k];
+ if (!this._equal(v, obj2[k])) {
return false;
}
}
return true;
+ } else {
+ return obj1 === obj2;
}
- return obj1 === obj2;
};
return Tour;
})();
- return window.Tour = Tour;
-})(jQuery, window);
+ return Tour;
+});
diff --git a/gulpfile.coffee b/gulpfile.coffee
index 990b169..8a57b80 100644
--- a/gulpfile.coffee
+++ b/gulpfile.coffee
@@ -202,7 +202,7 @@ gulp.task 'watch', ['connect'], ->
gulp.task 'bump', ['test'], ->
bumpType = $.util.env.type || 'patch'
- return gulp.src(['./bower.json', './package.json', './smart.json'])
+ return gulp.src(['./package.json', './smart.json'])
.pipe $.bump(type: bumpType)
.pipe gulp.dest('./')
diff --git a/package.json b/package.json
index 6206188..0d75fec 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "bootstrap-tour",
"description": "Quick and easy way to build your product tours with Bootstrap Popovers.",
- "version": "0.10.3",
+ "version": "0.11.0",
"keywords": [
"tour",
"bootstrap",
@@ -45,8 +45,8 @@
},
"devDependencies": {
"coffee-script": "~1.7.1",
- "gulp": "~3.8.6",
- "gulp-bump": "^0.1.11",
+ "gulp": "~3.9.0",
+ "gulp-bump": "~2.2",
"gulp-changed": "~0.4.1",
"gulp-clean": "~0.3.1",
"gulp-coffee": "~2.1.1",
diff --git a/smart.json b/smart.json
index 0bcea8f..a4aca04 100644
--- a/smart.json
+++ b/smart.json
@@ -3,7 +3,7 @@
"description": "Quick and easy way to build your product tours with Bootstrap Popovers.",
"homepage": "http://bootstraptour.com",
"author": "Ulrich Sossou <sorich87 at gmail.com> (http://ulrichsossou.com)",
- "version": "0.10.3",
+ "version": "0.11.0",
"git": "https://github.com/sorich87/bootstrap-tour.git",
"packages": {}
}
diff --git a/src/coffee/bootstrap-tour.coffee b/src/coffee/bootstrap-tour.coffee
index bcb0206..c7b4f98 100644
--- a/src/coffee/bootstrap-tour.coffee
+++ b/src/coffee/bootstrap-tour.coffee
@@ -1,4 +1,11 @@
-(($, window) ->
+((window, factory) ->
+ if typeof define is 'function' and define.amd
+ define ['jquery'], (jQuery) -> (window.Tour = factory(jQuery))
+ else if typeof exports is 'object'
+ module.exports = factory(require('jQuery'))
+ else
+ window.Tour = factory(window.jQuery)
+)(window, ($) ->
document = window.document
class Tour
@@ -97,6 +104,7 @@
backdropPadding: @_options.backdropPadding
redirect: @_options.redirect
reflexElement: @_options.steps[i].element
+ backdropElement: @_options.steps[i].element
orphan: @_options.orphan
duration: @_options.duration
delay: @_options.delay
@@ -145,16 +153,16 @@
# Hide current step and show next step
next: ->
- promise = @hideStep @_current
+ promise = @hideStep @_current, @_current+1
@_callOnPromiseDone promise, @_showNextStep
# Hide current step and show prev step
prev: ->
- promise = @hideStep @_current
+ promise = @hideStep @_current, @_current-1
@_callOnPromiseDone promise, @_showPrevStep
goTo: (i) ->
- promise = @hideStep @_current
+ promise = @hideStep @_current, i
@_callOnPromiseDone promise, @showStep, i
# End tour
@@ -215,7 +223,7 @@
step.onResume @, @_duration if step.onResume? and @_duration isnt step.duration
# Hide the specified step
- hideStep: (i) ->
+ hideStep: (i, iNext) ->
step = @getStep i
return unless step
@@ -228,20 +236,32 @@
$element = $ step.element
$element = $('body') unless $element.data('bs.popover') or $element.data('popover')
$element
- .popover('destroy')
- .removeClass "tour-#{@_options.name}-element tour-#{@_options.name}-#{i}-element"
- $element
- .removeData('bs.popover')
+ .popover('destroy')
+ .removeClass("tour-#{@_options.name}-element tour-#{@_options.name}-#{i}-element")
+ .removeData('bs.popover')
+ .focus()
+
if step.reflex
$ step.reflexElement
.removeClass('tour-step-element-reflex')
.off "#{@_reflexEvent(step.reflex)}.tour-#{@_options.name}"
- @_hideBackdrop() if step.backdrop
+ if step.backdrop
+ next_step = iNext? and @getStep iNext
+ @_hideBackdrop() if !next_step or !next_step.backdrop or next_step.backdropElement != step.backdropElement
step.onHidden(@) if step.onHidden?
- @_callOnPromiseDone promise, hideStepHelper
+ hideDelay = step.delay.hide || step.delay
+
+ if ({}).toString.call(hideDelay) is '[object Number]' and hideDelay > 0
+ @_debug "Wait #{hideDelay} milliseconds to hide the step #{@_current + 1}"
+ window.setTimeout =>
+ @_callOnPromiseDone promise, hideStepHelper
+ , hideDelay
+ else
+ @_callOnPromiseDone promise, hideStepHelper
+
promise
# Show the specified step
@@ -258,21 +278,21 @@
# If onShow returns a promise, let's wait until it's done to execute
promise = @_makePromise(step.onShow @, i if step.onShow?)
- showStepHelper = (e) =>
- @setCurrentStep i
+ @setCurrentStep i
- # Support string or function for path
- path = switch ({}).toString.call step.path
- when '[object Function]' then step.path()
- when '[object String]' then @_options.basePath + step.path
- else step.path
+ # Support string or function for path
+ path = switch ({}).toString.call step.path
+ when '[object Function]' then step.path()
+ when '[object String]' then @_options.basePath + step.path
+ else step.path
- # Redirect to step path if not already there
- if @_isRedirect step.host, path, document.location
- @_redirect step, i, path
+ # Redirect to step path if not already there
+ if step.redirect and @_isRedirect step.host, path, document.location
+ @_redirect step, i, path
- return unless @_isJustPathHashDifferent(step.host, path, document.location)
+ return unless @_isJustPathHashDifferent(step.host, path, document.location)
+ showStepHelper = (e) =>
# Skip if step is orphan and orphan options is false
if @_isOrphan step
if step.orphan is false
@@ -289,24 +309,26 @@
showPopoverAndOverlay = =>
return if @getCurrentStep() isnt i or @ended()
- @_showOverlayElement step if step.element? and step.backdrop
+ @_showOverlayElement step, true if step.element? and step.backdrop
@_showPopover step, i
step.onShown @ if step.onShown?
@_debug "Step #{@_current + 1} of #{@_options.steps.length}"
if step.autoscroll
- @_scrollIntoView step.element, showPopoverAndOverlay
+ @_scrollIntoView step, showPopoverAndOverlay
else
showPopoverAndOverlay()
# Play step timer
@resume() if step.duration
- if step.delay
- @_debug "Wait #{step.delay} milliseconds to show the step #{@_current + 1}"
+ showDelay = step.delay.show || step.delay
+
+ if ({}).toString.call(showDelay) is '[object Number]' and showDelay > 0
+ @_debug "Wait #{showDelay} milliseconds to show the step #{@_current + 1}"
window.setTimeout =>
@_callOnPromiseDone promise, showStepHelper
- , step.delay
+ , showDelay
else
@_callOnPromiseDone promise, showStepHelper
@@ -386,8 +408,10 @@
# Check if step path equals current document path
_isRedirect: (host, path, location) ->
- if host isnt ''
- return true if @_isHostDifferent(host, location.href)
+ return true if host? and host isnt '' and (
+ (({}).toString.call(host) is '[object RegExp]' and not host.test(location.origin)) or
+ (({}).toString.call(host) is '[object String]' and @_isHostDifferent(host, location))
+ )
currentPath = [
location.pathname,
@@ -400,9 +424,15 @@
(({}).toString.call(path) is '[object String]' and @_isPathDifferent(path, currentPath))
)
- _isHostDifferent: (host, currentURL) ->
- @_getProtocol(host) isnt @_getProtocol(currentURL) or
- @_getHost(host) isnt @_getHost(currentURL)
+ _isHostDifferent: (host, location) ->
+ switch ({}).toString.call(host)
+ when '[object RegExp]'
+ not host.test(location.origin)
+ when '[object String]'
+ @_getProtocol(host) isnt @_getProtocol(location.href) or
+ @_getHost(host) isnt @_getHost(location.href)
+ else
+ true
_isPathDifferent: (path, currentPath) ->
@_getPath(path) isnt @_getPath(currentPath) or not
@@ -410,8 +440,8 @@
@_equal(@_getHash(path), @_getHash(currentPath))
_isJustPathHashDifferent: (host, path, location) ->
- if host isnt ''
- return false if @_isHostDifferent(host, location.href)
+ if host? and host isnt ''
+ return false if @_isHostDifferent(host, location)
currentPath = [
location.pathname,
@@ -430,8 +460,10 @@
_redirect: (step, i, path) ->
if $.isFunction step.redirect
step.redirect.call this, path
- else if step.redirect is true
- @_debug "Redirect to #{step.host}#{path}"
+ else
+ href = if ({}).toString.call(step.host) is '[object String]' then "#{step.host}#{path}" else path
+ @_debug "Redirect to #{href}"
+
if @_getState('redirect_to') is "#{i}"
@_debug "Error redirection loop to #{path}"
@_removeState 'redirect_to'
@@ -439,7 +471,7 @@
step.onRedirectError @ if step.onRedirectError?
else
@_setState 'redirect_to', "#{i}"
- document.location.href = "#{step.host}#{path}"
+ document.location.href = href
_isOrphan: (step) ->
# Do not check for is(':hidden') on svg elements. jQuery does not work properly on svg.
@@ -496,6 +528,8 @@
# Tip adjustment
$tip = if $element.data 'bs.popover' then $element.data('bs.popover').tip() else $element.data('popover').tip()
$tip.attr 'id', step.id
+
+ @_focus $tip, $element, step.next < 0
@_reposition $tip, step
@_center $tip if isOrphan
@@ -515,18 +549,29 @@
$template.addClass 'orphan' if @_isOrphan step
$template.addClass "tour-#{@_options.name} tour-#{@_options.name}-#{i}"
$template.addClass "tour-#{@_options.name}-reflex" if step.reflex
+
if step.prev < 0
$prev.addClass('disabled')
- $prev.prop('disabled',true)
+ .prop('disabled', true)
+ .prop('tabindex', -1)
+
if step.next < 0
$next.addClass('disabled')
- $next.prop('disabled',true)
+ .prop('disabled', true)
+ .prop('tabindex', -1)
+
$resume.remove() unless step.duration
$template.clone().wrap('<div>').parent().html()
_reflexEvent: (reflex) ->
if ({}).toString.call(reflex) is '[object Boolean]' then 'click' else reflex
+ _focus: ($tip, $element, end) ->
+ role = if end then 'end' else 'next'
+ $next = $tip.find("[data-role='#{role}']")
+
+ $element.on 'shown.bs.popover', -> $next.focus()
+
# Prevent popover from crossing over the edge of the window
_reposition: ($tip, step) ->
offsetWidth = $tip[0].offsetWidth
@@ -562,14 +607,23 @@
$tip.find('.arrow').css position, if delta then 50 * (1 - delta / dimension) + '%' else ''
# Scroll to the popup if it is not in the viewport
- _scrollIntoView: (element, callback) ->
- $element = $(element)
+ _scrollIntoView: (step, callback) ->
+ $element = $(step.element)
return callback() unless $element.length
$window = $(window)
offsetTop = $element.offset().top
+ height = $element.outerHeight()
windowHeight = $window.height()
- scrollTop = Math.max(0, offsetTop - (windowHeight / 2))
+ scrollTop = 0
+
+ switch step.placement
+ when 'top'
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2))
+ when 'left', 'right'
+ scrollTop = Math.max(0, (offsetTop + height / 2) - (windowHeight / 2))
+ when 'bottom'
+ scrollTop = Math.max(0, (offsetTop + height) - (windowHeight / 2))
@_debug "Scroll into view. ScrollTop: #{scrollTop}. Element offset: #{offsetTop}. Window height: #{windowHeight}."
counter = 0
@@ -588,6 +642,7 @@
clearTimeout(timeout)
timeout = setTimeout(callback, 100)
+
# Event bindings for mouse navigation
_initMouseNavigation: ->
_this = @
@@ -606,7 +661,7 @@
@next()
.on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='prev']", (e) =>
e.preventDefault()
- @prev()
+ @prev() if @_current > 0
.on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='end']", (e) =>
e.preventDefault()
@end()
@@ -631,9 +686,6 @@
when 37
e.preventDefault()
@prev() if @_current > 0
- when 27
- e.preventDefault()
- @end()
# Checks if the result of a callback is a promise
_makePromise: (result) ->
@@ -658,26 +710,27 @@
@_hideBackground()
_hideBackground: ->
- if @backdrop
+ if @backdrop && @backdrop.remove
@backdrop.remove()
@backdrop.overlay = null
@backdrop.backgroundShown = false
_showOverlayElement: (step, force) ->
$element = $ step.element
+ $backdropElement = $ step.backdropElement
return if not $element or $element.length is 0 or @backdrop.overlayElementShown and not force
if !@backdrop.overlayElementShown
- @backdrop.$element = $element.addClass 'tour-step-backdrop'
+ @backdrop.$element = $backdropElement.addClass 'tour-step-backdrop'
@backdrop.$background = $ '<div>', class: 'tour-step-background'
@backdrop.$background.appendTo(step.backdropContainer)
@backdrop.overlayElementShown = true
elementData =
- width: $element.innerWidth()
- height: $element.innerHeight()
- offset: $element.offset()
+ width: $backdropElement.innerWidth()
+ height: $backdropElement.innerHeight()
+ offset: $backdropElement.offset()
elementData = @_applyBackdropPadding step.backdropPadding, elementData if step.backdropPadding
@backdrop
@@ -752,15 +805,24 @@
return paramsObject
_equal: (obj1, obj2) ->
- if ({}).toString.call(obj1) is '[object Object]' and
- ({}).toString.call(obj2) is '[object Object]'
+ if ({}).toString.call(obj1) is '[object Object]' and ({}).toString.call(obj2) is '[object Object]'
+ obj1Keys = Object.keys(obj1)
+ obj2Keys = Object.keys(obj2)
+ return false if obj1Keys.length isnt obj2Keys.length
+
for k,v of obj1
- return false if obj2[k] isnt v
- for k,v of obj2
- return false if obj1[k] isnt v
+ return false if not @_equal(obj2[k], v)
+
return true
+ else if ({}).toString.call(obj1) is '[object Array]' and ({}).toString.call(obj2) is '[object Array]'
+ return false if obj1.length isnt obj2.length
- return obj1 is obj2
- window.Tour = Tour
+ for v,k in obj1
+ return false if not @_equal(v, obj2[k])
+
+ return true
+ else
+ return obj1 is obj2
-) jQuery, window
+ Tour
+)
diff --git a/src/coffee/bootstrap-tour.spec.coffee b/src/coffee/bootstrap-tour.spec.coffee
index dbc8cb7..f6742ce 100644
--- a/src/coffee/bootstrap-tour.spec.coffee
+++ b/src/coffee/bootstrap-tour.spec.coffee
@@ -211,6 +211,7 @@ describe 'Bootstrap Tour', ->
backdrop: false
backdropPadding: 0
backdropContainer: 'body'
+ backdropElement: $('<div></div>').appendTo('body')
redirect: true
reflexElement: $('<div></div>').appendTo('body')
orphan: false
@@ -426,140 +427,287 @@ describe 'Bootstrap Tour', ->
# redirect if path doesn't match current path
expect(
@tour._isRedirect(
- '', '/anotherpath', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/anotherpath',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# don't redirect if no path
expect(
@tour._isRedirect(
- '', undefined, href: '', pathname: '/', search: '', hash: ''
+ '', undefined,
+ {
+ origin: ''
+ href: ''
+ pathname: '/'
+ search: ''
+ hash: ''
+ }
)
).toBe false
# don't redirect if path empty
expect(
@tour._isRedirect(
- '', '', href: '', pathname: '/', search: '', hash: ''
+ '', '',
+ {
+ origin: ''
+ href: ''
+ pathname: '/'
+ search: ''
+ hash: ''
+ }
)
).toBe false
# don't redirect if path matches current path
expect(
@tour._isRedirect(
- '', '/somepath', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe false
# don't redirect if path with slash matches current path
expect(
@tour._isRedirect(
- '', '/somepath/', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath/',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe false
# don't redirect if path matches current path with slash
expect(
@tour._isRedirect(
- '', '/somepath', href: '', pathname: '/somepath/', search: '', hash: ''
+ '', '/somepath',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath/'
+ search: ''
+ hash: ''
+ }
)
).toBe false
# redirect if path with query params doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath?search=true',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path with slash and query params doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath/?search=true', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath/?search=true',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path with more than one query param doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath?search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path with and query params doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true&foo=bar', href: '', pathname: '/somepath', search: '?search=true', hash: ''
+ '', '/somepath?search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: '?search=true'
+ hash: ''
+ }
)
).toBe true
# redirect if path query params number doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true&foo=bar', href: '', pathname: '/somepath', search: '?foo=bar', hash: ''
+ '', '/somepath?search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: '?foo=bar'
+ hash: ''
+ }
)
).toBe true
# don't redirect if path with query params matches current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true&foo=bar', href: '', pathname: '/somepath', search: '?foo=bar&search=true', hash: ''
+ '', '/somepath?search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: '?foo=bar&search=true'
+ hash: ''
+ }
)
).toBe false
# don't redirect if path with query params matches current path
expect(
@tour._isRedirect(
- '', '/somepath?search=true&foo=bar', href: '', pathname: '/somepath', search: '?search=true&foo=bar', hash:''
+ '', '/somepath?search=true&foo=bar'
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: '?search=true&foo=bar'
+ hash:''
+ }
)
).toBe false
# redirect if path with one hash param doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath#search=true',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path with slash and one hash param doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath/#search=true', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath/#search=true',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path with more than one hash params doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: ''
+ '', '/somepath#search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: ''
+ }
)
).toBe true
# redirect if path hash params number doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: '#search=true'
+ '', '/somepath#search=true&foo=bar',
+ {
+ origin: ''
+ href: ''
+ pathname: '/somepath'
+ search: ''
+ hash: '#search=true'
+ }
)
).toBe true
# redirect if path hash params number doesn't matche current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: '#foo=bar'
+ '', '/somepath#search=true&foo=bar',
+ {
+ origin: '',
+ href: '',
+ pathname: '/somepath',
+ search: '',
+ hash: '#foo=bar'
+ }
)
).toBe true
# don't redirect if path with hash params matches current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: '#foo=bar&search=true'
+ '', '/somepath#search=true&foo=bar',
+ {
+ origin: '',
+ href: '',
+ pathname: '/somepath',
+ search: '',
+ hash: '#foo=bar&search=true'
+ }
)
).toBe false
# don't redirect if path with hash params matches current path
expect(
@tour._isRedirect(
- '', '/somepath#search=true&foo=bar', href: '', pathname: '/somepath', search: '', hash: '#search=true&foo=bar'
+ '', '/somepath#search=true&foo=bar',
+ {
+ origin: '',
+ href: '',
+ pathname: '/somepath',
+ search: '',
+ hash: '#search=true&foo=bar'
+ }
)
).toBe false
# don't redirect if current path matches path regex
expect(
@tour._isRedirect(
- '', /some*/, href: '', pathname: '/somepath', search: '', hash: ''
+ '', /some.*/,
+ {
+ origin: '',
+ href: '',
+ pathname: '/somepath',
+ search: '',
+ hash: ''
+ }
)
).toBe false
@@ -585,9 +733,9 @@ describe 'Bootstrap Tour', ->
onShow: -> return deferred
@tour.start()
@tour.next()
- expect(@tour._current).toBe 0 # tour shows old state until resolving of onShow promise
+ expect(@tour.getStep(0).element.data('bs.popover')).toBeUndefined
deferred.resolve()
- expect(@tour._current).toBe 1 # tour shows new state after resolving onShow promise
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1
it 'should not hide popover until the onHide promise is resolved', ->
deferred = $.Deferred()
@@ -746,31 +894,65 @@ describe 'Bootstrap Tour', ->
)
).toBe false
- it 'should redirect to the steps if host is different', ->
+ it 'should evaluate the host correctly', ->
@tour = new Tour
- current_host = document.location.host
-
- @tour.addStep
- element: $('<div></div>').appendTo('body')
- path: 'test.html'
- host: 'http://sub.exemple.com'
expect(
@tour._isRedirect(
- @tour.getStep(0).host,
- @tour._options.basePath + @tour.getStep(0).path,
- href: 'http://exemple.com/test.html' , pathname: 'test.html', search: '', hash: ''
+ 'http://sub.exemple.com',
+ '/test.html',
+ {
+ origin: 'http://exemple.com'
+ href: 'http://exemple.com/test.html'
+ pathname: '/test.html'
+ search: ''
+ hash: ''
+ }
)
).toBe true
expect(
@tour._isRedirect(
- current_host,
- @tour._options.basePath + @tour.getStep(0).path,
- href: "http://#{current_host}/test.html" , pathname: 'test.html', search: '', hash: ''
+ 'http://sub.exemple.com',
+ '/test.html',
+ {
+ origin: 'http://sub.exemple.com'
+ href: 'http://sub.exemple.com/test.html'
+ pathname: '/test.html'
+ search: ''
+ hash: ''
+ }
+ )
+ ).toBe false
+
+ expect(
+ @tour._isRedirect(
+ /http:\/\/.*\.exemple\.com/,
+ '/test.html',
+ {
+ origin: 'http://sub.exemple.com'
+ href: 'http://sub.exemple.com/test.html'
+ pathname: '/test.html'
+ search: ''
+ hash: ''
+ }
)
).toBe false
+ expect(
+ @tour._isRedirect(
+ /http:\/\/exemple\.com/,
+ '/test.html',
+ {
+ origin: 'http://sub.exemple.com'
+ href: 'http://sub.exemple.com/test.html'
+ pathname: '/test.html'
+ search: ''
+ hash: ''
+ }
+ )
+ ).toBe true
+
it 'with `onNext` option should run the callback before showing the next step', ->
tour_test = 0
@tour = new Tour
@@ -999,6 +1181,29 @@ describe 'Bootstrap Tour', ->
expect(@tour._timer).toBe null
expect(@tour._duration).toBe null
+ it 'should call window.setTimeout when delay is defined', ->
+ counter = 0
+ initialTimeout = window.setTimeout
+ window.setTimeout = (callback, duration) ->
+ counter++
+ callback()
+
+ @tour = new Tour
+ delay: {
+ show: 300
+ hide: 400
+ }
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(counter).toBe 1
+ @tour.next()
+ expect(counter).toBe 3
+ @tour.end()
+ expect(counter).toBe 4
+
+ window.setTimeout = initialTimeout
+
### TODO: fix $.support.transition conflict between jquery and bootstrap
it 'should not display inactive popover upon rapid navigation', ->
# Flag that gives signal to the async test that it should evaluate.
diff --git a/src/docs/api.html b/src/docs/api.html
index feb2661..9a4749f 100644
--- a/src/docs/api.html
+++ b/src/docs/api.html
@@ -383,7 +383,7 @@ tour.addStep({
<tbody>
<tr class="success">
<td>host <span class="label label-success">NEW</span></td>
- <td>String</td>
+ <td>String or RegExp</td>
<td>Host of the page on which the step should be shown. This
allows you to build tours for several sub-domains</td>
<td><code>''</code></td>
diff --git a/src/docs/assets/css/bootstrap-tour.css b/src/docs/assets/css/bootstrap-tour.css
index 1b35295..659fa92 100644
--- a/src/docs/assets/css/bootstrap-tour.css
+++ b/src/docs/assets/css/bootstrap-tour.css
@@ -1,5 +1,5 @@
/* ========================================================================
- * bootstrap-tour - v0.10.2
+ * bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
* Copyright 2012-2015 Ulrich Sossou
@@ -49,6 +49,7 @@
}
.popover[class*="tour-"] .popover-navigation {
padding: 9px 14px;
+ overflow: hidden;
}
.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
float: right;
diff --git a/src/less/bootstrap-tour.less b/src/less/bootstrap-tour.less
index ab5f9fc..86f906f 100644
--- a/src/less/bootstrap-tour.less
+++ b/src/less/bootstrap-tour.less
@@ -13,7 +13,7 @@
.tour-step-backdrop {
position: relative;
z-index: 1101;
-
+
> td {
position: relative;
z-index: 1101;
@@ -32,6 +32,7 @@
.popover-navigation {
padding: 9px 14px;
+ overflow: hidden;
*[data-role="end"] {
float: right;
diff --git a/test/bootstrap-tour.js b/test/bootstrap-tour.js
index 009525a..f05a771 100644
--- a/test/bootstrap-tour.js
+++ b/test/bootstrap-tour.js
@@ -1,5 +1,5 @@
/* ========================================================================
- * bootstrap-tour - v0.10.2
+ * bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
* Copyright 2012-2015 Ulrich Sossou
@@ -19,7 +19,17 @@
* ========================================================================
*/
-(function($, window) {
+(function(window, factory) {
+ if (typeof define === 'function' && define.amd) {
+ return define(['jquery'], function(jQuery) {
+ return window.Tour = factory(jQuery);
+ });
+ } else if (typeof exports === 'object') {
+ return module.exports = factory(require('jQuery'));
+ } else {
+ return window.Tour = factory(window.jQuery);
+ }
+})(window, function($) {
var Tour, document;
document = window.document;
Tour = (function() {
@@ -108,6 +118,7 @@
backdropPadding: this._options.backdropPadding,
redirect: this._options.redirect,
reflexElement: this._options.steps[i].element,
+ backdropElement: this._options.steps[i].element,
orphan: this._options.orphan,
duration: this._options.duration,
delay: this._options.delay,
@@ -163,19 +174,19 @@
Tour.prototype.next = function() {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, this._current + 1);
return this._callOnPromiseDone(promise, this._showNextStep);
};
Tour.prototype.prev = function() {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, this._current - 1);
return this._callOnPromiseDone(promise, this._showPrevStep);
};
Tour.prototype.goTo = function(i) {
var promise;
- promise = this.hideStep(this._current);
+ promise = this.hideStep(this._current, i);
return this._callOnPromiseDone(promise, this.showStep, i);
};
@@ -249,8 +260,8 @@
}
};
- Tour.prototype.hideStep = function(i) {
- var hideStepHelper, promise, step;
+ Tour.prototype.hideStep = function(i, iNext) {
+ var hideDelay, hideStepHelper, promise, step;
step = this.getStep(i);
if (!step) {
return;
@@ -259,30 +270,42 @@
promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
hideStepHelper = (function(_this) {
return function(e) {
- var $element;
+ var $element, next_step;
$element = $(step.element);
if (!($element.data('bs.popover') || $element.data('popover'))) {
$element = $('body');
}
- $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element");
- $element.removeData('bs.popover');
+ $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element").removeData('bs.popover').focus();
if (step.reflex) {
$(step.reflexElement).removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
}
if (step.backdrop) {
- _this._hideBackdrop();
+ next_step = (iNext != null) && _this.getStep(iNext);
+ if (!next_step || !next_step.backdrop || next_step.backdropElement !== step.backdropElement) {
+ _this._hideBackdrop();
+ }
}
if (step.onHidden != null) {
return step.onHidden(_this);
}
};
})(this);
- this._callOnPromiseDone(promise, hideStepHelper);
+ hideDelay = step.delay.hide || step.delay;
+ if ({}.toString.call(hideDelay) === '[object Number]' && hideDelay > 0) {
+ this._debug("Wait " + hideDelay + " milliseconds to hide the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, hideStepHelper);
+ };
+ })(this), hideDelay);
+ } else {
+ this._callOnPromiseDone(promise, hideStepHelper);
+ }
return promise;
};
Tour.prototype.showStep = function(i) {
- var promise, showStepHelper, skipToPrevious, step;
+ var path, promise, showDelay, showStepHelper, skipToPrevious, step;
if (this.ended()) {
this._debug('Tour ended, showStep prevented.');
return this;
@@ -293,26 +316,26 @@
}
skipToPrevious = i < this._current;
promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ this.setCurrentStep(i);
+ path = (function() {
+ switch ({}.toString.call(step.path)) {
+ case '[object Function]':
+ return step.path();
+ case '[object String]':
+ return this._options.basePath + step.path;
+ default:
+ return step.path;
+ }
+ }).call(this);
+ if (step.redirect && this._isRedirect(step.host, path, document.location)) {
+ this._redirect(step, i, path);
+ if (!this._isJustPathHashDifferent(step.host, path, document.location)) {
+ return;
+ }
+ }
showStepHelper = (function(_this) {
return function(e) {
- var path, showPopoverAndOverlay;
- _this.setCurrentStep(i);
- path = (function() {
- switch ({}.toString.call(step.path)) {
- case '[object Function]':
- return step.path();
- case '[object String]':
- return this._options.basePath + step.path;
- default:
- return step.path;
- }
- }).call(_this);
- if (_this._isRedirect(step.host, path, document.location)) {
- _this._redirect(step, i, path);
- if (!_this._isJustPathHashDifferent(step.host, path, document.location)) {
- return;
- }
- }
+ var showPopoverAndOverlay;
if (_this._isOrphan(step)) {
if (step.orphan === false) {
_this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
@@ -333,7 +356,7 @@
return;
}
if ((step.element != null) && step.backdrop) {
- _this._showOverlayElement(step);
+ _this._showOverlayElement(step, true);
}
_this._showPopover(step, i);
if (step.onShown != null) {
@@ -342,7 +365,7 @@
return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
};
if (step.autoscroll) {
- _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ _this._scrollIntoView(step, showPopoverAndOverlay);
} else {
showPopoverAndOverlay();
}
@@ -351,13 +374,14 @@
}
};
})(this);
- if (step.delay) {
- this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ showDelay = step.delay.show || step.delay;
+ if ({}.toString.call(showDelay) === '[object Number]' && showDelay > 0) {
+ this._debug("Wait " + showDelay + " milliseconds to show the step " + (this._current + 1));
window.setTimeout((function(_this) {
return function() {
return _this._callOnPromiseDone(promise, showStepHelper);
};
- })(this), step.delay);
+ })(this), showDelay);
} else {
this._callOnPromiseDone(promise, showStepHelper);
}
@@ -466,17 +490,22 @@
Tour.prototype._isRedirect = function(host, path, location) {
var currentPath;
- if (host !== '') {
- if (this._isHostDifferent(host, location.href)) {
- return true;
- }
+ if ((host != null) && host !== '' && (({}.toString.call(host) === '[object RegExp]' && !host.test(location.origin)) || ({}.toString.call(host) === '[object String]' && this._isHostDifferent(host, location)))) {
+ return true;
}
currentPath = [location.pathname, location.search, location.hash].join('');
return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath)));
};
- Tour.prototype._isHostDifferent = function(host, currentURL) {
- return this._getProtocol(host) !== this._getProtocol(currentURL) || this._getHost(host) !== this._getHost(currentURL);
+ Tour.prototype._isHostDifferent = function(host, location) {
+ switch ({}.toString.call(host)) {
+ case '[object RegExp]':
+ return !host.test(location.origin);
+ case '[object String]':
+ return this._getProtocol(host) !== this._getProtocol(location.href) || this._getHost(host) !== this._getHost(location.href);
+ default:
+ return true;
+ }
};
Tour.prototype._isPathDifferent = function(path, currentPath) {
@@ -485,8 +514,8 @@
Tour.prototype._isJustPathHashDifferent = function(host, path, location) {
var currentPath;
- if (host !== '') {
- if (this._isHostDifferent(host, location.href)) {
+ if ((host != null) && host !== '') {
+ if (this._isHostDifferent(host, location)) {
return false;
}
}
@@ -498,10 +527,12 @@
};
Tour.prototype._redirect = function(step, i, path) {
+ var href;
if ($.isFunction(step.redirect)) {
return step.redirect.call(this, path);
- } else if (step.redirect === true) {
- this._debug("Redirect to " + step.host + path);
+ } else {
+ href = {}.toString.call(step.host) === '[object String]' ? "" + step.host + path : path;
+ this._debug("Redirect to " + href);
if (this._getState('redirect_to') === ("" + i)) {
this._debug("Error redirection loop to " + path);
this._removeState('redirect_to');
@@ -510,7 +541,7 @@
}
} else {
this._setState('redirect_to', "" + i);
- return document.location.href = "" + step.host + path;
+ return document.location.href = href;
}
}
};
@@ -563,6 +594,7 @@
}).popover('show');
$tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
$tip.attr('id', step.id);
+ this._focus($tip, $element, step.next < 0);
this._reposition($tip, step);
if (isOrphan) {
return this._center($tip);
@@ -588,12 +620,10 @@
$template.addClass("tour-" + this._options.name + "-reflex");
}
if (step.prev < 0) {
- $prev.addClass('disabled');
- $prev.prop('disabled', true);
+ $prev.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
}
if (step.next < 0) {
- $next.addClass('disabled');
- $next.prop('disabled', true);
+ $next.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
}
if (!step.duration) {
$resume.remove();
@@ -609,6 +639,15 @@
}
};
+ Tour.prototype._focus = function($tip, $element, end) {
+ var $next, role;
+ role = end ? 'end' : 'next';
+ $next = $tip.find("[data-role='" + role + "']");
+ return $element.on('shown.bs.popover', function() {
+ return $next.focus();
+ });
+ };
+
Tour.prototype._reposition = function($tip, step) {
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
offsetWidth = $tip[0].offsetWidth;
@@ -650,16 +689,28 @@
return $tip.find('.arrow').css(position, delta ? 50 * (1 - delta / dimension) + '%' : '');
};
- Tour.prototype._scrollIntoView = function(element, callback) {
- var $element, $window, counter, offsetTop, scrollTop, windowHeight;
- $element = $(element);
+ Tour.prototype._scrollIntoView = function(step, callback) {
+ var $element, $window, counter, height, offsetTop, scrollTop, windowHeight;
+ $element = $(step.element);
if (!$element.length) {
return callback();
}
$window = $(window);
offsetTop = $element.offset().top;
+ height = $element.outerHeight();
windowHeight = $window.height();
- scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ scrollTop = 0;
+ switch (step.placement) {
+ case 'top':
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ break;
+ case 'left':
+ case 'right':
+ scrollTop = Math.max(0, (offsetTop + height / 2) - (windowHeight / 2));
+ break;
+ case 'bottom':
+ scrollTop = Math.max(0, (offsetTop + height) - (windowHeight / 2));
+ }
this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
counter = 0;
return $('body, html').stop(true, true).animate({
@@ -692,7 +743,9 @@
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']", (function(_this) {
return function(e) {
e.preventDefault();
- return _this.prev();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
};
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
return function(e) {
@@ -735,10 +788,6 @@
if (_this._current > 0) {
return _this.prev();
}
- break;
- case 27:
- e.preventDefault();
- return _this.end();
}
};
})(this));
@@ -781,7 +830,7 @@
};
Tour.prototype._hideBackground = function() {
- if (this.backdrop) {
+ if (this.backdrop && this.backdrop.remove) {
this.backdrop.remove();
this.backdrop.overlay = null;
return this.backdrop.backgroundShown = false;
@@ -789,13 +838,14 @@
};
Tour.prototype._showOverlayElement = function(step, force) {
- var $element, elementData;
+ var $backdropElement, $element, elementData;
$element = $(step.element);
+ $backdropElement = $(step.backdropElement);
if (!$element || $element.length === 0 || this.backdrop.overlayElementShown && !force) {
return;
}
if (!this.backdrop.overlayElementShown) {
- this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$element = $backdropElement.addClass('tour-step-backdrop');
this.backdrop.$background = $('<div>', {
"class": 'tour-step-background'
});
@@ -803,9 +853,9 @@
this.backdrop.overlayElementShown = true;
}
elementData = {
- width: $element.innerWidth(),
- height: $element.innerHeight(),
- offset: $element.offset()
+ width: $backdropElement.innerWidth(),
+ height: $backdropElement.innerHeight(),
+ offset: $backdropElement.offset()
};
if (step.backdropPadding) {
elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
@@ -901,27 +951,38 @@
};
Tour.prototype._equal = function(obj1, obj2) {
- var k, v;
+ var k, obj1Keys, obj2Keys, v, _i, _len;
if ({}.toString.call(obj1) === '[object Object]' && {}.toString.call(obj2) === '[object Object]') {
+ obj1Keys = Object.keys(obj1);
+ obj2Keys = Object.keys(obj2);
+ if (obj1Keys.length !== obj2Keys.length) {
+ return false;
+ }
for (k in obj1) {
v = obj1[k];
- if (obj2[k] !== v) {
+ if (!this._equal(obj2[k], v)) {
return false;
}
}
- for (k in obj2) {
- v = obj2[k];
- if (obj1[k] !== v) {
+ return true;
+ } else if ({}.toString.call(obj1) === '[object Array]' && {}.toString.call(obj2) === '[object Array]') {
+ if (obj1.length !== obj2.length) {
+ return false;
+ }
+ for (k = _i = 0, _len = obj1.length; _i < _len; k = ++_i) {
+ v = obj1[k];
+ if (!this._equal(v, obj2[k])) {
return false;
}
}
return true;
+ } else {
+ return obj1 === obj2;
}
- return obj1 === obj2;
};
return Tour;
})();
- return window.Tour = Tour;
-})(jQuery, window);
+ return Tour;
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-bootstrap-tour.git
More information about the Pkg-javascript-commits
mailing list