[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