[Pkg-javascript-commits] [node-bootstrap-tour] 01/03: Imported Upstream version 0.10.2+dfsg
Julien Puydt
julien.puydt at laposte.net
Sat Oct 10 08:09:32 UTC 2015
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 cb1d2742d0f8e87568b1875db494c1f1403b070c
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Sat Oct 10 08:45:17 2015 +0200
Imported Upstream version 0.10.2+dfsg
---
.editorconfig | 21 +
.gitignore | 10 +
.travis.yml | 14 +
CNAME | 1 +
LICENSE | 176 +++
README.md | 77 ++
_config.yml | 5 +
bower.json | 20 +
build/css/bootstrap-tour-standalone.css | 728 +++++++++++
build/css/bootstrap-tour-standalone.min.css | 26 +
build/css/bootstrap-tour.css | 73 ++
build/css/bootstrap-tour.min.css | 22 +
build/js/bootstrap-tour-standalone.js | 1374 ++++++++++++++++++++
build/js/bootstrap-tour.js | 802 ++++++++++++
coffeelint.json | 127 ++
composer.json | 13 +
gulpfile.coffee | 214 +++
gulpfile.js | 2 +
karma.json | 19 +
package.js | 3 +
package.json | 83 ++
smart.json | 9 +
src/coffee/bootstrap-tour.coffee | 655 ++++++++++
src/coffee/bootstrap-tour.docs.coffee | 94 ++
src/coffee/bootstrap-tour.spec.coffee | 775 +++++++++++
src/docs/CNAME | 1 +
src/docs/_includes/footer.html | 14 +
src/docs/_includes/header.html | 29 +
src/docs/_includes/nav.html | 26 +
src/docs/_layouts/default.html | 12 +
src/docs/api.html | 746 +++++++++++
src/docs/assets/css/bootstrap-tour.css | 73 ++
src/docs/assets/css/bootstrap-tour.docs.css | 61 +
.../assets/fonts/glyphicons-halflings-regular.eot | Bin 0 -> 14079 bytes
.../assets/fonts/glyphicons-halflings-regular.svg | 228 ++++
.../assets/fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 29512 bytes
.../assets/fonts/glyphicons-halflings-regular.woff | Bin 0 -> 16448 bytes
.../img/apple-touch-icon-144-precomposed.png | Bin 0 -> 3549 bytes
src/docs/assets/img/favicon.png | Bin 0 -> 887 bytes
src/docs/assets/img/masthead-pattern.png | Bin 0 -> 6450 bytes
src/docs/assets/js/bootstrap-tour.js | 802 ++++++++++++
src/docs/assets/vendor/bootstrap.min.css | 9 +
src/docs/assets/vendor/jquery.smoothscroll.js | 47 +
src/docs/assets/vendor/pygments-manni.css | 66 +
src/docs/index.html | 107 ++
src/less/bootstrap-tour-standalone.less | 15 +
src/less/bootstrap-tour.less | 60 +
test/bootstrap-tour.js | 802 ++++++++++++
48 files changed, 8441 insertions(+)
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..fdbdf0d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 2
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cba3758
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+node_modules
+bower_components
+bootstrap-tour.sublime-project
+bootstrap-tour.sublime-workspace
+npm-debug.log
+test
+docs
+_SpecRunner.html
+*.DS_Store
+smart.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c3b821f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,14 @@
+language: node_js
+node_js:
+ - "0.10"
+before_install:
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
+before_script:
+ - npm install -g gulp
+ - npm install -g bower
+ - bower install
+branches:
+ only:
+ - master
+ - develop
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..b876b5a
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+bootstraptour.com
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2bb9ad2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..88a9010
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# Bootstrap Tour
+[![Build Status](http://img.shields.io/travis/sorich87/bootstrap-tour.svg?style=flat)](https://travis-ci.org/sorich87/bootstrap-tour)
+[![Dependency Status](http://img.shields.io/david/sorich87/bootstrap-tour.svg?style=flat)](https://david-dm.org/sorich87/bootstrap-tour)
+[![devDependency Status](http://img.shields.io/david/dev/sorich87/bootstrap-tour/dev-status.svg?style=flat)](https://david-dm.org/sorich87/bootstrap-tour#info=devDependencies)
+[![NPM Version](http://img.shields.io/npm/v/bootstrap-tour.svg?style=flat)](https://www.npmjs.org/)
+
+Quick and easy way to build your product tours with Bootstrap Popovers.
+
+*Compatible with Bootstrap >= 2.3.0*
+
+## Demo and Documentation
+[http://bootstraptour.com](http://bootstraptour.com)
+
+## Contributing
+In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Gulp](http://gulpjs.com/).
+
+Feel free to contribute with pull requests, bug reports or enhancement suggestions.
+
+We use [Gulp](http://gulpjs.com/) and [Jasmine](http://pivotal.github.io/jasmine/). Both make your life easier ;)
+
+### Develop
+
+Files to be developed are located under `./src/`.
+Compiled sources are then automatically put under `./build/`, `./test/` and `./docs/`.
+
+#### Requirements
+
+To begin, you need a few standard dependencies installed. These commands will install ruby, gem, node, npm, and grunt's command line runner:
+
+##### Linux
+
+```bash
+$ sudo apt-get install ruby
+$ sudo apt-get install ruby-dev
+$ sudo apt-get install npm
+$ sudo apt-get install nodejs-legacy
+```
+
+##### Mac OS X
+
+```bash
+ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
+\curl -L https://get.rvm.io | bash
+rvm install ruby-2.1.1
+brew install node
+```
+
+##### Development requirements
+
+```bash
+$ npm install -g gulp
+$ npm install
+$ gem install jekyll
+```
+
+For Mac OS X Mavericks (10.9) users: You will need to [jump through all these hoops](http://dean.io/setting-up-a-ruby-on-rails-development-environment-on-mavericks/) before you can install Jekyll.
+
+#### Gulp usage
+
+Run gulp and start to develop with ease:
+
+```bash
+$ gulp
+$ gulp dist
+$ gulp test
+$ gulp docs
+$ gulp clean
+$ gulp server
+$ gulp bump --type minor (major.minor.patch)
+```
+
+Check `gulpfile.coffee` to know more.
+
+## License
+
+Code licensed under the [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0).
+Documentation licensed under [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/).
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..80dfdc7
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,5 @@
+markdown: rdiscount
+permalink: pretty
+source: ./src/docs
+destination: ./docs
+encoding: UTF-8
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..908256f
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "bootstrap-tour",
+ "version": "0.10.2",
+ "main": [
+ "./build/js/bootstrap-tour.js",
+ "./build/js/bootstrap-tour-standalone.js",
+ "./build/css/bootstrap-tour.css",
+ "./build/css/bootstrap-tour-standalone.css"
+ ],
+ "dependencies": {
+ "bootstrap": ">=2.3.2",
+ "jquery": ">=1.9.0"
+ },
+ "devDependencies": {
+ "html5shiv": "~3.7.2",
+ "jquery": "~2.1.1",
+ "bootstrap": "~3.2.0",
+ "blueimp-md5": "~1.1.0"
+ }
+}
diff --git a/build/css/bootstrap-tour-standalone.css b/build/css/bootstrap-tour-standalone.css
new file mode 100644
index 0000000..19ae087
--- /dev/null
+++ b/build/css/bootstrap-tour-standalone.css
@@ -0,0 +1,728 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+/*!
+ * Bootstrap v3.1.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+.btn {
+ display: inline-block;
+ margin-bottom: 0;
+ font-weight: normal;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ background-image: none;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus {
+ color: #333333;
+ text-decoration: none;
+}
+.btn:active,
+.btn.active {
+ outline: 0;
+ background-image: none;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn.disabled,
+.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;
+}
+.btn-default {
+ color: #333333;
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+.btn-default:hover,
+.btn-default:focus,
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+ color: #333333;
+ background-color: #e6e6e6;
+ border-color: #adadad;
+}
+.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 {
+ background-color: #ffffff;
+ border-color: #cccccc;
+}
+.btn-default .badge {
+ color: #ffffff;
+ background-color: #333333;
+}
+.btn-primary {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #357ebd;
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+ color: #ffffff;
+ background-color: #3071a9;
+ border-color: #285e8e;
+}
+.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 .badge {
+ color: #428bca;
+ background-color: #ffffff;
+}
+.btn-success {
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+ color: #ffffff;
+ background-color: #449d44;
+ border-color: #398439;
+}
+.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 {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-success .badge {
+ color: #5cb85c;
+ background-color: #ffffff;
+}
+.btn-info {
+ color: #ffffff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+ color: #ffffff;
+ background-color: #31b0d5;
+ border-color: #269abc;
+}
+.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 {
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-info .badge {
+ color: #5bc0de;
+ background-color: #ffffff;
+}
+.btn-warning {
+ color: #ffffff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+ color: #ffffff;
+ background-color: #ec971f;
+ border-color: #d58512;
+}
+.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 {
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-warning .badge {
+ color: #f0ad4e;
+ background-color: #ffffff;
+}
+.btn-danger {
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+ color: #ffffff;
+ background-color: #c9302c;
+ border-color: #ac2925;
+}
+.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 {
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger .badge {
+ color: #d9534f;
+ background-color: #ffffff;
+}
+.btn-link {
+ color: #428bca;
+ font-weight: normal;
+ cursor: pointer;
+ border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+ color: #2a6496;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+ color: #777777;
+ text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-block {
+ display: block;
+ width: 100%;
+}
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.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,
+.btn-group .btn-group + .btn-group {
+ margin-left: -1px;
+}
+.btn-toolbar {
+ margin-left: -5px;
+}
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+ float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+ margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+.btn-group > .btn:first-child {
+ margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group > .btn-group {
+ float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group > .btn-group:first-child > .btn:last-child,
+.btn-group > .btn-group:first-child > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group > .btn-group:last-child > .btn:first-child {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+ padding-left: 8px;
+ padding-right: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-left: 12px;
+ padding-right: 12px;
+}
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn .caret {
+ margin-left: 0;
+}
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+ float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+ margin-top: -1px;
+ margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+ border-top-right-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;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+ float: none;
+ display: table-cell;
+ width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+ width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+ left: auto;
+}
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+ position: absolute;
+ z-index: -1;
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ text-align: left;
+ background-color: #ffffff;
+ background-clip: padding-box;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ 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;
+}
+.popover.right {
+ margin-left: 10px;
+}
+.popover.bottom {
+ margin-top: 10px;
+}
+.popover.left {
+ margin-left: -10px;
+}
+.popover-title {
+ 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;
+}
+.popover-content {
+ padding: 9px 14px;
+}
+.popover > .arrow,
+.popover > .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.popover > .arrow {
+ border-width: 11px;
+}
+.popover > .arrow:after {
+ border-width: 10px;
+ content: "";
+}
+.popover.top > .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ bottom: -11px;
+}
+.popover.top > .arrow:after {
+ content: " ";
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #ffffff;
+}
+.popover.right > .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-left-width: 0;
+ border-right-color: #999999;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+.popover.right > .arrow:after {
+ content: " ";
+ left: 1px;
+ bottom: -10px;
+ border-left-width: 0;
+ border-right-color: #ffffff;
+}
+.popover.bottom > .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ top: -11px;
+}
+.popover.bottom > .arrow:after {
+ content: " ";
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #ffffff;
+}
+.popover.left > .arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999999;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+.popover.left > .arrow:after {
+ content: " ";
+ right: 1px;
+ border-right-width: 0;
+ border-left-color: #ffffff;
+ bottom: -10px;
+}
+.tour-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1100;
+ background-color: #000;
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+.tour-step-backdrop {
+ position: relative;
+ z-index: 1101;
+ background: inherit;
+}
+.tour-step-backdrop > td {
+ position: relative;
+ z-index: 1101;
+}
+.tour-step-background {
+ position: absolute !important;
+ z-index: 1100;
+ background: inherit;
+ border-radius: 6px;
+}
+.popover[class*="tour-"] {
+ z-index: 1100;
+}
+.popover[class*="tour-"] .popover-navigation {
+ padding: 9px 14px;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ float: right;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"],
+.popover[class*="tour-"] .popover-navigation *[data-role="next"],
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ cursor: pointer;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="next"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="end"].disabled {
+ cursor: default;
+}
+.popover[class*="tour-"].orphan {
+ position: fixed;
+ margin-top: 0;
+}
+.popover[class*="tour-"].orphan .arrow {
+ display: none;
+}
diff --git a/build/css/bootstrap-tour-standalone.min.css b/build/css/bootstrap-tour-standalone.min.css
new file mode 100644
index 0000000..e00c1cc
--- /dev/null
+++ b/build/css/bootstrap-tour-standalone.min.css
@@ -0,0 +1,26 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+/*!
+ * Bootstrap v3.1.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:foc [...]
\ No newline at end of file
diff --git a/build/css/bootstrap-tour.css b/build/css/bootstrap-tour.css
new file mode 100644
index 0000000..b730758
--- /dev/null
+++ b/build/css/bootstrap-tour.css
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+.tour-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1100;
+ background-color: #000;
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+.tour-step-backdrop {
+ position: relative;
+ z-index: 1101;
+ background: inherit;
+}
+.tour-step-backdrop > td {
+ position: relative;
+ z-index: 1101;
+}
+.tour-step-background {
+ position: absolute !important;
+ z-index: 1100;
+ background: inherit;
+ border-radius: 6px;
+}
+.popover[class*="tour-"] {
+ z-index: 1100;
+}
+.popover[class*="tour-"] .popover-navigation {
+ padding: 9px 14px;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ float: right;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"],
+.popover[class*="tour-"] .popover-navigation *[data-role="next"],
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ cursor: pointer;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="next"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="end"].disabled {
+ cursor: default;
+}
+.popover[class*="tour-"].orphan {
+ position: fixed;
+ margin-top: 0;
+}
+.popover[class*="tour-"].orphan .arrow {
+ display: none;
+}
diff --git a/build/css/bootstrap-tour.min.css b/build/css/bootstrap-tour.min.css
new file mode 100644
index 0000000..f98810c
--- /dev/null
+++ b/build/css/bootstrap-tour.min.css
@@ -0,0 +1,22 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+.tour-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1100;background-color:#000;opacity:.8;filter:alpha(opacity=80)}.tour-step-backdrop{position:relative;z-index:1101;background:inherit}.tour-step-backdrop>td{position:relative;z-index:1101}.tour-step-background{position:absolute!important;z-index:1100;background:inherit;border-radius:6px}.popover[class*=tour-]{z-index:1100}.popover[class*=tour-] .popover-navigation{padding:9px 14px}.popover[class*=tour-] .popover-navigatio [...]
\ No newline at end of file
diff --git a/build/js/bootstrap-tour-standalone.js b/build/js/bootstrap-tour-standalone.js
new file mode 100644
index 0000000..9d1fd55
--- /dev/null
+++ b/build/js/bootstrap-tour-standalone.js
@@ -0,0 +1,1374 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.2.0
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Tooltip = function (element, options) {
+ this.type =
+ this.options =
+ this.enabled =
+ this.timeout =
+ this.hoverState =
+ this.$element = null
+
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.VERSION = '3.2.0'
+
+ Tooltip.DEFAULTS = {
+ animation: true,
+ placement: 'top',
+ selector: false,
+ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ container: false,
+ viewport: {
+ selector: 'body',
+ padding: 0
+ }
+ }
+
+ Tooltip.prototype.init = function (type, element, options) {
+ this.enabled = true
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+
+ var triggers = this.options.trigger.split(' ')
+
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
+
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
+ }
+
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
+
+ return options
+ }
+
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'in'
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'out'
+
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+
+ var inDom = $.contains(document.documentElement, this.$element[0])
+ if (e.isDefaultPrevented() || !inDom) return
+ var that = this
+
+ var $tip = this.tip()
+
+ var tipId = this.getUID(this.type)
+
+ this.setContent()
+ $tip.attr('id', tipId)
+ this.$element.attr('aria-describedby', tipId)
+
+ if (this.options.animation) $tip.addClass('fade')
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+ .data('bs.' + this.type, this)
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (autoPlace) {
+ var orgPlacement = placement
+ var $parent = this.$element.parent()
+ var parentDim = this.getPosition($parent)
+
+ 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
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+ this.applyPlacement(calculatedOffset, placement)
+
+ var complete = function () {
+ that.$element.trigger('shown.bs.' + that.type)
+ that.hoverState = null
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(150) :
+ complete()
+ }
+ }
+
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
+ var $tip = this.tip()
+ var width = $tip[0].offsetWidth
+ var height = $tip[0].offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt($tip.css('margin-top'), 10)
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset($tip[0], $.extend({
+ using: function (props) {
+ $tip.css({
+ top: Math.round(props.top),
+ left: Math.round(props.left)
+ })
+ }
+ }, offset), 0)
+
+ $tip.addClass('in')
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ }
+
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+ 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'
+
+ $tip.offset(offset)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
+ }
+
+ Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
+ this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
+ }
+
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ Tooltip.prototype.hide = function () {
+ var that = this
+ 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)
+ }
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(150) :
+ complete()
+
+ this.hoverState = null
+
+ return this
+ }
+
+ Tooltip.prototype.fixTitle = function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
+
+ 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())
+ }
+
+ 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 } :
+ 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 }
+
+ }
+
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+ if (!this.$viewport) return delta
+
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+ var viewportDimensions = this.getPosition(this.$viewport)
+
+ if (/right|left/.test(placement)) {
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
+ } else {
+ var leftEdgeOffset = pos.left - viewportPadding
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
+ }
+
+ return delta
+ }
+
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ Tooltip.prototype.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+ }
+
+ Tooltip.prototype.tip = function () {
+ return (this.$tip = this.$tip || $(this.options.template))
+ }
+
+ 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
+ }
+
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
+
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
+ }
+
+ Tooltip.prototype.toggle = function (e) {
+ var self = this
+ if (e) {
+ self = $(e.currentTarget).data('bs.' + this.type)
+ if (!self) {
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
+ $(e.currentTarget).data('bs.' + this.type, self)
+ }
+ }
+
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+
+ Tooltip.prototype.destroy = function () {
+ clearTimeout(this.timeout)
+ this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+ }
+
+
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = Plugin
+ $.fn.tooltip.Constructor = Tooltip
+
+
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.2.0
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+ Popover.VERSION = '3.2.0'
+
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+ })
+
+
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+ Popover.prototype.constructor = Popover
+
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
+
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ 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
+ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
+
+ $tip.removeClass('fade top bottom left right in')
+
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+ // this manually by checking the contents.
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+ }
+
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
+
+ Popover.prototype.arrow = function () {
+ 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
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.popover
+
+ $.fn.popover = Plugin
+ $.fn.popover.Constructor = Popover
+
+
+ // POPOVER NO CONFLICT
+ // ===================
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(jQuery);
+
+(function($, window) {
+ var Tour, document;
+ document = window.document;
+ Tour = (function() {
+ function Tour(options) {
+ var storage;
+ try {
+ storage = window.localStorage;
+ } catch (_error) {
+ storage = false;
+ }
+ this._options = $.extend({
+ name: 'tour',
+ steps: [],
+ container: 'body',
+ autoscroll: true,
+ keyboard: true,
+ storage: storage,
+ debug: false,
+ backdrop: false,
+ backdropPadding: 0,
+ redirect: true,
+ orphan: false,
+ duration: false,
+ delay: false,
+ basePath: '',
+ template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">« Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next »</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> [...]
+ afterSetState: function(key, value) {},
+ afterGetState: function(key, value) {},
+ afterRemoveState: function(key) {},
+ onStart: function(tour) {},
+ onEnd: function(tour) {},
+ onShow: function(tour) {},
+ onShown: function(tour) {},
+ onHide: function(tour) {},
+ onHidden: function(tour) {},
+ onNext: function(tour) {},
+ onPrev: function(tour) {},
+ onPause: function(tour, duration) {},
+ onResume: function(tour, duration) {}
+ }, options);
+ this._force = false;
+ this._inited = false;
+ this.backdrop = {
+ overlay: null,
+ $element: null,
+ $background: null,
+ backgroundShown: false,
+ overlayElementShown: false
+ };
+ this;
+ }
+
+ Tour.prototype.addSteps = function(steps) {
+ var step, _i, _len;
+ for (_i = 0, _len = steps.length; _i < _len; _i++) {
+ step = steps[_i];
+ this.addStep(step);
+ }
+ return this;
+ };
+
+ Tour.prototype.addStep = function(step) {
+ this._options.steps.push(step);
+ return this;
+ };
+
+ Tour.prototype.getStep = function(i) {
+ if (this._options.steps[i] != null) {
+ return $.extend({
+ id: "step-" + i,
+ path: '',
+ placement: 'right',
+ title: '',
+ content: '<p></p>',
+ next: i === this._options.steps.length - 1 ? -1 : i + 1,
+ prev: i - 1,
+ animation: true,
+ container: this._options.container,
+ autoscroll: this._options.autoscroll,
+ backdrop: this._options.backdrop,
+ backdropPadding: this._options.backdropPadding,
+ redirect: this._options.redirect,
+ orphan: this._options.orphan,
+ duration: this._options.duration,
+ delay: this._options.delay,
+ template: this._options.template,
+ onShow: this._options.onShow,
+ onShown: this._options.onShown,
+ onHide: this._options.onHide,
+ onHidden: this._options.onHidden,
+ onNext: this._options.onNext,
+ onPrev: this._options.onPrev,
+ onPause: this._options.onPause,
+ onResume: this._options.onResume
+ }, this._options.steps[i]);
+ }
+ };
+
+ Tour.prototype.init = function(force) {
+ this._force = force;
+ if (this.ended()) {
+ this._debug('Tour ended, init prevented.');
+ return this;
+ }
+ this.setCurrentStep();
+ this._initMouseNavigation();
+ this._initKeyboardNavigation();
+ this._onResize((function(_this) {
+ return function() {
+ return _this.showStep(_this._current);
+ };
+ })(this));
+ if (this._current !== null) {
+ this.showStep(this._current);
+ }
+ this._inited = true;
+ return this;
+ };
+
+ Tour.prototype.start = function(force) {
+ var promise;
+ if (force == null) {
+ force = false;
+ }
+ if (!this._inited) {
+ this.init(force);
+ }
+ if (this._current === null) {
+ promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
+ this._callOnPromiseDone(promise, this.showStep, 0);
+ }
+ return this;
+ };
+
+ Tour.prototype.next = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showNextStep);
+ };
+
+ Tour.prototype.prev = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showPrevStep);
+ };
+
+ Tour.prototype.goTo = function(i) {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this.showStep, i);
+ };
+
+ Tour.prototype.end = function() {
+ var endHelper, promise;
+ endHelper = (function(_this) {
+ return function(e) {
+ $(document).off("click.tour-" + _this._options.name);
+ $(document).off("keyup.tour-" + _this._options.name);
+ $(window).off("resize.tour-" + _this._options.name);
+ _this._setState('end', 'yes');
+ _this._inited = false;
+ _this._force = false;
+ _this._clearTimer();
+ if (_this._options.onEnd != null) {
+ return _this._options.onEnd(_this);
+ }
+ };
+ })(this);
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, endHelper);
+ };
+
+ Tour.prototype.ended = function() {
+ return !this._force && !!this._getState('end');
+ };
+
+ Tour.prototype.restart = function() {
+ this._removeState('current_step');
+ this._removeState('end');
+ return this.start();
+ };
+
+ Tour.prototype.pause = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = true;
+ this._duration -= new Date().getTime() - this._start;
+ window.clearTimeout(this._timer);
+ this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
+ if (step.onPause != null) {
+ return step.onPause(this, this._duration);
+ }
+ };
+
+ Tour.prototype.resume = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = false;
+ this._start = new Date().getTime();
+ this._duration = this._duration || step.duration;
+ this._timer = window.setTimeout((function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this), this._duration);
+ this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
+ if ((step.onResume != null) && this._duration !== step.duration) {
+ return step.onResume(this, this._duration);
+ }
+ };
+
+ Tour.prototype.hideStep = function(i) {
+ var hideStepHelper, promise, step;
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ this._clearTimer();
+ promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
+ hideStepHelper = (function(_this) {
+ return function(e) {
+ var $element;
+ $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");
+ if (step.reflex) {
+ $element.removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
+ }
+ if (step.backdrop) {
+ _this._hideBackdrop();
+ }
+ if (step.onHidden != null) {
+ return step.onHidden(_this);
+ }
+ };
+ })(this);
+ this._callOnPromiseDone(promise, hideStepHelper);
+ return promise;
+ };
+
+ Tour.prototype.showStep = function(i) {
+ var promise, showStepHelper, skipToPrevious, step;
+ if (this.ended()) {
+ this._debug('Tour ended, showStep prevented.');
+ return this;
+ }
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ skipToPrevious = i < this._current;
+ promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ showStepHelper = (function(_this) {
+ return function(e) {
+ var current_path, 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);
+ current_path = [document.location.pathname, document.location.hash].join('');
+ if (_this._isRedirect(path, current_path)) {
+ _this._redirect(step, path);
+ return;
+ }
+ if (_this._isOrphan(step)) {
+ if (!step.orphan) {
+ _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
+ if (skipToPrevious) {
+ _this._showPrevStep();
+ } else {
+ _this._showNextStep();
+ }
+ return;
+ }
+ _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
+ }
+ if (step.backdrop) {
+ _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
+ }
+ showPopoverAndOverlay = function() {
+ if (_this.getCurrentStep() !== i) {
+ return;
+ }
+ if ((step.element != null) && step.backdrop) {
+ _this._showOverlayElement(step);
+ }
+ _this._showPopover(step, i);
+ if (step.onShown != null) {
+ step.onShown(_this);
+ }
+ return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
+ };
+ if (step.autoscroll) {
+ _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ } else {
+ showPopoverAndOverlay();
+ }
+ if (step.duration) {
+ return _this.resume();
+ }
+ };
+ })(this);
+ if (step.delay) {
+ this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, showStepHelper);
+ };
+ })(this), step.delay);
+ } else {
+ this._callOnPromiseDone(promise, showStepHelper);
+ }
+ return promise;
+ };
+
+ Tour.prototype.getCurrentStep = function() {
+ return this._current;
+ };
+
+ Tour.prototype.setCurrentStep = function(value) {
+ if (value != null) {
+ this._current = value;
+ this._setState('current_step', value);
+ } else {
+ this._current = this._getState('current_step');
+ this._current = this._current === null ? null : parseInt(this._current, 10);
+ }
+ return this;
+ };
+
+ Tour.prototype._setState = function(key, value) {
+ var e, keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ try {
+ this._options.storage.setItem(keyName, value);
+ } catch (_error) {
+ e = _error;
+ if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+ this._debug('LocalStorage quota exceeded. State storage failed.');
+ }
+ }
+ return this._options.afterSetState(keyName, value);
+ } else {
+ if (this._state == null) {
+ this._state = {};
+ }
+ return this._state[key] = value;
+ }
+ };
+
+ Tour.prototype._removeState = function(key) {
+ var keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ this._options.storage.removeItem(keyName);
+ return this._options.afterRemoveState(keyName);
+ } else {
+ if (this._state != null) {
+ return delete this._state[key];
+ }
+ }
+ };
+
+ Tour.prototype._getState = function(key) {
+ var keyName, value;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ value = this._options.storage.getItem(keyName);
+ } else {
+ if (this._state != null) {
+ value = this._state[key];
+ }
+ }
+ if (value === void 0 || value === 'null') {
+ value = null;
+ }
+ this._options.afterGetState(key, value);
+ return value;
+ };
+
+ Tour.prototype._showNextStep = function() {
+ var promise, showNextStepHelper, step;
+ step = this.getStep(this._current);
+ showNextStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.next);
+ };
+ })(this);
+ promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0);
+ return this._callOnPromiseDone(promise, showNextStepHelper);
+ };
+
+ Tour.prototype._showPrevStep = function() {
+ var promise, showPrevStepHelper, step;
+ step = this.getStep(this._current);
+ showPrevStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.prev);
+ };
+ })(this);
+ promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0);
+ return this._callOnPromiseDone(promise, showPrevStepHelper);
+ };
+
+ Tour.prototype._debug = function(text) {
+ if (this._options.debug) {
+ return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
+ }
+ };
+
+ Tour.prototype._isRedirect = function(path, currentPath) {
+ return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && path.replace(/\?.*$/, '').replace(/\/?$/, '') !== currentPath.replace(/\/?$/, '')));
+ };
+
+ Tour.prototype._redirect = function(step, path) {
+ if ($.isFunction(step.redirect)) {
+ return step.redirect.call(this, path);
+ } else if (step.redirect === true) {
+ this._debug("Redirect to " + path);
+ return document.location.href = path;
+ }
+ };
+
+ Tour.prototype._isOrphan = function(step) {
+ return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');
+ };
+
+ Tour.prototype._isLast = function() {
+ return this._current < this._options.steps.length - 1;
+ };
+
+ Tour.prototype._showPopover = function(step, i) {
+ var $element, $tip, isOrphan, options;
+ $(".tour-" + this._options.name).remove();
+ options = $.extend({}, this._options);
+ isOrphan = this._isOrphan(step);
+ step.template = this._template(step, i);
+ if (isOrphan) {
+ step.element = 'body';
+ step.placement = 'top';
+ }
+ $element = $(step.element);
+ $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");
+ if (step.options) {
+ $.extend(options, step.options);
+ }
+ if (step.reflex && !isOrphan) {
+ $element.addClass('tour-step-element-reflex');
+ $element.off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name);
+ $element.on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this));
+ }
+ $element.popover({
+ placement: step.placement,
+ trigger: 'manual',
+ title: step.title,
+ content: step.content,
+ html: true,
+ animation: step.animation,
+ container: step.container,
+ template: step.template,
+ selector: step.element
+ }).popover('show');
+ $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
+ $tip.attr('id', step.id);
+ this._reposition($tip, step);
+ if (isOrphan) {
+ return this._center($tip);
+ }
+ };
+
+ Tour.prototype._template = function(step, i) {
+ var $navigation, $next, $prev, $resume, $template;
+ $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
+ $navigation = $template.find('.popover-navigation');
+ $prev = $navigation.find('[data-role="prev"]');
+ $next = $navigation.find('[data-role="next"]');
+ $resume = $navigation.find('[data-role="pause-resume"]');
+ if (this._isOrphan(step)) {
+ $template.addClass('orphan');
+ }
+ $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
+ if (step.prev < 0) {
+ $prev.addClass('disabled');
+ }
+ if (step.next < 0) {
+ $next.addClass('disabled');
+ }
+ if (!step.duration) {
+ $resume.remove();
+ }
+ return $template.clone().wrap('<div>').parent().html();
+ };
+
+ Tour.prototype._reflexEvent = function(reflex) {
+ if ({}.toString.call(reflex) === '[object Boolean]') {
+ return 'click';
+ } else {
+ return reflex;
+ }
+ };
+
+ Tour.prototype._reposition = function($tip, step) {
+ var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
+ offsetWidth = $tip[0].offsetWidth;
+ offsetHeight = $tip[0].offsetHeight;
+ tipOffset = $tip.offset();
+ originalLeft = tipOffset.left;
+ originalTop = tipOffset.top;
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
+ if (offsetBottom < 0) {
+ tipOffset.top = tipOffset.top + offsetBottom;
+ }
+ offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
+ if (offsetRight < 0) {
+ tipOffset.left = tipOffset.left + offsetRight;
+ }
+ if (tipOffset.top < 0) {
+ tipOffset.top = 0;
+ }
+ if (tipOffset.left < 0) {
+ tipOffset.left = 0;
+ }
+ $tip.offset(tipOffset);
+ if (step.placement === 'bottom' || step.placement === 'top') {
+ if (originalLeft !== tipOffset.left) {
+ return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
+ }
+ } else {
+ if (originalTop !== tipOffset.top) {
+ return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
+ }
+ }
+ };
+
+ Tour.prototype._center = function($tip) {
+ return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
+ };
+
+ Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
+ 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);
+ if (!$element.length) {
+ return callback();
+ }
+ $window = $(window);
+ offsetTop = $element.offset().top;
+ windowHeight = $window.height();
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
+ counter = 0;
+ return $('body, html').stop(true, true).animate({
+ scrollTop: Math.ceil(scrollTop)
+ }, (function(_this) {
+ return function() {
+ if (++counter === 2) {
+ callback();
+ return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._onResize = function(callback, timeout) {
+ return $(window).on("resize.tour-" + this._options.name, function() {
+ clearTimeout(timeout);
+ return timeout = setTimeout(callback, 100);
+ });
+ };
+
+ Tour.prototype._initMouseNavigation = function() {
+ var _this;
+ _this = this;
+ return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".pop [...]
+ return function(e) {
+ e.preventDefault();
+ return _this.next();
+ };
+ })(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();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ return _this.end();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) {
+ var $this;
+ e.preventDefault();
+ $this = $(this);
+ $this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
+ if (_this._paused) {
+ return _this.resume();
+ } else {
+ return _this.pause();
+ }
+ });
+ };
+
+ Tour.prototype._initKeyboardNavigation = function() {
+ if (!this._options.keyboard) {
+ return;
+ }
+ return $(document).on("keyup.tour-" + this._options.name, (function(_this) {
+ return function(e) {
+ if (!e.which) {
+ return;
+ }
+ switch (e.which) {
+ case 39:
+ e.preventDefault();
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ break;
+ case 37:
+ e.preventDefault();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
+ break;
+ case 27:
+ e.preventDefault();
+ return _this.end();
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._makePromise = function(result) {
+ if (result && $.isFunction(result.then)) {
+ return result;
+ } else {
+ return null;
+ }
+ };
+
+ Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
+ if (promise) {
+ return promise.then((function(_this) {
+ return function(e) {
+ return cb.call(_this, arg);
+ };
+ })(this));
+ } else {
+ return cb.call(this, arg);
+ }
+ };
+
+ Tour.prototype._showBackdrop = function(element) {
+ if (this.backdrop.backgroundShown) {
+ return;
+ }
+ this.backdrop = $('<div>', {
+ "class": 'tour-backdrop'
+ });
+ this.backdrop.backgroundShown = true;
+ return $('body').append(this.backdrop);
+ };
+
+ Tour.prototype._hideBackdrop = function() {
+ this._hideOverlayElement();
+ return this._hideBackground();
+ };
+
+ Tour.prototype._hideBackground = function() {
+ if (this.backdrop) {
+ this.backdrop.remove();
+ this.backdrop.overlay = null;
+ return this.backdrop.backgroundShown = false;
+ }
+ };
+
+ Tour.prototype._showOverlayElement = function(step) {
+ var $element, elementData;
+ $element = $(step.element);
+ if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.overlayElementShown = true;
+ this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$background = $('<div>', {
+ "class": 'tour-step-background'
+ });
+ elementData = {
+ width: $element.innerWidth(),
+ height: $element.innerHeight(),
+ offset: $element.offset()
+ };
+ this.backdrop.$background.appendTo('body');
+ if (step.backdropPadding) {
+ elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
+ }
+ return this.backdrop.$background.width(elementData.width).height(elementData.height).offset(elementData.offset);
+ };
+
+ Tour.prototype._hideOverlayElement = function() {
+ if (!this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.$element.removeClass('tour-step-backdrop');
+ this.backdrop.$background.remove();
+ this.backdrop.$element = null;
+ this.backdrop.$background = null;
+ return this.backdrop.overlayElementShown = false;
+ };
+
+ Tour.prototype._applyBackdropPadding = function(padding, data) {
+ if (typeof padding === 'object') {
+ if (padding.top == null) {
+ padding.top = 0;
+ }
+ if (padding.right == null) {
+ padding.right = 0;
+ }
+ if (padding.bottom == null) {
+ padding.bottom = 0;
+ }
+ if (padding.left == null) {
+ padding.left = 0;
+ }
+ data.offset.top = data.offset.top - padding.top;
+ data.offset.left = data.offset.left - padding.left;
+ data.width = data.width + padding.left + padding.right;
+ data.height = data.height + padding.top + padding.bottom;
+ } else {
+ data.offset.top = data.offset.top - padding;
+ data.offset.left = data.offset.left - padding;
+ data.width = data.width + (padding * 2);
+ data.height = data.height + (padding * 2);
+ }
+ return data;
+ };
+
+ Tour.prototype._clearTimer = function() {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ return this._duration = null;
+ };
+
+ return Tour;
+
+ })();
+ return window.Tour = Tour;
+})(jQuery, window);
diff --git a/build/js/bootstrap-tour.js b/build/js/bootstrap-tour.js
new file mode 100644
index 0000000..1874061
--- /dev/null
+++ b/build/js/bootstrap-tour.js
@@ -0,0 +1,802 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+(function($, window) {
+ var Tour, document;
+ document = window.document;
+ Tour = (function() {
+ function Tour(options) {
+ var storage;
+ try {
+ storage = window.localStorage;
+ } catch (_error) {
+ storage = false;
+ }
+ this._options = $.extend({
+ name: 'tour',
+ steps: [],
+ container: 'body',
+ autoscroll: true,
+ keyboard: true,
+ storage: storage,
+ debug: false,
+ backdrop: false,
+ backdropPadding: 0,
+ redirect: true,
+ orphan: false,
+ duration: false,
+ delay: false,
+ basePath: '',
+ template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">« Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next »</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> [...]
+ afterSetState: function(key, value) {},
+ afterGetState: function(key, value) {},
+ afterRemoveState: function(key) {},
+ onStart: function(tour) {},
+ onEnd: function(tour) {},
+ onShow: function(tour) {},
+ onShown: function(tour) {},
+ onHide: function(tour) {},
+ onHidden: function(tour) {},
+ onNext: function(tour) {},
+ onPrev: function(tour) {},
+ onPause: function(tour, duration) {},
+ onResume: function(tour, duration) {}
+ }, options);
+ this._force = false;
+ this._inited = false;
+ this.backdrop = {
+ overlay: null,
+ $element: null,
+ $background: null,
+ backgroundShown: false,
+ overlayElementShown: false
+ };
+ this;
+ }
+
+ Tour.prototype.addSteps = function(steps) {
+ var step, _i, _len;
+ for (_i = 0, _len = steps.length; _i < _len; _i++) {
+ step = steps[_i];
+ this.addStep(step);
+ }
+ return this;
+ };
+
+ Tour.prototype.addStep = function(step) {
+ this._options.steps.push(step);
+ return this;
+ };
+
+ Tour.prototype.getStep = function(i) {
+ if (this._options.steps[i] != null) {
+ return $.extend({
+ id: "step-" + i,
+ path: '',
+ placement: 'right',
+ title: '',
+ content: '<p></p>',
+ next: i === this._options.steps.length - 1 ? -1 : i + 1,
+ prev: i - 1,
+ animation: true,
+ container: this._options.container,
+ autoscroll: this._options.autoscroll,
+ backdrop: this._options.backdrop,
+ backdropPadding: this._options.backdropPadding,
+ redirect: this._options.redirect,
+ orphan: this._options.orphan,
+ duration: this._options.duration,
+ delay: this._options.delay,
+ template: this._options.template,
+ onShow: this._options.onShow,
+ onShown: this._options.onShown,
+ onHide: this._options.onHide,
+ onHidden: this._options.onHidden,
+ onNext: this._options.onNext,
+ onPrev: this._options.onPrev,
+ onPause: this._options.onPause,
+ onResume: this._options.onResume
+ }, this._options.steps[i]);
+ }
+ };
+
+ Tour.prototype.init = function(force) {
+ this._force = force;
+ if (this.ended()) {
+ this._debug('Tour ended, init prevented.');
+ return this;
+ }
+ this.setCurrentStep();
+ this._initMouseNavigation();
+ this._initKeyboardNavigation();
+ this._onResize((function(_this) {
+ return function() {
+ return _this.showStep(_this._current);
+ };
+ })(this));
+ if (this._current !== null) {
+ this.showStep(this._current);
+ }
+ this._inited = true;
+ return this;
+ };
+
+ Tour.prototype.start = function(force) {
+ var promise;
+ if (force == null) {
+ force = false;
+ }
+ if (!this._inited) {
+ this.init(force);
+ }
+ if (this._current === null) {
+ promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
+ this._callOnPromiseDone(promise, this.showStep, 0);
+ }
+ return this;
+ };
+
+ Tour.prototype.next = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showNextStep);
+ };
+
+ Tour.prototype.prev = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showPrevStep);
+ };
+
+ Tour.prototype.goTo = function(i) {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this.showStep, i);
+ };
+
+ Tour.prototype.end = function() {
+ var endHelper, promise;
+ endHelper = (function(_this) {
+ return function(e) {
+ $(document).off("click.tour-" + _this._options.name);
+ $(document).off("keyup.tour-" + _this._options.name);
+ $(window).off("resize.tour-" + _this._options.name);
+ _this._setState('end', 'yes');
+ _this._inited = false;
+ _this._force = false;
+ _this._clearTimer();
+ if (_this._options.onEnd != null) {
+ return _this._options.onEnd(_this);
+ }
+ };
+ })(this);
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, endHelper);
+ };
+
+ Tour.prototype.ended = function() {
+ return !this._force && !!this._getState('end');
+ };
+
+ Tour.prototype.restart = function() {
+ this._removeState('current_step');
+ this._removeState('end');
+ return this.start();
+ };
+
+ Tour.prototype.pause = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = true;
+ this._duration -= new Date().getTime() - this._start;
+ window.clearTimeout(this._timer);
+ this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
+ if (step.onPause != null) {
+ return step.onPause(this, this._duration);
+ }
+ };
+
+ Tour.prototype.resume = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = false;
+ this._start = new Date().getTime();
+ this._duration = this._duration || step.duration;
+ this._timer = window.setTimeout((function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this), this._duration);
+ this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
+ if ((step.onResume != null) && this._duration !== step.duration) {
+ return step.onResume(this, this._duration);
+ }
+ };
+
+ Tour.prototype.hideStep = function(i) {
+ var hideStepHelper, promise, step;
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ this._clearTimer();
+ promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
+ hideStepHelper = (function(_this) {
+ return function(e) {
+ var $element;
+ $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");
+ if (step.reflex) {
+ $element.removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
+ }
+ if (step.backdrop) {
+ _this._hideBackdrop();
+ }
+ if (step.onHidden != null) {
+ return step.onHidden(_this);
+ }
+ };
+ })(this);
+ this._callOnPromiseDone(promise, hideStepHelper);
+ return promise;
+ };
+
+ Tour.prototype.showStep = function(i) {
+ var promise, showStepHelper, skipToPrevious, step;
+ if (this.ended()) {
+ this._debug('Tour ended, showStep prevented.');
+ return this;
+ }
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ skipToPrevious = i < this._current;
+ promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ showStepHelper = (function(_this) {
+ return function(e) {
+ var current_path, 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);
+ current_path = [document.location.pathname, document.location.hash].join('');
+ if (_this._isRedirect(path, current_path)) {
+ _this._redirect(step, path);
+ return;
+ }
+ if (_this._isOrphan(step)) {
+ if (!step.orphan) {
+ _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
+ if (skipToPrevious) {
+ _this._showPrevStep();
+ } else {
+ _this._showNextStep();
+ }
+ return;
+ }
+ _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
+ }
+ if (step.backdrop) {
+ _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
+ }
+ showPopoverAndOverlay = function() {
+ if (_this.getCurrentStep() !== i) {
+ return;
+ }
+ if ((step.element != null) && step.backdrop) {
+ _this._showOverlayElement(step);
+ }
+ _this._showPopover(step, i);
+ if (step.onShown != null) {
+ step.onShown(_this);
+ }
+ return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
+ };
+ if (step.autoscroll) {
+ _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ } else {
+ showPopoverAndOverlay();
+ }
+ if (step.duration) {
+ return _this.resume();
+ }
+ };
+ })(this);
+ if (step.delay) {
+ this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, showStepHelper);
+ };
+ })(this), step.delay);
+ } else {
+ this._callOnPromiseDone(promise, showStepHelper);
+ }
+ return promise;
+ };
+
+ Tour.prototype.getCurrentStep = function() {
+ return this._current;
+ };
+
+ Tour.prototype.setCurrentStep = function(value) {
+ if (value != null) {
+ this._current = value;
+ this._setState('current_step', value);
+ } else {
+ this._current = this._getState('current_step');
+ this._current = this._current === null ? null : parseInt(this._current, 10);
+ }
+ return this;
+ };
+
+ Tour.prototype._setState = function(key, value) {
+ var e, keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ try {
+ this._options.storage.setItem(keyName, value);
+ } catch (_error) {
+ e = _error;
+ if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+ this._debug('LocalStorage quota exceeded. State storage failed.');
+ }
+ }
+ return this._options.afterSetState(keyName, value);
+ } else {
+ if (this._state == null) {
+ this._state = {};
+ }
+ return this._state[key] = value;
+ }
+ };
+
+ Tour.prototype._removeState = function(key) {
+ var keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ this._options.storage.removeItem(keyName);
+ return this._options.afterRemoveState(keyName);
+ } else {
+ if (this._state != null) {
+ return delete this._state[key];
+ }
+ }
+ };
+
+ Tour.prototype._getState = function(key) {
+ var keyName, value;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ value = this._options.storage.getItem(keyName);
+ } else {
+ if (this._state != null) {
+ value = this._state[key];
+ }
+ }
+ if (value === void 0 || value === 'null') {
+ value = null;
+ }
+ this._options.afterGetState(key, value);
+ return value;
+ };
+
+ Tour.prototype._showNextStep = function() {
+ var promise, showNextStepHelper, step;
+ step = this.getStep(this._current);
+ showNextStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.next);
+ };
+ })(this);
+ promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0);
+ return this._callOnPromiseDone(promise, showNextStepHelper);
+ };
+
+ Tour.prototype._showPrevStep = function() {
+ var promise, showPrevStepHelper, step;
+ step = this.getStep(this._current);
+ showPrevStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.prev);
+ };
+ })(this);
+ promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0);
+ return this._callOnPromiseDone(promise, showPrevStepHelper);
+ };
+
+ Tour.prototype._debug = function(text) {
+ if (this._options.debug) {
+ return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
+ }
+ };
+
+ Tour.prototype._isRedirect = function(path, currentPath) {
+ return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && path.replace(/\?.*$/, '').replace(/\/?$/, '') !== currentPath.replace(/\/?$/, '')));
+ };
+
+ Tour.prototype._redirect = function(step, path) {
+ if ($.isFunction(step.redirect)) {
+ return step.redirect.call(this, path);
+ } else if (step.redirect === true) {
+ this._debug("Redirect to " + path);
+ return document.location.href = path;
+ }
+ };
+
+ Tour.prototype._isOrphan = function(step) {
+ return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');
+ };
+
+ Tour.prototype._isLast = function() {
+ return this._current < this._options.steps.length - 1;
+ };
+
+ Tour.prototype._showPopover = function(step, i) {
+ var $element, $tip, isOrphan, options;
+ $(".tour-" + this._options.name).remove();
+ options = $.extend({}, this._options);
+ isOrphan = this._isOrphan(step);
+ step.template = this._template(step, i);
+ if (isOrphan) {
+ step.element = 'body';
+ step.placement = 'top';
+ }
+ $element = $(step.element);
+ $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");
+ if (step.options) {
+ $.extend(options, step.options);
+ }
+ if (step.reflex && !isOrphan) {
+ $element.addClass('tour-step-element-reflex');
+ $element.off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name);
+ $element.on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this));
+ }
+ $element.popover({
+ placement: step.placement,
+ trigger: 'manual',
+ title: step.title,
+ content: step.content,
+ html: true,
+ animation: step.animation,
+ container: step.container,
+ template: step.template,
+ selector: step.element
+ }).popover('show');
+ $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
+ $tip.attr('id', step.id);
+ this._reposition($tip, step);
+ if (isOrphan) {
+ return this._center($tip);
+ }
+ };
+
+ Tour.prototype._template = function(step, i) {
+ var $navigation, $next, $prev, $resume, $template;
+ $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
+ $navigation = $template.find('.popover-navigation');
+ $prev = $navigation.find('[data-role="prev"]');
+ $next = $navigation.find('[data-role="next"]');
+ $resume = $navigation.find('[data-role="pause-resume"]');
+ if (this._isOrphan(step)) {
+ $template.addClass('orphan');
+ }
+ $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
+ if (step.prev < 0) {
+ $prev.addClass('disabled');
+ }
+ if (step.next < 0) {
+ $next.addClass('disabled');
+ }
+ if (!step.duration) {
+ $resume.remove();
+ }
+ return $template.clone().wrap('<div>').parent().html();
+ };
+
+ Tour.prototype._reflexEvent = function(reflex) {
+ if ({}.toString.call(reflex) === '[object Boolean]') {
+ return 'click';
+ } else {
+ return reflex;
+ }
+ };
+
+ Tour.prototype._reposition = function($tip, step) {
+ var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
+ offsetWidth = $tip[0].offsetWidth;
+ offsetHeight = $tip[0].offsetHeight;
+ tipOffset = $tip.offset();
+ originalLeft = tipOffset.left;
+ originalTop = tipOffset.top;
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
+ if (offsetBottom < 0) {
+ tipOffset.top = tipOffset.top + offsetBottom;
+ }
+ offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
+ if (offsetRight < 0) {
+ tipOffset.left = tipOffset.left + offsetRight;
+ }
+ if (tipOffset.top < 0) {
+ tipOffset.top = 0;
+ }
+ if (tipOffset.left < 0) {
+ tipOffset.left = 0;
+ }
+ $tip.offset(tipOffset);
+ if (step.placement === 'bottom' || step.placement === 'top') {
+ if (originalLeft !== tipOffset.left) {
+ return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
+ }
+ } else {
+ if (originalTop !== tipOffset.top) {
+ return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
+ }
+ }
+ };
+
+ Tour.prototype._center = function($tip) {
+ return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
+ };
+
+ Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
+ 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);
+ if (!$element.length) {
+ return callback();
+ }
+ $window = $(window);
+ offsetTop = $element.offset().top;
+ windowHeight = $window.height();
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
+ counter = 0;
+ return $('body, html').stop(true, true).animate({
+ scrollTop: Math.ceil(scrollTop)
+ }, (function(_this) {
+ return function() {
+ if (++counter === 2) {
+ callback();
+ return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._onResize = function(callback, timeout) {
+ return $(window).on("resize.tour-" + this._options.name, function() {
+ clearTimeout(timeout);
+ return timeout = setTimeout(callback, 100);
+ });
+ };
+
+ Tour.prototype._initMouseNavigation = function() {
+ var _this;
+ _this = this;
+ return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".pop [...]
+ return function(e) {
+ e.preventDefault();
+ return _this.next();
+ };
+ })(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();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ return _this.end();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) {
+ var $this;
+ e.preventDefault();
+ $this = $(this);
+ $this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
+ if (_this._paused) {
+ return _this.resume();
+ } else {
+ return _this.pause();
+ }
+ });
+ };
+
+ Tour.prototype._initKeyboardNavigation = function() {
+ if (!this._options.keyboard) {
+ return;
+ }
+ return $(document).on("keyup.tour-" + this._options.name, (function(_this) {
+ return function(e) {
+ if (!e.which) {
+ return;
+ }
+ switch (e.which) {
+ case 39:
+ e.preventDefault();
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ break;
+ case 37:
+ e.preventDefault();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
+ break;
+ case 27:
+ e.preventDefault();
+ return _this.end();
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._makePromise = function(result) {
+ if (result && $.isFunction(result.then)) {
+ return result;
+ } else {
+ return null;
+ }
+ };
+
+ Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
+ if (promise) {
+ return promise.then((function(_this) {
+ return function(e) {
+ return cb.call(_this, arg);
+ };
+ })(this));
+ } else {
+ return cb.call(this, arg);
+ }
+ };
+
+ Tour.prototype._showBackdrop = function(element) {
+ if (this.backdrop.backgroundShown) {
+ return;
+ }
+ this.backdrop = $('<div>', {
+ "class": 'tour-backdrop'
+ });
+ this.backdrop.backgroundShown = true;
+ return $('body').append(this.backdrop);
+ };
+
+ Tour.prototype._hideBackdrop = function() {
+ this._hideOverlayElement();
+ return this._hideBackground();
+ };
+
+ Tour.prototype._hideBackground = function() {
+ if (this.backdrop) {
+ this.backdrop.remove();
+ this.backdrop.overlay = null;
+ return this.backdrop.backgroundShown = false;
+ }
+ };
+
+ Tour.prototype._showOverlayElement = function(step) {
+ var $element, elementData;
+ $element = $(step.element);
+ if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.overlayElementShown = true;
+ this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$background = $('<div>', {
+ "class": 'tour-step-background'
+ });
+ elementData = {
+ width: $element.innerWidth(),
+ height: $element.innerHeight(),
+ offset: $element.offset()
+ };
+ this.backdrop.$background.appendTo('body');
+ if (step.backdropPadding) {
+ elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
+ }
+ return this.backdrop.$background.width(elementData.width).height(elementData.height).offset(elementData.offset);
+ };
+
+ Tour.prototype._hideOverlayElement = function() {
+ if (!this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.$element.removeClass('tour-step-backdrop');
+ this.backdrop.$background.remove();
+ this.backdrop.$element = null;
+ this.backdrop.$background = null;
+ return this.backdrop.overlayElementShown = false;
+ };
+
+ Tour.prototype._applyBackdropPadding = function(padding, data) {
+ if (typeof padding === 'object') {
+ if (padding.top == null) {
+ padding.top = 0;
+ }
+ if (padding.right == null) {
+ padding.right = 0;
+ }
+ if (padding.bottom == null) {
+ padding.bottom = 0;
+ }
+ if (padding.left == null) {
+ padding.left = 0;
+ }
+ data.offset.top = data.offset.top - padding.top;
+ data.offset.left = data.offset.left - padding.left;
+ data.width = data.width + padding.left + padding.right;
+ data.height = data.height + padding.top + padding.bottom;
+ } else {
+ data.offset.top = data.offset.top - padding;
+ data.offset.left = data.offset.left - padding;
+ data.width = data.width + (padding * 2);
+ data.height = data.height + (padding * 2);
+ }
+ return data;
+ };
+
+ Tour.prototype._clearTimer = function() {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ return this._duration = null;
+ };
+
+ return Tour;
+
+ })();
+ return window.Tour = Tour;
+})(jQuery, window);
diff --git a/coffeelint.json b/coffeelint.json
new file mode 100644
index 0000000..4187a7d
--- /dev/null
+++ b/coffeelint.json
@@ -0,0 +1,127 @@
+{
+ "coffeescript_error": {
+ "level": "error"
+ },
+ "arrow_spacing": {
+ "name": "arrow_spacing",
+ "level": "ignore"
+ },
+ "no_tabs": {
+ "name": "no_tabs",
+ "level": "error"
+ },
+ "no_trailing_whitespace": {
+ "name": "no_trailing_whitespace",
+ "level": "error",
+ "allowed_in_comments": false,
+ "allowed_in_empty_lines": false
+ },
+ "max_line_length": {
+ "name": "max_line_length",
+ "value": 120,
+ "level": "error",
+ "limitComments": true
+ },
+ "line_endings": {
+ "name": "line_endings",
+ "level": "warn",
+ "value": "unix"
+ },
+ "no_trailing_semicolons": {
+ "name": "no_trailing_semicolons",
+ "level": "error"
+ },
+ "indentation": {
+ "name": "indentation",
+ "value": 2,
+ "level": "error"
+ },
+ "camel_case_classes": {
+ "name": "camel_case_classes",
+ "level": "error"
+ },
+ "colon_assignment_spacing": {
+ "name": "colon_assignment_spacing",
+ "level": "ignore",
+ "spacing": {
+ "left": 0,
+ "right": 0
+ }
+ },
+ "no_implicit_braces": {
+ "name": "no_implicit_braces",
+ "level": "ignore",
+ "strict": true
+ },
+ "no_plusplus": {
+ "name": "no_plusplus",
+ "level": "ignore"
+ },
+ "no_throwing_strings": {
+ "name": "no_throwing_strings",
+ "level": "error"
+ },
+ "no_backticks": {
+ "name": "no_backticks",
+ "level": "error"
+ },
+ "no_implicit_parens": {
+ "name": "no_implicit_parens",
+ "strict": true,
+ "level": "ignore"
+ },
+ "no_empty_param_list": {
+ "name": "no_empty_param_list",
+ "level": "error"
+ },
+ "no_stand_alone_at": {
+ "name": "no_stand_alone_at",
+ "level": "ignore"
+ },
+ "space_operators": {
+ "name": "space_operators",
+ "level": "ignore"
+ },
+ "duplicate_key": {
+ "name": "duplicate_key",
+ "level": "error"
+ },
+ "empty_constructor_needs_parens": {
+ "name": "empty_constructor_needs_parens",
+ "level": "ignore"
+ },
+ "cyclomatic_complexity": {
+ "name": "cyclomatic_complexity",
+ "value": 10,
+ "level": "ignore"
+ },
+ "newlines_after_classes": {
+ "name": "newlines_after_classes",
+ "value": 3,
+ "level": "error"
+ },
+ "no_unnecessary_fat_arrows": {
+ "name": "no_unnecessary_fat_arrows",
+ "level": "error"
+ },
+ "missing_fat_arrows": {
+ "name": "missing_fat_arrows",
+ "level": "ignore"
+ },
+ "non_empty_constructor_needs_parens": {
+ "name": "non_empty_constructor_needs_parens",
+ "level": "ignore"
+ },
+ "no_unnecessary_double_quotes": {
+ "name": "no_unnecessary_double_quotes",
+ "level": "error"
+ },
+ "no_debugger": {
+ "name": "no_debugger",
+ "level": "warn"
+ },
+ "no_interpolation_in_single_quotes": {
+ "name": "no_interpolation_in_single_quotes",
+ "level": "ignore"
+ }
+}
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..bb3ba64
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,13 @@
+{
+ "name": "sorich87/bootstrap-tour",
+ "description": "Quick and easy way to build your product tours with Bootstrap Popovers.",
+ "type":"component",
+ "url":"https://github.com/sorich87/bootstrap-tour.git",
+ "homepage": "http://bootstraptour.com/",
+ "description": "Show people how to use your web site",
+ "keywords": [
+ "bootstrap",
+ "tour"
+ ],
+ "license": "Apache-2.0"
+}
diff --git a/gulpfile.coffee b/gulpfile.coffee
new file mode 100644
index 0000000..cc1665c
--- /dev/null
+++ b/gulpfile.coffee
@@ -0,0 +1,214 @@
+gulp = require 'gulp'
+$ = require('gulp-load-plugins') lazy: false
+extend = require('util')._extend
+streamqueue = require 'streamqueue'
+spawn = require('child_process').spawn
+karma = require('karma').server
+karmaConfig = require './karma.json'
+pkg = require './package.json'
+name = pkg.name
+
+paths =
+ src: './src'
+ dist: './build'
+ test: './test'
+ docs: './docs'
+server =
+ host: 'localhost'
+ port: 3000
+banner = '''
+ /* ========================================================================
+ * <%= pkg.name %> - v<%= pkg.version %>
+ * <%= pkg.homepage %>
+ * ========================================================================
+ * Copyright 2012-2013 <%= pkg.author.name %>
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+
+ '''
+
+# coffee
+gulp.task 'coffee', ->
+ gulp
+ .src "#{paths.src}/coffee/#{name}.coffee"
+ .pipe $.changed "#{paths.dist}/js"
+ .pipe $.coffeelint './coffeelint.json'
+ .pipe $.coffeelint.reporter()
+ .on 'error', $.util.log
+ .pipe $.coffee bare: true
+ .on 'error', $.util.log
+ .pipe $.header banner, pkg: pkg
+ .pipe gulp.dest "#{paths.dist}/js"
+ .pipe gulp.dest "#{paths.src}/docs/assets/js"
+ .pipe gulp.dest paths.test
+ .pipe $.uglify()
+ .pipe $.header banner, pkg: pkg
+ .pipe $.rename suffix: '.min'
+ .pipe gulp.dest "#{paths.dist}/js"
+
+gulp.task 'coffee-standalone', ->
+ streamqueue objectMode: true,
+ gulp
+ .src [
+ "./bower_components/bootstrap/js/tooltip.js"
+ "./bower_components/bootstrap/js/popover.js"
+ ]
+ ,
+ gulp
+ .src "#{paths.src}/coffee/#{name}.coffee"
+ .pipe $.changed "#{paths.dist}/js"
+ .pipe $.coffeelint './coffeelint.json'
+ .pipe $.coffeelint.reporter()
+ .on 'error', $.util.log
+ .pipe $.coffee bare: true
+ .on 'error', $.util.log
+ .pipe $.concat "#{name}-standalone.js"
+ .pipe $.header banner, pkg: pkg
+ .pipe gulp.dest "#{paths.dist}/js"
+ .pipe $.uglify()
+ .pipe $.header banner, pkg: pkg
+ .pipe $.rename suffix: '.min'
+ .pipe gulp.dest "#{paths.dist}/js"
+
+# less
+gulp.task 'less', ->
+ gulp
+ .src [
+ "#{paths.src}/less/#{name}.less"
+ ]
+ .pipe $.changed "#{paths.dist}/css"
+ .pipe $.less()
+ .on 'error', $.util.log
+ .pipe $.header banner, pkg: pkg
+ .pipe gulp.dest "#{paths.dist}/css"
+ .pipe gulp.dest "#{paths.src}/docs/assets/css"
+ .pipe $.less compress: true, cleancss: true
+ .pipe $.header banner, pkg: pkg
+ .pipe $.rename suffix: '.min'
+ .pipe gulp.dest "#{paths.dist}/css"
+
+gulp.task 'less-standalone', ->
+ gulp
+ .src "#{paths.src}/less/#{name}-standalone.less"
+ .pipe $.changed "#{paths.dist}/css"
+ .pipe $.less()
+ .on 'error', $.util.log
+ .pipe $.header banner, pkg: pkg
+ .pipe gulp.dest "#{paths.dist}/css"
+ .pipe $.less compress: true, cleancss: true
+ .pipe $.header banner, pkg: pkg
+ .pipe $.rename suffix: '.min'
+ .pipe gulp.dest "#{paths.dist}/css"
+
+# test
+gulp.task 'test-coffee', ['coffee'], ->
+ gulp
+ .src "#{paths.src}/coffee/#{name}.spec.coffee"
+ .pipe $.changed paths.test
+ .pipe $.coffeelint './coffeelint.json'
+ .pipe $.coffeelint.reporter()
+ .on 'error', $.util.log
+ .pipe $.coffee()
+ .on 'error', $.util.log
+ .pipe gulp.dest paths.test
+
+gulp.task 'test-go', ['test-coffee'], (done) ->
+ karma.start extend(karmaConfig, singleRun: true), done
+
+# docs
+gulp.task 'docs-build', ['coffee', 'less'], (done) ->
+ spawn 'jekyll', ['build']
+ .on 'close', done
+
+gulp.task 'docs-copy', ['docs-build'], ->
+ gulp
+ .src "./bower_components/**/*"
+ .pipe gulp.dest "#{paths.docs}/components"
+
+gulp.task 'docs-coffee', ['docs-build'], ->
+ gulp
+ .src "#{paths.src}/coffee/#{name}.docs.coffee"
+ .pipe $.changed "#{paths.docs}/assets/js"
+ .pipe $.coffeelint.reporter()
+ .on 'error', $.util.log
+ .pipe $.coffee()
+ .on 'error', $.util.log
+ .pipe gulp.dest "#{paths.docs}/assets/js"
+
+# clean
+gulp.task 'clean-dist', ->
+ gulp
+ .src paths.dist
+ .pipe $.clean()
+
+gulp.task 'clean-test', ->
+ gulp
+ .src paths.test
+ .pipe $.clean()
+
+gulp.task 'clean-docs', ->
+ gulp
+ .src paths.docs
+ .pipe $.clean()
+
+# connect
+gulp.task 'connect', ['docs'], ->
+ $.connect.server
+ root: [paths.docs]
+ host: server.host
+ port: server.port
+ livereload: true
+
+# open
+gulp.task 'open', ['connect'], ->
+ gulp
+ .src "#{paths.docs}/index.html"
+ .pipe $.open '', url: "http://#{server.host}:#{server.port}"
+
+gulp.task 'watch', ['connect'], ->
+ gulp.watch "#{paths.src}/coffee/#{name}.coffee", ['coffee', 'coffee-standalone']
+ gulp.watch "#{paths.src}/less/#{name}.less", ["less", "less-standalone"]
+ gulp.watch "#{paths.src}/less/#{name}-standalone.less", ['less-standalone']
+ gulp.watch "#{paths.src}/coffee/#{name}.spec.coffee", ['test']
+ gulp.watch [
+ "#{paths.src}/coffee/#{name}.docs.coffee"
+ "#{paths.src}/docs/**/*"
+ ], ['docs']
+ gulp.watch [
+ "#{paths.dist}/js/**/*.js"
+ "#{paths.dist}/css/**/*.css"
+ "#{paths.docs}/index.html"
+ ]
+ .on 'change', (event) ->
+ gulp.src event.path
+ .pipe $.connect.reload()
+
+# bump
+gulp.task 'bump', ['test'], ->
+ bumpType = $.util.env.type || 'patch'
+
+ return gulp.src(['./bower.json', './package.json', './smart.json'])
+ .pipe $.bump(type: bumpType)
+ .pipe gulp.dest('./')
+
+# tasks
+gulp.task 'clean', ['clean-dist', 'clean-test', 'clean-docs']
+gulp.task 'server', ['connect', 'open', 'watch']
+gulp.task 'dist', ['coffee', 'coffee-standalone', 'less', 'less-standalone']
+gulp.task 'test', ['coffee', 'test-coffee', 'test-go']
+gulp.task 'docs', ['coffee', 'less', 'docs-build', 'docs-copy', 'docs-coffee']
+gulp.task 'default', ['dist', 'docs', 'server']
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..c7f2b8b
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,2 @@
+require('coffee-script/register');
+require('./gulpfile.coffee');
diff --git a/karma.json b/karma.json
new file mode 100644
index 0000000..74fb1f2
--- /dev/null
+++ b/karma.json
@@ -0,0 +1,19 @@
+{
+ "frameworks": ["jasmine"],
+ "files": [
+ "bower_components/jquery/dist/jquery.js",
+ "bower_components/bootstrap/dist/js/bootstrap.js",
+ "test/bootstrap-tour.js",
+ "test/bootstrap-tour.spec.js"
+ ],
+ "reporters": ["progress"],
+ "port": 9876,
+ "colors": true,
+ "autoWatch": true,
+ "browsers": ["Firefox"],
+ "singleRun": false,
+ "plugins": [
+ "karma-jasmine",
+ "karma-firefox-launcher"
+ ]
+}
diff --git a/package.js b/package.js
new file mode 100644
index 0000000..de35ef2
--- /dev/null
+++ b/package.js
@@ -0,0 +1,3 @@
+Package.describe({
+ summary: "Quick and easy way to build your product tours with Bootstrap Popovers."
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fcdf9df
--- /dev/null
+++ b/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "bootstrap-tour",
+ "description": "Quick and easy way to build your product tours with Bootstrap Popovers.",
+ "version": "0.10.2",
+ "keywords": [
+ "tour",
+ "bootstrap",
+ "js",
+ "tour",
+ "intro"
+ ],
+ "homepage": "http://bootstraptour.com",
+ "author": {
+ "name": "Ulrich Sossou",
+ "email": "sorich87 at gmail.com",
+ "url": "http://ulrichsossou.com"
+ },
+ "contributors": [
+ {
+ "name": "Emanuele Marchi",
+ "email": "emanuele at lostcrew.it",
+ "url": "http://lostcrew.it"
+ },
+ {
+ "name": "Nicola Molinari",
+ "email": "emmenko at gmail.com"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sorich87/bootstrap-tour.git"
+ },
+ "bugs": {
+ "url": "https://github.com/sorich87/bootstrap-tour/issues"
+ },
+ "licenses": [
+ {
+ "type": "Apache-2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0"
+ }
+ ],
+ "dependencies": {
+ "bootstrap": "~3",
+ "jquery": ">=1.8"
+ },
+ "devDependencies": {
+ "coffee-script": "~1.7.1",
+ "gulp": "~3.8.6",
+ "gulp-bump": "^0.1.11",
+ "gulp-changed": "~0.4.1",
+ "gulp-clean": "~0.3.1",
+ "gulp-coffee": "~2.1.1",
+ "gulp-coffeelint": "~0.3.3",
+ "gulp-concat": "~2.3.4",
+ "gulp-connect": "~2.0.6",
+ "gulp-header": "~1.0.5",
+ "gulp-jasmine": "~0.3.0",
+ "gulp-karma": "0.0.4",
+ "gulp-less": "~1.3.2",
+ "gulp-load-plugins": "~0.5.3",
+ "gulp-open": "~0.2.8",
+ "gulp-rename": "~1.2.0",
+ "gulp-uglify": "~0.3.1",
+ "gulp-util": "~3.0.0",
+ "karma": "~0.12.19",
+ "karma-firefox-launcher": "~0.1.3",
+ "karma-jasmine": "~0.1.5",
+ "streamqueue": "0.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "main": [
+ "./build/js/bootstrap-tour.js",
+ "./build/js/bootstrap-tour-standalone.js",
+ "./build/css/bootstrap-tour.css",
+ "./build/css/bootstrap-tour-standalone.css"
+ ],
+ "scripts": {
+ "build": "gulp dist",
+ "test": "gulp test"
+ }
+}
diff --git a/smart.json b/smart.json
new file mode 100644
index 0000000..a3184aa
--- /dev/null
+++ b/smart.json
@@ -0,0 +1,9 @@
+{
+ "name": "bootstrap-tour",
+ "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.2",
+ "git": "https://github.com/sorich87/bootstrap-tour.git",
+ "packages": {}
+}
diff --git a/src/coffee/bootstrap-tour.coffee b/src/coffee/bootstrap-tour.coffee
new file mode 100644
index 0000000..43fa372
--- /dev/null
+++ b/src/coffee/bootstrap-tour.coffee
@@ -0,0 +1,655 @@
+(($, window) ->
+ document = window.document
+
+ class Tour
+ constructor: (options) ->
+ try
+ storage = window.localStorage
+ catch
+ # localStorage may be unavailable due to security settings
+ storage = false
+ @_options = $.extend
+ name: 'tour'
+ steps: []
+ container: 'body'
+ autoscroll: true
+ keyboard: true
+ storage: storage
+ debug: false
+ backdrop: false
+ backdropPadding: 0
+ redirect: true
+ orphan: false
+ duration: false
+ delay: false
+ basePath: ''
+ template: '<div class="popover" role="tooltip">
+ <div class="arrow"></div>
+ <h3 class="popover-title"></h3>
+ <div class="popover-content"></div>
+ <div class="popover-navigation">
+ <div class="btn-group">
+ <button class="btn btn-sm btn-default" data-role="prev">« Prev</button>
+ <button class="btn btn-sm btn-default" data-role="next">Next »</button>
+ <button class="btn btn-sm btn-default"
+ data-role="pause-resume"
+ data-pause-text="Pause"
+ data-resume-text="Resume">Pause</button>
+ </div>
+ <button class="btn btn-sm btn-default" data-role="end">End tour</button>
+ </div>
+ </div>'
+ afterSetState: (key, value) ->
+ afterGetState: (key, value) ->
+ afterRemoveState: (key) ->
+ onStart: (tour) ->
+ onEnd: (tour) ->
+ onShow: (tour) ->
+ onShown: (tour) ->
+ onHide: (tour) ->
+ onHidden: (tour) ->
+ onNext: (tour) ->
+ onPrev: (tour) ->
+ onPause: (tour, duration) ->
+ onResume: (tour, duration) ->
+ , options
+
+ @_force = false
+ @_inited = false
+ @backdrop =
+ overlay: null
+ $element: null
+ $background: null
+ backgroundShown: false
+ overlayElementShown: false
+ @
+
+ # Add multiple steps
+ addSteps: (steps) ->
+ @addStep step for step in steps
+ @
+
+ # Add a new step
+ addStep: (step) ->
+ @_options.steps.push step
+ @
+
+ # Get a step by its indice
+ getStep: (i) ->
+ if @_options.steps[i]?
+ $.extend
+ id: "step-#{i}"
+ path: ''
+ placement: 'right'
+ title: ''
+ content: '<p></p>' # no empty as default, otherwise popover won't show up
+ next: if i is @_options.steps.length - 1 then -1 else i + 1
+ prev: i - 1
+ animation: true
+ container: @_options.container
+ autoscroll: @_options.autoscroll
+ backdrop: @_options.backdrop
+ backdropPadding: @_options.backdropPadding
+ redirect: @_options.redirect
+ orphan: @_options.orphan
+ duration: @_options.duration
+ delay: @_options.delay
+ template: @_options.template
+ onShow: @_options.onShow
+ onShown: @_options.onShown
+ onHide: @_options.onHide
+ onHidden: @_options.onHidden
+ onNext: @_options.onNext
+ onPrev: @_options.onPrev
+ onPause: @_options.onPause
+ onResume: @_options.onResume
+ , @_options.steps[i]
+
+ # Setup event bindings and continue a tour that has already started
+ init: (force) ->
+ @_force = force
+
+ if @ended()
+ @_debug 'Tour ended, init prevented.'
+ return @
+
+ @setCurrentStep()
+
+ @_initMouseNavigation()
+ @_initKeyboardNavigation()
+
+ # Reshow popover on window resize using debounced resize
+ @_onResize => @showStep @_current
+
+ # Continue a tour that had started on a previous page load
+ @showStep @_current unless @_current is null
+
+ @_inited = true
+ @
+
+ # Start tour from current step
+ start: (force = false) ->
+ @init force unless @_inited # Backward compatibility
+
+ if @_current is null
+ promise = @_makePromise(@_options.onStart(@) if @_options.onStart?)
+ @_callOnPromiseDone(promise, @showStep, 0)
+ @
+
+ # Hide current step and show next step
+ next: ->
+ promise = @hideStep @_current
+ @_callOnPromiseDone promise, @_showNextStep
+
+ # Hide current step and show prev step
+ prev: ->
+ promise = @hideStep @_current
+ @_callOnPromiseDone promise, @_showPrevStep
+
+ goTo: (i) ->
+ promise = @hideStep @_current
+ @_callOnPromiseDone promise, @showStep, i
+
+ # End tour
+ end: ->
+ endHelper = (e) =>
+ $(document).off "click.tour-#{@_options.name}"
+ $(document).off "keyup.tour-#{@_options.name}"
+ $(window).off "resize.tour-#{@_options.name}"
+ @_setState('end', 'yes')
+ @_inited = false
+ @_force = false
+
+ @_clearTimer()
+
+ @_options.onEnd(@) if @_options.onEnd?
+
+ promise = @hideStep(@_current)
+ @_callOnPromiseDone(promise, endHelper)
+
+ # Verify if tour is enabled
+ ended: ->
+ not @_force and not not @_getState 'end'
+
+ # Restart tour
+ restart: ->
+ @_removeState 'current_step'
+ @_removeState 'end'
+ @start()
+
+ # Pause step timer
+ pause: ->
+ step = @getStep @_current
+ return @ unless step and step.duration
+
+ @_paused = true
+ @_duration -= new Date().getTime() - @_start
+ window.clearTimeout(@_timer)
+
+ @_debug "Paused/Stopped step #{@_current + 1} timer (#{@_duration} remaining)."
+
+ step.onPause @, @_duration if step.onPause?
+
+ # Resume step timer
+ resume: ->
+ step = @getStep @_current
+ return @ unless step and step.duration
+
+ @_paused = false
+ @_start = new Date().getTime()
+ @_duration = @_duration or step.duration
+ @_timer = window.setTimeout =>
+ if @_isLast() then @next() else @end()
+ , @_duration
+
+ @_debug "Started step #{@_current + 1} timer with duration #{@_duration}"
+
+ step.onResume @, @_duration if step.onResume? and @_duration isnt step.duration
+
+ # Hide the specified step
+ hideStep: (i) ->
+ step = @getStep i
+ return unless step
+
+ @_clearTimer()
+
+ # If onHide returns a promise, let's wait until it's done to execute
+ promise = @_makePromise(step.onHide @, i if step.onHide?)
+
+ hideStepHelper = (e) =>
+ $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"
+ if step.reflex
+ $element
+ .removeClass('tour-step-element-reflex')
+ .off "#{@_reflexEvent(step.reflex)}.tour-#{@_options.name}"
+
+ @_hideBackdrop() if step.backdrop
+
+ step.onHidden(@) if step.onHidden?
+
+ @_callOnPromiseDone promise, hideStepHelper
+ promise
+
+ # Show the specified step
+ showStep: (i) ->
+ if @ended()
+ @_debug 'Tour ended, showStep prevented.'
+ return @
+
+ step = @getStep i
+ return unless step
+
+ skipToPrevious = i < @_current
+
+ # 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
+
+ # 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
+ current_path = [document.location.pathname, document.location.hash].join('')
+ if @_isRedirect path, current_path
+ @_redirect step, path
+ return
+
+ # Skip if step is orphan and orphan options is false
+ if @_isOrphan step
+ if not step.orphan
+ @_debug """Skip the orphan step #{@_current + 1}.
+ Orphan option is false and the element does not exist or is hidden."""
+ if skipToPrevious then @_showPrevStep() else @_showNextStep()
+ return
+
+ @_debug "Show the orphan step #{@_current + 1}. Orphans option is true."
+
+ # Show backdrop
+ @_showBackdrop(step.element unless @_isOrphan step) if step.backdrop
+
+ showPopoverAndOverlay = =>
+ return if @getCurrentStep() isnt i
+
+ @_showOverlayElement step 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
+ else
+ showPopoverAndOverlay()
+
+ # Play step timer
+ @resume() if step.duration
+
+ if step.delay
+ @_debug "Wait #{step.delay} milliseconds to show the step #{@_current + 1}"
+ window.setTimeout =>
+ @_callOnPromiseDone promise, showStepHelper
+ , step.delay
+ else
+ @_callOnPromiseDone promise, showStepHelper
+
+ promise
+
+ getCurrentStep: ->
+ @_current
+
+ # Setup current step variable
+ setCurrentStep: (value) ->
+ if value?
+ @_current = value
+ @_setState 'current_step', value
+ else
+ @_current = @_getState 'current_step'
+ @_current = if @_current is null then null else parseInt @_current, 10
+ @
+
+ # Set a state in storage
+ _setState: (key, value) ->
+ if @_options.storage
+ keyName = "#{@_options.name}_#{key}"
+ try @_options.storage.setItem keyName, value
+ catch e
+ if e.code is DOMException.QUOTA_EXCEEDED_ERR
+ @_debug 'LocalStorage quota exceeded. State storage failed.'
+ @_options.afterSetState keyName, value
+ else
+ @_state ?= {}
+ @_state[key] = value
+
+ # Remove the current state from the storage layer
+ _removeState: (key) ->
+ if @_options.storage
+ keyName = "#{@_options.name}_#{key}"
+ @_options.storage.removeItem keyName
+ @_options.afterRemoveState keyName
+ else
+ delete @_state[key] if @_state?
+
+ # Get the current state from the storage layer
+ _getState: (key) ->
+ if @_options.storage
+ keyName = "#{@_options.name}_#{key}"
+ value = @_options.storage.getItem keyName
+ else
+ value = @_state[key] if @_state?
+
+ value = null if value is undefined or value is 'null'
+
+ @_options.afterGetState key, value
+ return value
+
+ # Show next step
+ _showNextStep: ->
+ step = @getStep @_current
+ showNextStepHelper = (e) => @showStep step.next
+
+ promise = @_makePromise(step.onNext @ if step.onNext?)
+ @_callOnPromiseDone promise, showNextStepHelper
+
+ # Show prev step
+ _showPrevStep: ->
+ step = @getStep @_current
+ showPrevStepHelper = (e) => @showStep step.prev
+
+ promise = @_makePromise(step.onPrev @ if step.onPrev?)
+ @_callOnPromiseDone promise, showPrevStepHelper
+
+ # Print message in console
+ _debug: (text) ->
+ window.console.log "Bootstrap Tour '#{@_options.name}' | #{text}" if @_options.debug
+
+ # Check if step path equals current document path
+ _isRedirect: (path, currentPath) ->
+ path? and path isnt '' and (
+ (({}).toString.call(path) is '[object RegExp]' and not path.test currentPath) or
+ (({}).toString.call(path) is '[object String]' and
+ path.replace(/\?.*$/, '').replace(/\/?$/, '') isnt currentPath.replace(/\/?$/, ''))
+ )
+
+ # Execute the redirect
+ _redirect: (step, path) ->
+ if $.isFunction step.redirect
+ step.redirect.call this, path
+ else if step.redirect is true
+ @_debug "Redirect to #{path}"
+ document.location.href = path
+
+ _isOrphan: (step) ->
+ # Do not check for is(':hidden') on svg elements. jQuery does not work properly on svg.
+ not step.element? or
+ not $(step.element).length or
+ $(step.element).is(':hidden') and
+ ($(step.element)[0].namespaceURI isnt 'http://www.w3.org/2000/svg')
+
+ _isLast: ->
+ @_current < @_options.steps.length - 1
+
+ # Show step popover
+ _showPopover: (step, i) ->
+ # Remove previously existing tour popovers. This prevents displaying of
+ # multiple inactive popovers when user navigates the tour too quickly.
+ $(".tour-#{@_options.name}").remove()
+
+ options = $.extend {}, @_options
+ isOrphan = @_isOrphan step
+
+ step.template = @_template step, i
+
+ if isOrphan
+ step.element = 'body'
+ step.placement = 'top'
+
+ $element = $ step.element
+ $element.addClass "tour-#{@_options.name}-element tour-#{@_options.name}-#{i}-element"
+
+ $.extend options, step.options if step.options
+ if step.reflex and not isOrphan
+ $element.addClass('tour-step-element-reflex')
+ $element.off("#{@_reflexEvent(step.reflex)}.tour-#{@_options.name}")
+ $element.on "#{@_reflexEvent(step.reflex)}.tour-#{@_options.name}", =>
+ if @_isLast() then @next() else @end()
+
+ $element
+ .popover(
+ placement: step.placement
+ trigger: 'manual'
+ title: step.title
+ content: step.content
+ html: true
+ animation: step.animation
+ container: step.container
+ template: step.template
+ selector: step.element
+ )
+ .popover 'show'
+
+ # Tip adjustment
+ $tip = if $element.data 'bs.popover' then $element.data('bs.popover').tip() else $element.data('popover').tip()
+ $tip.attr 'id', step.id
+ @_reposition $tip, step
+ @_center $tip if isOrphan
+
+ # Get popover template
+ _template: (step, i) ->
+ $template = if $.isFunction step.template then $(step.template i, step) else $(step.template)
+ $navigation = $template.find '.popover-navigation'
+ $prev = $navigation.find '[data-role="prev"]'
+ $next = $navigation.find '[data-role="next"]'
+ $resume = $navigation.find '[data-role="pause-resume"]'
+
+ $template.addClass 'orphan' if @_isOrphan step
+ $template.addClass "tour-#{@_options.name} tour-#{@_options.name}-#{i}"
+ $prev.addClass('disabled') if step.prev < 0
+ $next.addClass('disabled') if step.next < 0
+ $resume.remove() unless step.duration
+ $template.clone().wrap('<div>').parent().html()
+
+ _reflexEvent: (reflex) ->
+ if ({}).toString.call(reflex) is '[object Boolean]' then 'click' else reflex
+
+ # Prevent popover from crossing over the edge of the window
+ _reposition: ($tip, step) ->
+ offsetWidth = $tip[0].offsetWidth
+ offsetHeight = $tip[0].offsetHeight
+
+ tipOffset = $tip.offset()
+ originalLeft = tipOffset.left
+ originalTop = tipOffset.top
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight()
+ tipOffset.top = tipOffset.top + offsetBottom if offsetBottom < 0
+ offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth()
+ tipOffset.left = tipOffset.left + offsetRight if offsetRight < 0
+
+ tipOffset.top = 0 if tipOffset.top < 0
+ tipOffset.left = 0 if tipOffset.left < 0
+
+ $tip.offset(tipOffset)
+
+ # Reposition the arrow
+ if step.placement is 'bottom' or step.placement is 'top'
+ if originalLeft isnt tipOffset.left
+ @_replaceArrow $tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left'
+ else
+ if originalTop isnt tipOffset.top
+ @_replaceArrow $tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top'
+
+ # Center popover in the page
+ _center: ($tip) ->
+ $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2)
+
+ # Copy pasted from bootstrap-tooltip.js with some alterations
+ _replaceArrow: ($tip, delta, dimension, position)->
+ $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)
+ return callback() unless $element.length
+
+ $window = $(window)
+ offsetTop = $element.offset().top
+ windowHeight = $window.height()
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2))
+
+ @_debug "Scroll into view. ScrollTop: #{scrollTop}. Element offset: #{offsetTop}. Window height: #{windowHeight}."
+ counter = 0
+ $('body, html').stop(true, true).animate
+ scrollTop: Math.ceil(scrollTop),
+ =>
+ if ++counter is 2
+ callback()
+ @_debug """Scroll into view.
+ Animation end element offset: #{$element.offset().top}.
+ Window height: #{$window.height()}."""
+
+ # Debounced window resize
+ _onResize: (callback, timeout) ->
+ $(window).on "resize.tour-#{@_options.name}", ->
+ clearTimeout(timeout)
+ timeout = setTimeout(callback, 100)
+
+ # Event bindings for mouse navigation
+ _initMouseNavigation: ->
+ _this = @
+
+ # Go to next step after click on element with attribute 'data-role=next'
+ # Go to previous step after click on element with attribute 'data-role=prev'
+ # End tour after click on element with attribute 'data-role=end'
+ # Pause/resume tour after click on element with attribute 'data-role=pause-resume'
+ $(document)
+ .off("click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='prev']")
+ .off("click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='next']")
+ .off("click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='end']")
+ .off("click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='pause-resume']")
+ .on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='next']", (e) =>
+ e.preventDefault()
+ @next()
+ .on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='prev']", (e) =>
+ e.preventDefault()
+ @prev()
+ .on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='end']", (e) =>
+ e.preventDefault()
+ @end()
+ .on "click.tour-#{@_options.name}", ".popover.tour-#{@_options.name} *[data-role='pause-resume']", (e) ->
+ e.preventDefault()
+ $this = $ @
+
+ $this.text if _this._paused then $this.data 'pause-text' else $this.data 'resume-text'
+ if _this._paused then _this.resume() else _this.pause()
+
+ # Keyboard navigation
+ _initKeyboardNavigation: ->
+ return unless @_options.keyboard
+
+ $(document).on "keyup.tour-#{@_options.name}", (e) =>
+ return unless e.which
+
+ switch e.which
+ when 39
+ e.preventDefault()
+ if @_isLast() then @next() else @end()
+ 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) ->
+ if result and $.isFunction(result.then) then result else null
+
+ _callOnPromiseDone: (promise, cb, arg) ->
+ if promise
+ promise.then (e) =>
+ cb.call(@, arg)
+ else
+ cb.call(@, arg)
+
+ _showBackdrop: (element) ->
+ return if @backdrop.backgroundShown
+
+ @backdrop = $ '<div>', class: 'tour-backdrop'
+ @backdrop.backgroundShown = true
+ $('body').append @backdrop
+
+ _hideBackdrop: ->
+ @_hideOverlayElement()
+ @_hideBackground()
+
+ _hideBackground: ->
+ if @backdrop
+ @backdrop.remove()
+ @backdrop.overlay = null
+ @backdrop.backgroundShown = false
+
+ _showOverlayElement: (step) ->
+ $element = $ step.element
+
+ return if not $element or $element.length is 0 or @backdrop.overlayElementShown
+
+ @backdrop.overlayElementShown = true
+ @backdrop.$element = $element.addClass 'tour-step-backdrop'
+ @backdrop.$background = $ '<div>', class: 'tour-step-background'
+ elementData =
+ width: $element.innerWidth()
+ height: $element.innerHeight()
+ offset: $element.offset()
+
+ @backdrop.$background.appendTo('body')
+
+ elementData = @_applyBackdropPadding step.backdropPadding, elementData if step.backdropPadding
+ @backdrop
+ .$background
+ .width(elementData.width)
+ .height(elementData.height)
+ .offset(elementData.offset)
+
+ _hideOverlayElement: ->
+ return unless @backdrop.overlayElementShown
+
+ @backdrop.$element.removeClass 'tour-step-backdrop'
+ @backdrop.$background.remove()
+ @backdrop.$element = null
+ @backdrop.$background = null
+ @backdrop.overlayElementShown = false
+
+ _applyBackdropPadding: (padding, data) ->
+ if typeof padding is 'object'
+ padding.top ?= 0
+ padding.right ?= 0
+ padding.bottom ?= 0
+ padding.left ?= 0
+
+ data.offset.top = data.offset.top - padding.top
+ data.offset.left = data.offset.left - padding.left
+ data.width = data.width + padding.left + padding.right
+ data.height = data.height + padding.top + padding.bottom
+ else
+ data.offset.top = data.offset.top - padding
+ data.offset.left = data.offset.left - padding
+ data.width = data.width + (padding * 2)
+ data.height = data.height + (padding * 2)
+
+ data
+
+ _clearTimer: ->
+ window.clearTimeout @_timer
+ @_timer = null
+ @_duration = null
+
+ window.Tour = Tour
+
+) jQuery, window
diff --git a/src/coffee/bootstrap-tour.docs.coffee b/src/coffee/bootstrap-tour.docs.coffee
new file mode 100644
index 0000000..87fc21a
--- /dev/null
+++ b/src/coffee/bootstrap-tour.docs.coffee
@@ -0,0 +1,94 @@
+$ ->
+ $demo = $("#demo")
+ duration = 5000
+ remaining = duration
+ tour = new Tour(
+ onStart: -> $demo.addClass "disabled", true
+ onEnd: -> $demo.removeClass "disabled", true
+ debug: true
+ steps: [
+ path: "/"
+ element: "#demo"
+ placement: "bottom"
+ title: "Welcome to Bootstrap Tour!"
+ content: """
+ Introduce new users to your product by walking them through it step by step.
+ """
+ ,
+ path: "/"
+ element: "#usage"
+ placement: "top"
+ title: "A super simple setup"
+ content: "Easy is better, right? The tour is up and running with just a
+ few options and steps."
+ ,
+ path: "/"
+ element: "#license"
+ placement: "top"
+ title: "Best of all, it's free!"
+ content: "Yeah! Free as in beer... or speech. Use and abuse, but don't forget to contribute!"
+ ,
+ path: "/api"
+ element: "#options"
+ placement: "top"
+ title: "Flexibilty and expressiveness"
+ content: """
+ There are more options for those who want to get on the dark side.<br>
+ Power to the people!
+ """
+ reflex: true
+ ,
+ path: "/api"
+ element: "#duration"
+ placement: "top"
+ title: "Automagically expiring step",
+ content: """
+ A new addition: make your tour (or step) completely automatic. You set the duration, Bootstrap
+ Tour does the rest. For instance, this step will disappear in <em>5</em> seconds.
+ """
+ duration: 5000
+ ,
+ path: "/api"
+ element: "#methods table"
+ placement: "top"
+ title: "A new shiny Backdrop option"
+ content: """
+ If you need to highlight the current step's element, activate the backdrop and you won't lose
+ the focus anymore!
+ """
+ backdrop: true
+ ,
+ path: "/api"
+ element: "#reflex"
+ placement: "bottom"
+ title: "Reflex mode"
+ content: "Reflex mode is enabled, click on the text in the cell to continue!"
+ reflex: true
+ ,
+ path: "/api"
+ title: "And support for orphan steps"
+ content: """
+ If you activate the orphan property, the step(s) are shown centered in the page, and you can
+ forget to specify element and placement!
+ """
+ orphan: true
+ onHidden: -> window.location.assign "/"
+ ]
+ )
+ .init()
+
+ $('<div class="alert alert-info alert-dismissable"><button class="close" data-dismiss="alert" aria-hidden="true">×</button>You ended the demo tour. <a href="#" data-demo>Restart the demo tour.</a></div>').prependTo(".content").alert() if tour.ended()
+
+ $(document).on "click", "[data-demo]", (e) ->
+ e.preventDefault()
+ return if $(this).hasClass "disabled"
+ tour.restart()
+ $(".alert").alert "close"
+
+ $("html").smoothScroll()
+
+ $(".gravatar").each ->
+ $this = $(@)
+ email = md5 $this.data "email"
+
+ $(@).attr "src", "http://www.gravatar.com/avatar/#{email}?s=60"
diff --git a/src/coffee/bootstrap-tour.spec.coffee b/src/coffee/bootstrap-tour.spec.coffee
new file mode 100644
index 0000000..4b05fea
--- /dev/null
+++ b/src/coffee/bootstrap-tour.spec.coffee
@@ -0,0 +1,775 @@
+describe 'Bootstrap Tour', ->
+
+ beforeEach ->
+ $.support.transition = false
+ $.fx.off = true
+
+ afterEach ->
+ tour = @tour
+ @tour._setState('current_step', null)
+ @tour._setState('end', null)
+ $.each @tour._options.steps, (i, s) ->
+ $element = $(tour.getStep(i).element)
+
+ $element
+ .popover('destroy')
+ .removeData('bs.popover')
+ $element.remove()
+
+ it 'should set the tour options', ->
+ @tour = new Tour
+ name: 'test'
+ afterSetState: -> true
+ afterGetState: -> true
+ expect(@tour._options.name).toBe 'test'
+ expect(@tour._options.afterGetState).toBeTruthy
+ expect(@tour._options.afterSetState).toBeTruthy
+
+ it 'should have `tour` as default name', ->
+ @tour = new Tour
+ expect(@tour._options.name).toBe 'tour'
+
+ it 'should accept an array of steps', ->
+ @tour = new Tour
+ expect(@tour._options.steps).toEqual [] # tour accepts an array of steps
+
+ it '`_setState` should save state as localStorage item', ->
+ @tour = new Tour
+ @tour._setState('test', 'yes')
+ expect(window.localStorage.getItem('tour_test')).toBe 'yes'
+
+ it '`_setState` should execute storage.setItem function if provided', ->
+ aliasKeyName = undefined
+ aliasValue = undefined
+
+ @tour = new Tour
+ name: 'test'
+ storage:
+ setItem: (keyName, value) ->
+ aliasKeyName = keyName
+ aliasValue = value
+ getItem: (value) ->
+ return aliasValue
+
+ @tour._setState('save', 'yes')
+ expect(aliasKeyName).toBe 'test_save'
+ expect(aliasValue).toBe 'yes'
+
+ it '`_setState` should save state internally if storage is false', ->
+ @tour = new Tour
+ storage: false
+ @tour._setState('test', 'yes')
+ expect(@tour._state['test']).toBe 'yes'
+
+ it '`_removeState` should remove state localStorage item', ->
+ @tour = new Tour
+ @tour._setState('test', 'yes')
+ @tour._removeState('test')
+ expect(window.localStorage.getItem('tour_test')).toBe null
+
+ it '`_removeState` should remove state internally if storage is false', ->
+ @tour = new Tour
+ storage: false
+ @tour._setState('test', 'yes')
+ @tour._removeState('test')
+ expect(@tour._state['test']).toBeUndefined()
+
+ it '`_getState` should get state localStorage items', ->
+ @tour = new Tour
+ @tour._setState('test', 'yes')
+ expect(@tour._getState('test')).toBe 'yes'
+ window.localStorage.setItem('tour_test', null)
+
+ it '`_getState` should get the internal state if storage is false', ->
+ @tour = new Tour
+ storage: false
+ @tour._setState('test', 'yes')
+ expect(@tour._getState('test')).toBe 'yes'
+
+ it '`addStep` should add a step', ->
+ @tour = new Tour
+ step = element: $('<div></div>').appendTo('body')
+ @tour.addStep(step)
+ expect(@tour._options.steps).toEqual [step]
+
+ it '`addSteps` should add multiple step', ->
+ @tour = new Tour
+ firstStep = element: $('<div></div>').appendTo('body')
+ secondStep = element: $('<div></div>').appendTo('body')
+ @tour.addSteps([firstStep, secondStep])
+ expect(@tour._options.steps).toEqual [firstStep, secondStep]
+
+ it 'step should have an id', ->
+ @tour = new Tour
+ $element = $('<div></div>').appendTo('body')
+ @tour.addStep({element: $element})
+ @tour.start()
+ expect($element.data('bs.popover').tip().attr('id')).toBe 'step-0' # tour runs onStart when the first step shown
+
+ it 'with `onStart` option should run the callback before showing the first step', ->
+ tour_test = 0
+ @tour = new Tour
+ onStart: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(tour_test).toBe 2 # tour runs onStart when the first step shown
+
+ it 'with `onEnd` option should run the callback after hiding the last step', ->
+ tour_test = 0
+ @tour = new Tour
+ onEnd: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.end()
+ expect(tour_test).toBe 2 # tour runs onEnd when the last step hidden
+
+ it 'with `onShow` option should run the callback before showing the step', ->
+ tour_test = 0
+ @tour = new Tour
+ onShow: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(tour_test).toBe 2 # tour runs onShow when first step shown
+ @tour.next()
+ expect(tour_test).toBe 4 # tour runs onShow when next step shown
+
+ it 'with `onShown` option should run the callback after showing the step', ->
+ tour_test = 0
+ @tour = new Tour
+ onShown: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(tour_test).toBe 2 # tour runs onShown after first step shown
+
+
+ it 'with `onHide` option should run the callback before hiding the step', ->
+ tour_test = 0
+ @tour = new Tour
+ onHide: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(tour_test).toBe 2 # tour runs onHide when first step hidden
+ @tour.hideStep(1)
+ expect(tour_test).toBe 4 # tour runs onHide when next step hidden
+
+ it 'with onHidden option should run the callback after hiding the step', ->
+ tour_test = 0
+ @tour = new Tour
+ onHidden: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(tour_test).toBe 2 # tour runs onHidden after first step hidden
+ @tour.next()
+ expect(tour_test).toBe 4 # tour runs onHidden after next step hidden
+
+ it '`addStep` with onShow option should run the callback before showing the step', ->
+ tour_test = 0
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onShow: -> tour_test = 2
+ @tour.start()
+ expect(tour_test).toBe 0 # tour does not run onShow when step not shown
+ @tour.next()
+ expect(tour_test).toBe 2 # tour runs onShow when step shown
+
+ it '`addStep` with onHide option should run the callback before hiding the step', ->
+ tour_test = 0
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onHide: -> tour_test = 2
+ @tour.start()
+ @tour.next()
+ expect(tour_test).toBe 0 # tour does not run onHide when step not hidden
+ @tour.hideStep(1)
+ expect(tour_test).toBe 2 # tour runs onHide when step hidden
+
+ it '`getStep` should get a step', ->
+ @tour = new Tour
+ step =
+ element: $('<div></div>').appendTo('body')
+ id: 'step-0'
+ path: 'test'
+ placement: 'left'
+ title: 'Test'
+ content: 'Just a test'
+ next: 2
+ prev: -1
+ animation: false
+ autoscroll: true
+ container: 'body'
+ backdrop: false
+ backdropPadding: 0
+ redirect: true
+ orphan: false
+ duration: false
+ delay: false
+ template: '<div class="popover">
+ <div class="arrow"></div>
+ <h3 class="popover-title"></h3>
+ <div class="popover-content"></div>
+ <nav class="popover-navigation">
+ <div class="btn-group">
+ <button class="btn btn-sm btn-default" data-role="prev">« Prev</button>
+ <button class="btn btn-sm btn-default" data-role="next">Next »</button>
+ </div>
+ <button class="btn btn-sm btn-default" data-role="end">End tour</button>
+ </nav>
+ </div>'
+ onShow: (tour) ->
+ onShown: (tour) ->
+ onHide: (tour) ->
+ onHidden: (tour) ->
+ onNext: (tour) ->
+ onPrev: (tour) ->
+ onPause: (tour) ->
+ onResume: (tour) ->
+ @tour.addStep(step)
+ # remove properties that we don't want to check from both steps object
+ expect(@tour.getStep(0)).toEqual step
+
+ it '`start` should start a tour', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect($('.popover').length).toBe 1
+
+ it '`init` should continue a tour', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour._setState('current_step', 0)
+ @tour.init()
+ expect($('.popover').length).toBe 1
+
+ it '`init` should not continue a tour that ended', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour._setState('current_step', 0)
+ @tour._setState('end', 'yes')
+ @tour.init()
+ expect($('.popover').length).toBe 0 # previously ended tour don't start again
+
+ it '`init`(true) should force continuing a tour that ended', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour._setState('current_step', 0)
+ @tour._setState('end', 'yes')
+ @tour.init(true)
+ expect($('.popover').length).toBe 1 # previously ended tour starts again if forced to
+
+ it '`next` should hide current step and show next step', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(@tour.getStep(0).element.data('bs.popover')).toBeUndefined() # tour hides current step
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1 # tour shows next step
+
+ it '`end` should hide current step and set end state', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.end()
+ expect(@tour.getStep(0).element.data('bs.popover')).toBeUndefined() # tour hides current step
+ expect(@tour._getState('end')).toBe 'yes'
+
+ it '`ended` should return true if tour ended and false if not', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(@tour.ended()).toBe false
+ @tour.end()
+ expect(@tour.ended()).toBe true
+
+ it '`ended` should always return false if tour started by force', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.end()
+ @tour.start(true)
+ expect(@tour.ended()).toBe false
+
+ it '`restart` should clear all states and start tour', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ @tour.end()
+ @tour.restart()
+ expect(@tour._getState('end')).toBe null
+ expect(@tour._current).toBe 0
+ expect($('.popover').length).toBe 1 # tour starts
+
+ it '`hideStep` should hide a step', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.hideStep(0)
+ expect(@tour.getStep(0).element.data('bs.popover')).toBeUndefined()
+
+ it '`showStep` should set a step and show it', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.showStep(1)
+ expect(@tour._current).toBe 1
+ expect($('.popover').length).toBe 1 # tour shows one step
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1 # tour shows correct step
+
+ it '`showStep` should not show anything when the step does not exist', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.showStep(2)
+ expect($('.popover').length).toBe 0
+
+ it '`showStep` should execute template if it is a function', ->
+ @tour = new Tour
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ template: -> '<div class="popover"></div>'
+ @tour.showStep(0)
+ expect($('.popover').length).toBe 1
+
+ it '`getStep` should add disabled classes to the first and last popover buttons', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.showStep(0)
+ expect($('.popover [data-role="prev"]').hasClass('disabled')).toBe true
+ @tour.showStep(1)
+ expect($('.popover [data-role="next"]').hasClass('disabled')).toBe true
+
+ it '`setCurrentStep` should set the current step', ->
+ @tour = new Tour
+ @tour.setCurrentStep(4)
+ expect(@tour._current).toBe 4 # tour sets current step if passed a value
+ @tour._setState('current_step', 2)
+ @tour.setCurrentStep()
+ expect(@tour._current).toBe 2 # tour reads current step state if not passed a value
+
+ it '`goTo` should show the specified step', ->
+ @tour = new Tour
+ @tour.addStep({element: $('<div></div>').appendTo('body')})
+ @tour.addStep({element: $('<div></div>').appendTo('body')})
+ @tour.goTo(1)
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1
+
+ it '`next` should show the next step', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1
+
+ it '`prev` should show the previous step', ->
+ @tour = new Tour
+ @tour.addStep({element: $('<div></div>').appendTo('body')})
+ @tour.addStep({element: $('<div></div>').appendTo('body')})
+ @tour.goTo(1)
+ @tour.prev()
+ expect(@tour.getStep(0).element.data('bs.popover').tip().filter(':visible').length).toBe 1
+
+ it '`showStep` should show multiple step on the same element', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+
+ # tour show the first step
+ expect(@tour.getStep(0).element.data('bs.popover').tip().filter(':visible').length).toBe 1
+ @tour.next()
+
+ # tour show the second step on the same element
+ expect(@tour.getStep(1).element.data('bs.popover').tip().filter(':visible').length).toBe 1
+
+ it 'should evaluate `path correctly', ->
+ @tour = new Tour
+
+ # redirect if path doesn't match current path
+ expect(@tour._isRedirect('/anotherpath', '/somepath')).toBe true
+ # don't redirect if no path
+ expect(@tour._isRedirect(undefined, '/')).toBe false
+ # don't redirect if path empty
+ expect(@tour._isRedirect('', '/')).toBe false
+ # don't redirect if path matches current path
+ expect(@tour._isRedirect('/somepath', '/somepath')).toBe false
+ # don't redirect if path with slash matches current path
+ expect(@tour._isRedirect('/somepath/', '/somepath')).toBe false
+ # don't redirect if path matches current path with slash
+ expect(@tour._isRedirect('/somepath', '/somepath/')).toBe false
+ # don't redirect if path with query params matches current path
+ expect(@tour._isRedirect('/somepath?search=true', '/somepath')).toBe false
+ # don't redirect if path with slash and query params matches current path
+ expect(@tour._isRedirect('/somepath/?search=true', '/somepath')).toBe false
+ # don't redirect if current path matches path regex
+ expect(@tour._isRedirect /some*/, '/somepath').toBe false
+
+ it '`_getState` should return null after `_removeState` with null value', ->
+ @tour = new Tour
+ @tour._setState('test', 'test')
+ @tour._removeState('test')
+ expect(@tour._getState('test')).toBe null
+
+ it '`_removeState` should call `afterRemoveState` callback', ->
+ sentinel = false
+ @tour = new Tour
+ afterRemoveState: -> sentinel = true
+ @tour._removeState('current_step')
+ expect(sentinel).toBe true
+
+ it 'should not move to the next state until the onShow promise is resolved', ->
+ @tour = new Tour
+ deferred = $.Deferred()
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onShow: -> return deferred
+ @tour.start()
+ @tour.next()
+ expect(@tour._current).toBe 0 # tour shows old state until resolving of onShow promise
+ deferred.resolve()
+ expect(@tour._current).toBe 1 # tour shows new state after resolving onShow promise
+
+ it 'should not hide popover until the onHide promise is resolved', ->
+ deferred = $.Deferred()
+ @tour = new Tour
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onHide: -> return deferred
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(@tour._current).toBe 0 # tour shows old state until resolving of onHide promise
+ deferred.resolve()
+ expect(@tour._current).toBe 1 # tour shows new state after resolving onShow promise
+
+ it 'should not start until the onStart promise is resolved', ->
+ deferred = $.Deferred()
+ @tour = new Tour
+ onStart: -> deferred
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect($('.popover').length).toBe 0
+ deferred.resolve()
+ expect($('.popover').length).toBe 1
+
+ it 'should add `tour-step-element-reflex` class to the step element if reflex is active', ->
+ @tour = new Tour
+ $element = $('<div></div>').appendTo('body')
+ @tour.addStep
+ element: $element
+ reflex: true
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ expect($element.hasClass('tour-step-element-reflex')).toBe false
+ @tour.start()
+ expect($element.hasClass('tour-step-element-reflex')).toBe true
+ @tour.next()
+ expect($element.hasClass('tour-step-element-reflex')).toBe false
+
+ it '`showStep` redirects to the anchor when the path is an anchor', ->
+ @tour = new Tour
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ path: '#mytest'
+ @tour.showStep(0)
+ expect(document.location.hash).toBe '#mytest' # Tour step has moved to the anchor
+ document.location.hash = ''
+
+ it '`backdrop` parameter should show backdrop with step', ->
+ @tour = new Tour
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ backdrop: false
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ backdrop: true
+ @tour.start()
+ expect($('.tour-backdrop').length).toBe 0 # disable backdrop
+ expect($('.tour-step-backdrop').length).toBe 0 # disable backdrop
+ expect($('.tour-step-background').length).toBe 0 # disable backdrop
+ @tour.next()
+ expect($('.tour-backdrop').length).toBe 1 # enable backdrop
+ expect($('.tour-step-backdrop').length).toBe 1 # enable backdrop
+ expect($('.tour-step-background').length).toBe 1 # enable backdrop
+ @tour.end()
+ expect($('.tour-backdrop').length).toBe 0 # disable backdrop
+ expect($('.tour-step-backdrop').length).toBe 0 # disable backdrop
+ expect($('.tour-step-background').length).toBe 0 # disable backdrop
+
+ it 'step with backdrop and invalid selector should not attempt to create an overlay element', ->
+ @tour = new Tour
+ @tour._showOverlayElement '#nonExistingElement'
+ expect(@tour.backdrop.overlayElementShown).toBe false
+
+ it 'should render the padding on the backdrop element', ->
+ @tour = new Tour
+ backdrop: true
+ $firstElement = $('<div></div>', width: 10, height: 10).appendTo('body')
+ $secondElement = $('<div></div>', width: 10, height: 10).appendTo('body')
+ firstPadding = 20
+ secondPadding =
+ top: 40
+ right: 30
+ bottom: 20
+ left: 10
+
+ @tour.addStep
+ backdrop: true
+ backdropPadding: firstPadding
+ element: $firstElement
+ @tour.addStep
+ backdrop: true
+ backdropPadding: secondPadding
+ element: $secondElement
+ @tour.start()
+ expect(@tour.backdrop.$background.width()).toBe $firstElement.innerWidth() + (firstPadding * 2)
+ expect(@tour.backdrop.$background.height()).toBe $firstElement.innerHeight() + (firstPadding * 2)
+ @tour.next()
+ expect(@tour.backdrop.$background.width())
+ .toBe $secondElement.innerWidth() + secondPadding.left + secondPadding.right
+ expect(@tour.backdrop.$background.height())
+ .toBe $secondElement.innerHeight() + secondPadding.top + secondPadding.bottom
+
+ it '`basePath` should prepend the path to the steps', ->
+ @tour = new Tour
+ basePath: 'test/'
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ path: 'test.html'
+
+ # Tour adds basePath to step path
+ expect(@tour._isRedirect(@tour._options.basePath + @tour.getStep(0).path, 'test/test.html')).toBe false
+
+ it 'with `onNext` option should run the callback before showing the next step', ->
+ tour_test = 0
+ @tour = new Tour
+ onNext: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect(tour_test).toBe 2
+
+ it '`showStep` should not show step if tour ended', ->
+ @tour = new Tour
+ onNext: (t) -> t.end()
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect($('.popover').length).toBe 0
+
+ it '`addStep` with onNext option should run the callback before showing the next step', ->
+ tour_test = 0
+ @tour = new Tour
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onNext: -> tour_test = 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(tour_test).toBe 0 # tour does not run onNext when next step is not called
+ @tour.next()
+ expect(tour_test).toBe 2 # tour runs onNext when next step is called
+
+ it 'with `onPrev` option should run the callback before showing the prev step', ->
+ tour_test = 0
+ @tour = new Tour
+ onPrev: -> tour_test += 2
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ @tour.prev()
+ expect(tour_test).toBe 2 # tour runs onPrev when prev step is called
+
+ it '`addStep` with `onPrev` option should run the callback before showing the prev step', ->
+ tour_test = 0
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onPrev: -> tour_test = 2
+ @tour.start()
+ expect(tour_test).toBe 0 # tour does not run onPrev when prev step is not called
+ @tour.next()
+ @tour.prev()
+ expect(tour_test).toBe 2 # tour runs onPrev when prev step is called
+
+ it 'should render custom navigation template', ->
+ @tour = new Tour
+ template:
+ '<div class="popover tour">
+ <div class="arrow"></div>
+ <h3 class="popover-title"></h3>
+ <div class="popover-content"></div>
+ <div class="popover-navigation">
+ <a data-role="prev"></a>
+ <a data-role="next"></a>
+ <a data-role="end"></a>
+ </div>
+ </div>'
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.next()
+ expect($('.popover .popover-navigation a').length).toBe 3
+
+ it 'should have `data-role` attribute for navigation template', ->
+ @tour = new Tour
+ template = $(@tour._options.template)
+ expect(template.find('*[data-role=next]').size()).toBe 1
+ expect(template.find('*[data-role=prev]').size()).toBe 1
+ expect(template.find('*[data-role=end]').size()).toBe 1
+
+ it 'should unbind click events when hiding step (in reflex mode)', ->
+ $element = $('<div></div>').appendTo('body')
+ @tour = new Tour
+ @tour.addStep
+ element: $element
+ reflex: true
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+
+ expect($._data($element[0], 'events')).not.toBeDefined()
+ @tour.start()
+ expect($._data($element[0], 'events').click.length).toBeGreaterThan 0
+ expect($._data($element[0], 'events').click[0].namespace).toBe "tour-#{@tour._options.name}"
+
+ $.each [0..10], =>
+ @tour.next()
+ expect($._data($element[0], 'events')).not.toBeDefined()
+ @tour.prev()
+ expect($._data($element[0], 'events').click.length).toBeGreaterThan 0
+ expect($._data($element[0], 'events').click[0].namespace).toBe "tour-#{@tour._options.name}"
+
+ it 'should add `tour-{tourName}` and `tour-{tourName}-{stepId}` classses to the popover', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.showStep(0)
+ expect($('.popover').hasClass("tour-#{@tour._options.name}")).toBe true
+ expect($('.popover').hasClass("tour-#{@tour._options.name}-0")).toBe true
+
+ it 'should add `tour-{tourName}-element` and `tour-{tourName}-{stepId}-element` classses to the popover element', ->
+ @tour = new Tour
+ $element = $ '<div></div>'
+ @tour.addStep element: $element.appendTo 'body'
+ @tour.showStep 0
+ expect($element.hasClass "tour-#{@tour._options.name}-element").toBe true
+ expect($element.hasClass "tour-#{@tour._options.name}-0-element").toBe true
+
+ # orphan
+ it 'should show orphan steps', ->
+ @tour = new Tour
+ @tour.addStep
+ orphan: true
+ @tour.showStep(0)
+ expect($('.popover').length).toBe 1
+ $('.popover').remove()
+
+ it 'should add `orphan` class to the popover', ->
+ @tour = new Tour
+ @tour.addStep
+ orphan: true
+ @tour.showStep(0)
+ expect($('.popover').hasClass('orphan')).toBe true
+ $('.popover').remove()
+
+ it 'handles quota_exceeded exceptions', ->
+ @tour = new Tour
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ spyOn(@tour._options.storage, 'setItem').andCallFake ->
+ throw new Error 'QUOTA_EXCEEDED_ERR', 'QUOTA_EXCEEDED_ERR: DOM Exception 22'
+ spyOn(@tour, '_setState')
+ @tour._setState('test', '1')
+ expect(=> @tour._setState).not.toThrow()
+
+ it 'should not try to scroll to non-existing element', ->
+ @tour = new Tour
+ orphan: true
+ @tour.addStep
+ element: '#nonExistingElement'
+ @tour.showStep 0
+ expect($('.popover').length).toBe 1
+
+ # duration
+ it 'should start the timer', ->
+ @tour = new Tour
+ duration: 5000
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ expect(@tour._timer).toBeDefined()
+ expect(@tour._duration).toBeDefined()
+ window.clearTimeout(@tour._timer)
+
+ it 'should pause the timer on pause', ->
+ @tour = new Tour
+ duration: 5000
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ window.setTimeout( =>
+ @tour.pause()
+ expect(@tour._timer).toBe null
+ expect(@tour._duration).toBeGreaterThan(0).toBeLessThan(5000)
+ , 1000)
+
+ it 'should stop the timer on hideStep', ->
+ @tour = new Tour
+ duration: 5000
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.hideStep(0)
+ expect(@tour._timer).toBe null
+ expect(@tour._duration).toBe null
+
+ it 'should stop the timer on end', ->
+ @tour = new Tour
+ duration: 5000
+ @tour.addStep(element: $('<div></div>').appendTo('body'))
+ @tour.start()
+ @tour.end()
+ expect(@tour._timer).toBe null
+ expect(@tour._duration).toBe null
+
+ ### 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.
+ $.support.transition = true
+ $.fx.off = false
+ isStepShown = false
+
+ # Cleanup all leftover popovers from previous tests.
+ $('.popover').remove()
+
+ # Setup two-step tour. The problem should occur when switching from first
+ # step to the second while the transition effect of the first one is still
+ # active.
+ @tour = new Tour
+ @tour.addStep element: $('<div></div>').appendTo('body')
+ @tour.addStep
+ element: $('<div></div>').appendTo('body')
+ onShown: ->
+ isStepShown = true
+
+ # Request the first step and immediately the second one. This way the first
+ # step won't be displayed when the second step is requested, so the request
+ # for second step can not cleanup existing popovers yet.
+ runs ->
+ @tour.goTo(0)
+ @tour.goTo(1)
+ waitsFor ->
+ isStepShown
+ , 'The second step should be displayed.', 1000
+ runs -> expect($('.popover').length).toBe 1
+ ###
diff --git a/src/docs/CNAME b/src/docs/CNAME
new file mode 100644
index 0000000..b876b5a
--- /dev/null
+++ b/src/docs/CNAME
@@ -0,0 +1 @@
+bootstraptour.com
diff --git a/src/docs/_includes/footer.html b/src/docs/_includes/footer.html
new file mode 100644
index 0000000..ea93f42
--- /dev/null
+++ b/src/docs/_includes/footer.html
@@ -0,0 +1,14 @@
+<script src="/components/jquery/dist/jquery.min.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script src="/components/bootstrap/dist/js/bootstrap.min.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script src="/components/blueimp-md5/js/md5.min.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script src="/assets/vendor/jquery.smoothscroll.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script src="/assets/js/bootstrap-tour.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script src="/assets/js/bootstrap-tour.docs.js?{{ site.time | date_to_xmlschema | cgi_escape }}"></script>
+<script>
+window.twttr = (function (d,s,id) {
+ var t, js, fjs = d.getElementsByTagName(s)[0];
+ if (d.getElementById(id)) return; js=d.createElement(s); js.id=id; js.async=1;
+ js.src="https://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs);
+ return window.twttr || (t = { _e: [], ready: function(f){ t._e.push(f) } });
+}(document, "script", "twitter-wjs"));
+</script>
diff --git a/src/docs/_includes/header.html b/src/docs/_includes/header.html
new file mode 100644
index 0000000..4c2e5d1
--- /dev/null
+++ b/src/docs/_includes/header.html
@@ -0,0 +1,29 @@
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="description" content="Quick and easy way to build your product tours with Bootstrap Popovers.">
+<title>
+ {% if page.title == "Bootstrap Tour" %}
+ {{ page.title }}
+ {% else %}
+ {{ page.title }} · Bootstrap Tour
+ {% endif %}
+</title>
+<link href="/components/bootstrap/dist/css/bootstrap.min.css?{{ site.time | date_to_xmlschema | cgi_escape }}" rel="stylesheet">
+<link href="/assets/vendor/pygments-manni.css?{{ site.time | date_to_xmlschema | cgi_escape }}" rel="stylesheet">
+<link href="/assets/css/bootstrap-tour.css?{{ site.time | date_to_xmlschema | cgi_escape }}" rel="stylesheet">
+<link href="/assets/css/bootstrap-tour.docs.css?{{ site.time | date_to_xmlschema | cgi_escape }}" rel="stylesheet">
+<link href="http://boostraptour.com{{ page.url }}" rel="canonical">
+<!--[if lt IE 9]><script src="/components/html5shiv/dist/html5shiv.min.js"></script><![endif]-->
+<script>
+if (window.location.href.indexOf('sorich87.github.com') > -1) {
+ window.location.href = 'http://bootstraptour.com{{ page.url }}';
+}
+
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-47485647-1', 'bootstraptour.com');
+ga('send', 'pageview');
+</script>
diff --git a/src/docs/_includes/nav.html b/src/docs/_includes/nav.html
new file mode 100644
index 0000000..19e2244
--- /dev/null
+++ b/src/docs/_includes/nav.html
@@ -0,0 +1,26 @@
+<div id="navbar" class="navbar navbar-static-top">
+ <div class="container">
+ <button type="button" class="navbar-toggle pull-right" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a href="/" class="navbar-brand">Bootstrap Tour</a>
+ <div class="collapse navbar-collapse">
+ <ul class="navbar-nav nav">
+ <li {% if page.slug == "home" %} class="active"{% endif %}>
+ <a href="/">Getting started</a>
+ </li>
+ <li>
+ <a href="https://github.com/sorich87/bootstrap-tour/releases">Download</a>
+ </li>
+ <li {% if page.slug == "api" %} class="active"{% endif %}>
+ <a href="/api">API Documentation</a>
+ </li>
+ <li>
+ <a href="https://github.com/sorich87/bootstrap-tour/issues">Bug reports</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
diff --git a/src/docs/_layouts/default.html b/src/docs/_layouts/default.html
new file mode 100644
index 0000000..195669e
--- /dev/null
+++ b/src/docs/_layouts/default.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ {% include header.html %}
+ </head>
+ <body>
+ <a href="https://github.com/sorich87/bootstrap-tour" id="github"><img src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
+ {% include nav.html %}
+ {{ content }}
+ {% include footer.html %}
+ </body>
+</html>
diff --git a/src/docs/api.html b/src/docs/api.html
new file mode 100644
index 0000000..39a3ac2
--- /dev/null
+++ b/src/docs/api.html
@@ -0,0 +1,746 @@
+---
+layout: default
+title: API Documentation
+slug: api
+---
+
+<section id="options">
+ <div class="container">
+ <div class="page-header">
+ <h2>Options <small>Well, let's go deeper</small></h2>
+ </div>
+ <h3 id="tour-options">Global options</h3>
+{% highlight javascript %}
+var tour = new Tour({
+ name: "tour",
+ steps: [],
+ container: "body",
+ keyboard: true,
+ storage: window.localStorage,
+ debug: false,
+ backdrop: false,
+ backdropPadding: 0,
+ redirect: true,
+ orphan: false,
+ duration: false,
+ delay: false,
+ basePath: "",
+ template: "<div class='popover tour'>
+ <div class='arrow'></div>
+ <h3 class='popover-title'></h3>
+ <div class='popover-content'></div>
+ <div class='popover-navigation'>
+ <button class='btn btn-default' data-role='prev'>« Prev</button>
+ <span data-role='separator'>|</span>
+ <button class='btn btn-default' data-role='next'>Next »</button>
+ </div>
+ <button class='btn btn-default' data-role='end'>End tour</button>
+ </nav>
+ </div>",
+ afterGetState: function (key, value) {},
+ afterSetState: function (key, value) {},
+ afterRemoveState: function (key, value) {},
+ onStart: function (tour) {},
+ onEnd: function (tour) {},
+ onShow: function (tour) {},
+ onShown: function (tour) {},
+ onHide: function (tour) {},
+ onHidden: function (tour) {},
+ onNext: function (tour) {},
+ onPrev: function (tour) {},
+ onPause: function (tour, duration) {},
+ onResume: function (tour, duration) {}
+});
+{% endhighlight %}
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Description</th>
+ <th width="50%">Default</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>name</td>
+ <td>String</td>
+ <td>This option is used to build the name of the storage item where the tour state is stored.
+ The name should contain only alphanumerics, underscores and hyphens.
+ You can initialize several tours with different names in the same page and application.</td>
+ <td><code>'tour'</code></td>
+ </tr>
+ <tr>
+ <td>steps</td>
+ <td>Array</td>
+ <td>A list of object representing the steps to be included in the tour. Jump to
+ <a href="#step-options">Step options</a> for the single step API.</td>
+ <td><code>[]</code></td>
+ </tr>
+ <tr>
+ <td>container</td>
+ <td>String</td>
+ <td>Appends the step popover to a specific element.<br>
+ <em>See Usage section of
+ <a href="http://getbootstrap.com/javascript/#popovers" target="_blank">Popover</a>.
+ </em>
+ </td>
+ <td><code>'body'</code></td>
+ </tr>
+ <tr>
+ <td>autoscroll</td>
+ <td>Boolean</td>
+ <td>Autoscrolls the window when the step popover is out of view.</td>
+ <td><code>true</code></td>
+ </tr>
+ <tr>
+ <td>keyboard</td>
+ <td>Boolean</td>
+ <td>This option set the left and right arrow navigation.</td>
+ <td><code>true</code></td>
+ </tr>
+ <tr id="storage">
+ <td>storage</td>
+ <td>Object</td>
+ <td>The storage system you want to use. Could be the objects window.localStorage,
+ window.sessionStorage or your own object.<br>
+ You can set this option as
+ <code>false</code> to disable storage
+ persistence (the tour starts from beginning every time the page is
+ loaded).<br>
+ <em>Read more about <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage" target="_blank">DOM Storage interfaces</a>.</em>
+ </td>
+ <td><code>window.localStorage</code></td>
+ </tr>
+ <tr>
+ <td>debug</td>
+ <td>Boolean</td>
+ <td>Set this option to true to have some useful informations printed in the
+ console.</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr>
+ <td>backdrop</td>
+ <td>Boolean</td>
+ <td>Show a dark backdrop behind the popover and its element,
+ highlighting the current step.</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr class="success">
+ <td>backdropPadding <span class="label label-success">NEW</span></td>
+ <td>Number|Object</td>
+ <td>Add padding to the backdrop element that highlights the step element.<br>
+ It can be a number or a object containing optional <code>top</code>, <code>right</code>, <code>bottom</code> and <code>left</code> numbers.</td>
+ <td><code>0</code></td>
+ </tr>
+ <tr>
+ <td>redirect</td>
+ <td>Boolean|Function</td>
+ <td>Set a custom function to execute as redirect function.
+ The default redirect relies on the traditional
+ <code class="language-javascript">document.location.href</code></td>
+ <td><code>true</code></td>
+ </tr>
+ <tr>
+ <td>orphan</td>
+ <td>Boolean</td>
+ <td>Allow to show the step regardless whether its element is not set, is
+ not present in the page or is hidden. The step is fixed
+ positioned in the middle of the page.</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr id="duration" class="success">
+ <td>duration <span class="label label-success">NEW</span></td>
+ <td>Boolean|Number</td>
+ <td>Set a expiration time for the steps. When the current step expires,
+ the next step is automatically shown. See it as a sort of guided, automatized tour
+ functionality. The value is specified in milliseconds</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr id="delay" class="success">
+ <td>delay <span class="label label-success">NEW</span></td>
+ <td>Boolean|Number</td>
+ <td>Specifies a delay for the showing and hiding the tour steps.
+ It can be:
+ <ul>
+ <li>a falsy - there is no delay</li>
+ <li>a number - used as a delay for both showing and hiding. In milliseconds</li>
+ <li>a object containing optional <code>show</code> and <code>hide</code> numbers - defines the delays for showing and hiding respectively</li>
+ </ul>
+ <td><code>0</code></td>
+ </tr>
+ <tr>
+ <td>basePath</td>
+ <td>String</td>
+ <td>Specify a default base path prepended to the
+ <code>path</code> option of every single
+ step. Very useful if you need to reuse the same tour on different
+ environments or sub-projects.</td>
+ <td><code>''</code></td>
+ </tr>
+ <tr>
+ <td>template</td>
+ <td>String|Function</td>
+ <td>String or function that returns a string of the HTML template for
+ the popovers. If you pass a Function, two parameters are available:
+ <strong>i</strong> is the position of step in the tour and
+ <strong>step</strong> is the an object that contains all the other step
+ options.<br>
+ From version 0.5, the navigation template is included inside the
+ template so you can easily rewrite it. However, Bootstrap Tour maps the
+ <em>previous</em>, <em>next</em> and <em>end</em> logics to the elements
+ which have the related <code class="language-markup">data-role</code>
+ attribute. Therefore, you can also have multiple elements with the same
+ <code class="language-markup">data-role</code> attribute.
+ </td>
+ <td>
+{% highlight javascript %}
+"<div class='popover tour'>
+ <div class='arrow'></div>
+ <h3 class='popover-title'></h3>
+ <div class='popover-content'></div>
+ <div class='popover-navigation'>
+ <button class='btn btn-default' data-role='prev'>« Prev</button>
+ <span data-role='separator'>|</span>
+ <button class='btn btn-default' data-role='next'>Next »</button>
+ <button class='btn btn-default' data-role='end'>End tour</button>
+ </div>
+</div>"
+{% endhighlight %}
+ </td>
+ </tr>
+ <tr>
+ <td>afterGetState, afterSetState, afterRemoveState</td>
+ <td>Function</td>
+ <td>You may want to do something right after Bootstrap Tour read, write or remove
+ the state. Just pass functions to these.<br />
+ Your functions can have two parameters:
+ <ul class="unstyled">
+ <li>
+ <strong>key</strong>
+ Contains the name of the state being saved. It can be
+ <code>current_step</code> (for the state where the
+ latest step the visitor viewed is saved) or
+ <code>end</code> (for the state which is saved when
+ the user complete the tour). Note that Bootstrap Tour prepends the key with
+ <code>tour_</code> when saving the state.
+ </li>
+ <li>
+ <strong>value</strong>
+ The value of the state been saved. Can be the index of the
+ current step if the key is <code>current_step</code>, or
+ <code>yes</code> if the key is <code>end</code>.
+ </li>
+ </ul>
+ <p>A simple example if to send a post request to your server each
+ time there is a change:</p>
+{% highlight javascript %}
+var tour = new Tour({
+ afterSetState: function (key, value) {
+ $.post("/some/path", value);
+ }
+});
+{% endhighlight %}
+ </td>
+ <td><code>function (key, value) { }</code></td>
+ </tr>
+ <tr>
+ <td>onStart</td>
+ <td>Function</td>
+ <td>Function to execute when the tour starts.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onEnd</td>
+ <td>Function</td>
+ <td>Function to execute when the tour ends.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onShow</td>
+ <td>Function</td>
+ <td>Function to execute right before each step is shown.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onShown</td>
+ <td>Function</td>
+ <td>Function to execute right after each step is shown.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onHide</td>
+ <td>Function</td>
+ <td>Function to execute right before each step is hidden.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onHidden</td>
+ <td>Function</td>
+ <td>Function to execute right after each step is hidden.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onNext</td>
+ <td>Function</td>
+ <td>Function to execute when next step is called.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onPrev</td>
+ <td>Function</td>
+ <td>Function to execute when prev step is called.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr class="success">
+ <td>onPause <span class="label label-success">NEW</span></td>
+ <td>Function</td>
+ <td>Function to execute when pause is called. The second argument refers to the
+ remaining duration.</td>
+ <td><code>function (tour, duration) { }</code></td>
+ </tr>
+ <tr class="success">
+ <td>onResume <span class="label label-success">NEW</span></td>
+ <td>Function</td>
+ <td>Function to execute when resume is called. The second argument refers to the
+ remaining duration.</td>
+ <td><code>function (tour, duration) { }</code></td>
+ </tr>
+ </tbody>
+ </table>
+ <a id="step-options"></a>
+ <h3>Step Options</h3>
+{% highlight javascript %}
+tour.addStep({
+ path: "",
+ element: "",
+ placement: "right",
+ title: "",
+ content: "",
+ next: 0,
+ prev: 0,
+ animation: true,
+ container: "body",
+ backdrop: false,
+ backdropPadding: false,
+ redirect: true,
+ reflex: false,
+ orphan: false,
+ template: "",
+ onShow: function (tour) {},
+ onShown: function (tour) {},
+ onHide: function (tour) {},
+ onHidden: function (tour) {},
+ onNext: function (tour) {},
+ onPrev: function (tour) {},
+ onPause: function (tour) {},
+ onResume: function (tour) {}
+});
+{% endhighlight %}
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Description</th>
+ <th width="50%">Default</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>path</td>
+ <td>String or RegExp</td>
+ <td>Path to the page on which the step should be shown. This
+ allows you to build tours that span several pages!</td>
+ <td><code>''</code></td>
+ </tr>
+ <tr>
+ <td>element</td>
+ <td>String (jQuery selector)</td>
+ <td>HTML element on which the step popover should be shown.<br />
+ <em>If orphan is false, this option is required.</em></td>
+ <td><code>''</code></td>
+ </tr>
+ <tr>
+ <td>placement</td>
+ <td>String|Function</td>
+ <td>How to position the popover. Possible choices:
+ <code>'top'</code>,
+ <code>'bottom'</code>,
+ <code>'left'</code>,
+ <code>'right'</code>.
+ </td>
+ <td><code>'right'</code></td>
+ </tr>
+ <tr>
+ <td>title</td>
+ <td>String|Function</td>
+ <td>Step title</td>
+ <td><code>''</code></td>
+ </tr>
+ <tr>
+ <td>content</td>
+ <td>String|Function</td>
+ <td>Step content</td>
+ <td><code>''</code></td>
+ </tr>
+ <tr>
+ <td>next</td>
+ <td>Integer</td>
+ <td>Index of the step to show after this one, starting from
+ <code>0</code> for the first step of the
+ tour. <code>-1</code> to not show the link
+ to next step. By default, the next step (in the order you added
+ them) will be shown.<br />
+ <em>This option should be used in conjunction with
+ <code>prev</code>.</em></td>
+ <td><code>0</code></td>
+ </tr>
+ <tr>
+ <td>prev</td>
+ <td>Integer</td>
+ <td>Index of the step to show before this one, starting from
+ <code>0</code> for the first step of the
+ tour. <code>-1</code> to not show the link
+ to previous step. By default, the previous step (in the order you added
+ them) will be shown.<br />
+ <em>This option should be used in conjunction with
+ <code>next</code>.</em></td>
+ <td><code>0</code></td>
+ </tr>
+ <tr>
+ <td>animation</td>
+ <td>Boolean</td>
+ <td>Apply a css fade transition to the tooltip.</td>
+ <td><code>true</code></td>
+ </tr>
+ <tr>
+ <td>container</td>
+ <td>String (jQuery selector)</td>
+ <td>Attachment of popover. Pass an element to append the popover
+ to. By default the popover is appended after the 'element' above.
+ This option is particularly helpful for Internet Explorer.</td>
+ <td><code>'body'</code></td>
+ </tr>
+ <tr>
+ <td>backdrop</td>
+ <td>Boolean</td>
+ <td>Show a dark backdrop behind the popover and its element,
+ highlighting the current step.</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr class="success">
+ <td>backdropPadding <span class="label label-success">NEW</span></td>
+ <td>Boolean|Object</td>
+ <td>Add padding to the backdrop element that highlights the step element.<br>
+ It can be a number or a object containing optional <code>top</code>, <code>right</code>, <code>bottom</code> and <code>left</code> numbers.</td>
+ <td><code>0</code></td>
+ </tr>
+ <tr>
+ <td>redirect</td>
+ <td>Boolean|Function</td>
+ <td>Set a custom function to execute as redirect function.
+ The default redirect relies on the traditional
+ <code class="language-javascript">document.location.href</code></td>
+ <td><code>true</code></td>
+ </tr>
+ <tr class="success">
+ <td id="reflex">reflex <span class="label label-warning">UPDATED</span></td>
+ <td>Boolean|String</td>
+ <td>Enable the reflex mode: attach an handler on <code>click</code> on the step element to continue the tour.<br>
+ In order to bind the handler to a custom event, you can pass a string with its name.<br>
+ Also, the class <code>tour-step-element-reflex</code> is added to the element, as hook for your custom style (e.g: cursor: pointer).</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr>
+ <td>orphan</td>
+ <td>Boolean</td>
+ <td>Allow to show the step regardless whether its element is not set, is
+ not present in the page or is hidden. The step is fixed
+ positioned in the middle of the page.</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr class="success">
+ <td>duration <span class="label label-success">NEW</span></td>
+ <td>Boolean|String</td>
+ <td>Set a expiration time for the steps. When the step expires,
+ the next step is automatically shown. See it as a sort of guided, automatized tour
+ functionality. The value is specified in milliseconds</td>
+ <td><code>false</code></td>
+ </tr>
+ <tr>
+ <td>template</td>
+ <td>String|Function</td>
+ <td>String or function that returns a string of the HTML template for
+ the popovers. If you pass a Function, two parameters are available:
+ <strong>i</strong> is the position of step in the tour and
+ <strong>step</strong> is the object that contains all the other step
+ options.<br>
+ From version 0.5, the navigation template is included inside the
+ template so you can easily rewrite it. However, Bootstrap Tour maps the
+ <em>previous</em>, <em>next</em> and <em>end</em> logics to the elements
+ which have the related <code class="language-markup">data-role</code>
+ attribute. Therefore, you can also have multiple elements with the same
+ <code class="language-markup">data-role</code> attribute.
+ </td>
+ <td>
+{% highlight javascript %}
+"<div class='popover tour'>
+ <div class='arrow'></div>
+ <h3 class='popover-title'></h3>
+ <div class='popover-content'></div>
+ <div class='popover-navigation'>
+ <button class='btn btn-default' data-role='prev'>« Prev</button>
+ <span data-role='separator'>|</span>
+ <button class='btn btn-default' data-role='next'>Next »</button>
+ <button class='btn btn-default' data-role='end'>End tour</button>
+ </div>
+</div>"
+{% endhighlight %}
+ </td>
+ </tr>
+ <tr>
+ <td>onShow</td>
+ <td>Function</td>
+ <td>Function to execute right before the step is shown. It overrides the
+ global <code>onShow</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onShown</td>
+ <td>Function</td>
+ <td>Function to execute right after the step is shown. It overrides the
+ global <code>onShown</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onHide</td>
+ <td>Function</td>
+ <td>Function to execute right before the step is hidden. It overrides
+ the global <code>onHide</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onHidden</td>
+ <td>Function</td>
+ <td>Function to execute right after the step is hidden. It overrides the
+ global <code>onHidden</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onNext</td>
+ <td>Function</td>
+ <td>Function to execute when next step is called. It overrides the
+ global <code>onNext</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr>
+ <td>onPrev</td>
+ <td>Function</td>
+ <td>Function to execute when prev step is called. It overrides the global
+ <code>onPrev</code> option.</td>
+ <td><code>function (tour) { }</code></td>
+ </tr>
+ <tr class="success">
+ <td>onPause <span class="label label-success">NEW</span></td>
+ <td>Function</td>
+ <td>Function to execute when pause is called. The second argument refers to the
+ remaining duration. It overrides the global the
+ <code>onPause</code> option</td>
+ <td><code>function (tour, duration) { }</code></td>
+ </tr>
+ <tr class="success">
+ <td>onResume <span class="label label-success">NEW</span></td>
+ <td>Function</td>
+ <td>Function to execute when resume is called. The second argument refers to the
+ remaining duration. It overrides the global
+ <code>onResume</code> option</td>
+ <td><code>function (tour, duration) { }</code></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</section>
+
+<section id="methods">
+ <div class="container">
+ <div class="page-header">
+ <h2>Methods <small>Always in control</small></h2>
+ </div>
+ <!--<p>If, for some reasons, you want to force the tour to be
+ displayed, pass <code>true</code> to the <code>start()</code>
+ method.</p>-->
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>addSteps(<code>[]</code>)</td>
+ <td>Add multiple steps to the tour. Pass an array of objects.</td>
+ </tr>
+ <tr>
+ <td>addStep(<code>{}</code>)</td>
+ <td>Add single step to the tour. Pass an object.</td>
+ </tr>
+ <tr>
+ <td>init()</td>
+ <td>Initialize the tour. You must do it before calling start.</td>
+ </tr>
+ <tr>
+ <td>start(<code>true</code>)</td>
+ <td>Start the tour. Pass <code>true</code> to force the start.</td>
+ </tr>
+ <tr>
+ <td>restart()</td>
+ <td>Restart the tour after it ended.</td>
+ </tr>
+ <tr>
+ <td>end()</td>
+ <td>End the tour prematurely.</td>
+ </tr>
+ <tr>
+ <td>next()</td>
+ <td>Skip to the next step.</td>
+ </tr>
+ <tr>
+ <td>prev()</td>
+ <td>Go back to the previous step.</td>
+ </tr>
+ <tr class="warning">
+ <td>goTo(<code>i</code>)
+ <span class="label label-warning">UPDATED</span></td>
+ <td>Skip to a specific step. Pass <code>i</code> as the
+ index of the step in the tour (0-based).<br>
+ <em>From version 0.7.0, the method has been renamed since some Javascript compilers
+ are confused by the property name <strong>goto</strong>, which is a reserved word)
+ </em>.
+ </td>
+ </tr>
+ <tr>
+ <td>pause()</td>
+ <td>Pause the duration timer. It works only if tour or current step has duration.</td>
+ </tr>
+ <tr>
+ <td>resume()</td>
+ <td>Resume the duration timer. It works only if tour or current step has duration.</td>
+ </tr>
+ <tr>
+ <td>ended()</td>
+ <td>Verify if the tour ended. Returns boolean.</td>
+ </tr>
+ <tr>
+ <td>getStep(<code>i</code>)</td>
+ <td>Get the step object. Pass <code>i</code> as the index
+ of the step in the tour (0-based).</td>
+ </tr>
+ <tr>
+ <td>getCurrentStep()</td>
+ <td>Get the index of the current step.</td>
+ </tr>
+ <tr>
+ <td>setCurrentStep(<code>i</code>)</td>
+ <td>Override the current step. Pass <code>i</code>
+ as the index of the step in the tour (0-based).</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</section>
+
+<section id="multipage">
+ <div class="container">
+ <div class="page-header">
+ <h2>Multipage
+ <small>How to traverse across pages</small>
+ </h2>
+ </div>
+ <p>Bootstrap Tour can be used to create tours that span multiple pages. If you have URLs for each page that have
+ unique paths, and the dependencies are loaded on each page, you can easily create a tour like so:
+ </p>
+{% highlight javascript %}
+var tour = new Tour({
+ steps: [
+ {
+ element: "#my-element",
+ title: "Title of my step",
+ content: "Content of my step"
+ },
+ {
+ element: "#my-other-element",
+ title: "Title of my step",
+ content: "Content of my step",
+ path: "/url/to/go/to/"
+ }
+ ]
+});
+{% endhighlight %}
+
+ <p>It's that simple.</p>
+
+ <p>If you do not know the URL you wish to go to because it contains a different value per user or per
+ instance, you can use a regular expression as the <code>path</code> attribute and set the redirect
+ attribute to a function that performs the redirect.
+ </p>
+ <p>For example:</p>
+
+{% highlight javascript %}
+var tour = new Tour({
+ steps: [
+ {
+ element: "#my-element",
+ title: "Title of my step",
+ content: "Content of my step",
+ redirect: function(){
+ document.location.href = '/url/' + userId;
+ };
+ },
+ {
+ element: "#my-other-element",
+ title: "Title of my step",
+ content: "Content of my step",
+ path: Regexp("/\/url\/[^/]+/i")
+ }
+ ]
+});
+{% endhighlight %}
+
+ <p>Finally, if you are only using GET parameters to define different pages, and wish to redirect using those
+ parameters, you may run into the problem that Bootstrap Tour will consider the path of the two steps to be
+ identical. For example, you cannot use the path parameter to go from your homepage at <code>/</code> to a
+ search results page at <code>/?q=foo</code>, because from Bootstrap Tour's perspective, those are the same
+ location (<code>/</code>).
+ </p>
+ <p>To work around this limitation, you can set the <code>onNext</code> attribute a function that returns a
+ promise.</p>
+ <p>For example:</p>
+
+{% highlight javascript %}
+var tour = new Tour({
+ steps: [
+ {
+ element: "#my-element",
+ title: "Title of my step",
+ content: "Content of my step",
+ onNext: function(){
+ document.location.href = '/?q=foo';
+ return (new jQuery.Deferred()).promise();
+ };
+ },
+ {
+ element: "#my-other-element",
+ title: "Title of my step",
+ content: "Content of my step",
+ }
+ ]
+});
+{% endhighlight %}
+
+ <p>Doing this will prevent the next step from popping up while the redirect is being completed in the
+ <code>onNext</code> function.
+ </p>
+ </div>
+</section>
diff --git a/src/docs/assets/css/bootstrap-tour.css b/src/docs/assets/css/bootstrap-tour.css
new file mode 100644
index 0000000..b730758
--- /dev/null
+++ b/src/docs/assets/css/bootstrap-tour.css
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+.tour-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1100;
+ background-color: #000;
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+.tour-step-backdrop {
+ position: relative;
+ z-index: 1101;
+ background: inherit;
+}
+.tour-step-backdrop > td {
+ position: relative;
+ z-index: 1101;
+}
+.tour-step-background {
+ position: absolute !important;
+ z-index: 1100;
+ background: inherit;
+ border-radius: 6px;
+}
+.popover[class*="tour-"] {
+ z-index: 1100;
+}
+.popover[class*="tour-"] .popover-navigation {
+ padding: 9px 14px;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ float: right;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"],
+.popover[class*="tour-"] .popover-navigation *[data-role="next"],
+.popover[class*="tour-"] .popover-navigation *[data-role="end"] {
+ cursor: pointer;
+}
+.popover[class*="tour-"] .popover-navigation *[data-role="prev"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="next"].disabled,
+.popover[class*="tour-"] .popover-navigation *[data-role="end"].disabled {
+ cursor: default;
+}
+.popover[class*="tour-"].orphan {
+ position: fixed;
+ margin-top: 0;
+}
+.popover[class*="tour-"].orphan .arrow {
+ display: none;
+}
diff --git a/src/docs/assets/css/bootstrap-tour.docs.css b/src/docs/assets/css/bootstrap-tour.docs.css
new file mode 100644
index 0000000..2c05e71
--- /dev/null
+++ b/src/docs/assets/css/bootstrap-tour.docs.css
@@ -0,0 +1,61 @@
+body {
+ padding-bottom: 50px;
+}
+
+#github {
+ display: block;
+ position: absolute;
+ width: 150px;
+ height: 150px;
+ top: 0;
+ right: 0;
+ z-index: 1050;
+}
+
+.navbar {
+ margin-bottom: 0;
+}
+
+.masthead {
+ position: relative;
+ padding: 30px 15px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 0 rgba(0,0,0,.1);
+ background-color: #3276b1;
+ background-image: -webkit-linear-gradient(top, #285e8e 0%, #3276b1 100%);
+ background-image: linear-gradient(to bottom, #285e8e 0%, #3276b1 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#285e8e', endColorstr='#3276b1', GradientType=0);
+}
+.masthead small {
+ font-size: 80%;
+ color: #ccc;
+}
+
+/*
+ * Social buttons
+ *
+ * Twitter and GitHub social action buttons (for homepage and footer).
+ */
+
+.social {
+ display: inline-block;
+ margin-top: 40px;
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+}
+.social li {
+ display: inline-block;
+ line-height: 1;
+ padding: 5px 8px;
+}
+.social .tweet-btn a {
+ width: 98px !important;
+}
+/* Style the GitHub buttons via CSS instead of inline attributes */
+.github-btn {
+ border: 0;
+ overflow: hidden;
+}
diff --git a/src/docs/assets/fonts/glyphicons-halflings-regular.eot b/src/docs/assets/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..87eaa43
Binary files /dev/null and b/src/docs/assets/fonts/glyphicons-halflings-regular.eot differ
diff --git a/src/docs/assets/fonts/glyphicons-halflings-regular.svg b/src/docs/assets/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..5fee068
--- /dev/null
+++ b/src/docs/assets/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,228 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph />
+<glyph />
+<glyph unicode=" " />
+<glyph unicode="*" d="M1100 500h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200z" />
+<glyph unicode="+" d="M1100 400h-400v-400h-300v400h-400v300h400v400h300v-400h400v-300z" />
+<glyph unicode=" " />
+<glyph unicode=" " horiz-adv-x="652" />
+<glyph unicode=" " horiz-adv-x="1304" />
+<glyph unicode=" " horiz-adv-x="652" />
+<glyph unicode=" " horiz-adv-x="1304" />
+<glyph unicode=" " horiz-adv-x="434" />
+<glyph unicode=" " horiz-adv-x="326" />
+<glyph unicode=" " horiz-adv-x="217" />
+<glyph unicode=" " horiz-adv-x="217" />
+<glyph unicode=" " horiz-adv-x="163" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="72" />
+<glyph unicode=" " horiz-adv-x="260" />
+<glyph unicode=" " horiz-adv-x="326" />
+<glyph unicode="€" d="M800 500h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257 q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406z" />
+<glyph unicode="−" d="M1100 700h-900v-300h900v300z" />
+<glyph unicode="☁" d="M178 300h750q120 0 205 86t85 208q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57z" />
+<glyph unicode="✉" d="M1200 1100h-1200l600 -603zM300 600l-300 -300v600zM1200 900v-600l-300 300zM800 500l400 -400h-1200l400 400l200 -200z" />
+<glyph unicode="✏" d="M1101 889l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13l-94 -97zM401 189l614 614l-214 214l-614 -614zM-13 -13l333 112l-223 223z" />
+<glyph unicode="" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="" d="M700 100h300v-100h-800v100h300v550l-500 550h1200l-500 -550v-550z" />
+<glyph unicode="" d="M1000 934v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q17 -55 85.5 -75.5t147.5 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7q-79 -25 -122.5 -82t-25.5 -112t86 -75.5t147 5.5 q65 21 109 69t44 90v606z" />
+<glyph unicode="" d="M913 432l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -142 -78 -261zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
+<glyph unicode="" d="M649 949q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5t-94 124.5t-33.5 117.5q0 64 28 123t73 100.5t104.5 64t119 20.5 t120 -38.5t104.5 -104.5z" />
+<glyph unicode="" d="M791 522l145 -449l-384 275l-382 -275l146 447l-388 280h479l146 400h2l146 -400h472zM168 71l2 1z" />
+<glyph unicode="" d="M791 522l145 -449l-384 275l-382 -275l146 447l-388 280h479l146 400h2l146 -400h472zM747 331l-74 229l193 140h-235l-77 211l-78 -211h-239l196 -142l-73 -226l192 140zM168 71l2 1z" />
+<glyph unicode="" d="M1200 143v-143h-1200v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100z" />
+<glyph unicode="" d="M1200 1100v-1100h-1200v1100h1200zM200 1000h-100v-100h100v100zM900 1000h-600v-400h600v400zM1100 1000h-100v-100h100v100zM200 800h-100v-100h100v100zM1100 800h-100v-100h100v100zM200 600h-100v-100h100v100zM1100 600h-100v-100h100v100zM900 500h-600v-400h600 v400zM200 400h-100v-100h100v100zM1100 400h-100v-100h100v100zM200 200h-100v-100h100v100zM1100 200h-100v-100h100v100z" />
+<glyph unicode="" d="M500 1050v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5zM1100 1050v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400 q21 0 35.5 -14.5t14.5 -35.5zM500 450v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5zM1100 450v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-40 [...]
+<glyph unicode="" d="M300 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM700 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5zM1100 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM300 650v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-20 [...]
+<glyph unicode="" d="M300 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5zM1200 1050v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h700 q21 0 35.5 -14.5t14.5 -35.5zM300 450v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-200q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM1200 650v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-70 [...]
+<glyph unicode="" d="M448 34l818 820l-212 212l-607 -607l-206 207l-212 -212z" />
+<glyph unicode="" d="M882 106l-282 282l-282 -282l-212 212l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282z" />
+<glyph unicode="" d="M913 432l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -142 -78 -261zM507 363q137 0 233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5t-234 -97t-97 -233 t97 -233t234 -97zM600 800h100v-200h-100v-100h-200v100h-100v200h100v100h200v-100z" />
+<glyph unicode="" d="M913 432l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 299q-120 -77 -261 -77q-200 0 -342 142t-142 342t142 342t342 142t342 -142t142 -342q0 -141 -78 -262zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 801v-200h400v200h-400z" />
+<glyph unicode="" d="M700 750v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5zM800 975v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123 t-123 184t-45.5 224.5q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155z" />
+<glyph unicode="" d="M1200 1h-200v1200h200v-1200zM900 1h-200v800h200v-800zM600 1h-200v500h200v-500zM300 301h-200v-300h200v300z" />
+<glyph unicode="" d="M488 183l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5 q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81 [...]
+<glyph unicode="" d="M900 1100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100zM800 1100v100h-300v-100h300zM200 900h900v-800q0 -41 -29.5 -71 t-70.5 -30h-700q-41 0 -70.5 30t-29.5 71v800zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
+<glyph unicode="" d="M1301 601h-200v-600h-300v400h-300v-400h-300v600h-200l656 644z" />
+<glyph unicode="" d="M600 700h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18v1150q0 11 7 18t18 7h475v-500zM1000 800h-300v300z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM600 600h200 v-100h-300v400h100v-300z" />
+<glyph unicode="" d="M721 400h-242l-40 -400h-539l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538zM712 500l-27 300h-170l-27 -300h224z" />
+<glyph unicode="" d="M1100 400v-400h-1100v400h490l-290 300h200v500h300v-500h200l-290 -300h490zM988 300h-175v-100h175v100z" />
+<glyph unicode="" d="M600 1199q122 0 233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233t47.5 233t127.5 191t191 127.5t233 47.5zM600 1012q-170 0 -291 -121t-121 -291t121 -291t291 -121t291 121 t121 291t-121 291t-291 121zM700 600h150l-250 -300l-250 300h150v300h200v-300z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM850 600h-150 v-300h-200v300h-150l250 300z" />
+<glyph unicode="" d="M0 500l200 700h800q199 -700 200 -700v-475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18v475zM903 1000h-606l-97 -500h200l50 -200h300l50 200h200z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5zM797 598 l-297 -201v401z" />
+<glyph unicode="" d="M1177 600h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123t-123 -184t-45.5 -224.5t45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123 t123 184t45.5 224.5z" />
+<glyph unicode="" d="M700 800l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400zM500 400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122l-145 -145v400h400z" />
+<glyph unicode="" d="M100 1200v-1200h1100v1200h-1100zM1100 100h-900v900h900v-900zM400 800h-100v100h100v-100zM1000 800h-500v100h500v-100zM400 600h-100v100h100v-100zM1000 600h-500v100h500v-100zM400 400h-100v100h100v-100zM1000 400h-500v100h500v-100zM400 200h-100v100h100v-100 zM1000 300h-500v-100h500v100z" />
+<glyph unicode="" d="M200 0h-100v1100h100v-1100zM1100 600v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5z" />
+<glyph unicode="" d="M1200 275v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5t-49.5 -227v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50 q11 0 18 7t7 18zM400 480v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14zM1000 480v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14z" />
+<glyph unicode="" d="M0 800v-400h300l300 -200v800l-300 -200h-300zM971 600l141 -141l-71 -71l-141 141l-141 -141l-71 71l141 141l-141 141l71 71l141 -141l141 141l71 -71z" />
+<glyph unicode="" d="M0 800v-400h300l300 -200v800l-300 -200h-300zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
+<glyph unicode="" d="M974 186l6 8q142 178 142 405q0 230 -144 408l-6 8l-83 -64l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8zM300 801l300 200v-800l-300 200h-300v400h300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257z" />
+<glyph unicode="" d="M100 700h400v100h100v100h-100v300h-500v-600h100v100zM1200 700v500h-600v-200h100v-300h200v-300h300v200h-200v100h200zM100 1100h300v-300h-300v300zM800 800v300h300v-300h-300zM200 900h100v100h-100v-100zM900 1000h100v-100h-100v100zM300 600h-100v-100h-200 v-500h500v500h-200v100zM900 200v-100h-200v100h-100v100h100v200h-200v100h300v-300h200v-100h-100zM400 400v-300h-300v300h300zM300 200h-100v100h100v-100zM1100 300h100v-100h-100v100zM600 100h100v-100h-100v100zM1200 100v [...]
+<glyph unicode="" d="M100 1200h-100v-1000h100v1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 1200v-1000h-200v1000h200zM400 100v-100h-300v100h300zM500 91h100v-91h-100v91zM700 91h100v-91h-100v91zM1100 91v-91h-200v91h200z " />
+<glyph unicode="" d="M1200 500l-500 -500l-699 700v475q0 10 7.5 17.5t17.5 7.5h474zM320 882q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71t29 -71q30 -30 71.5 -30t71.5 30z" />
+<glyph unicode="" d="M1201 500l-500 -500l-699 700v475q0 11 7 18t18 7h474zM1501 500l-500 -500l-50 50l450 450l-700 700h100zM320 882q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71t30 -71q29 -30 71 -30t71 30z" />
+<glyph unicode="" d="M1200 1200v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900v1025l175 175h925z" />
+<glyph unicode="" d="M947 829l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18l-94 -346l40 -124h592zM1200 800v-700h-200v200h-800v-200h-200v700h200l100 -200h600l100 200h200zM881 176l38 -152q2 -10 -3.5 -17t-15.5 -7h-600q-10 0 -15.5 7t-3.5 17l38 152q2 10 11.5 17t19.5 7 h500q10 0 19.5 -7t11.5 -17z" />
+<glyph unicode="" d="M1200 0v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417zM416 521l178 457l46 -140l116 -317 h-340z" />
+<glyph unicode="" d="M100 1199h471q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111t-162 -38.5h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21 t-29 14t-49 14.5v70zM400 1079v-379h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400z" />
+<glyph unicode="" d="M877 1200l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425z" />
+<glyph unicode="" d="M1150 1200h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49v300h150h700zM100 1000v-800h75l-125 -167l-125 167h75v800h-75l125 167 l125 -167h-75z" />
+<glyph unicode="" d="M950 1201h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50v300h150h700zM200 101h800v75l167 -125l-167 -125v75h-800v-75l-167 125l167 125 v-75z" />
+<glyph unicode="" d="M700 950v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35zM1100 650v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h1000 q21 0 35.5 15t14.5 35zM900 350v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35zM1200 50v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -3 [...]
+<glyph unicode="" d="M1000 950v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35zM1200 650v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h1100 q21 0 35.5 15t14.5 35zM1000 350v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35zM1200 50v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 [...]
+<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 [...]
+<glyph unicode="" d="M0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 - [...]
+<glyph unicode="" d="M0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 [...]
+<glyph unicode="" d="M400 1100h-100v-1100h100v1100zM700 950v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35zM1100 650v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15 h500q20 0 35 15t15 35zM100 425v75h-201v100h201v75l166 -125zM900 350v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35zM1200 50v100q0 21 -15 35.5t-35 [...]
+<glyph unicode="" d="M201 950v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35zM801 1100h100v-1100h-100v1100zM601 650v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15 h500q20 0 35 15t15 35zM1101 425v75h200v100h-200v75l-167 -125zM401 350v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35zM701 50v100q0 21 -15 35.5t-35 [...]
+<glyph unicode="" d="M900 925v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="" d="M1200 1056v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31zM1100 1000h-1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500zM476 750q0 -56 -39 -95t-95 -39t-95 39t-39 95t39 95t95 39t95 -39 t39 -95z" />
+<glyph unicode="" d="M600 1213q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262q0 124 60.5 231.5t165 172t226.5 64.5zM599 514q107 0 182.5 75.5t75.5 182.5t-75.5 182 t-182.5 75t-182 -75.5t-75 -181.5q0 -107 75.5 -182.5t181.5 -75.5z" />
+<glyph unicode="" d="M600 1199q122 0 233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233t47.5 233t127.5 191t191 127.5t233 47.5zM600 173v854q-176 0 -301.5 -125t-125.5 -302t125.5 -302t301.5 -125z " />
+<glyph unicode="" d="M554 1295q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 138.5t-64 210.5q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5zM455 296q-7 6 -18 17 t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156q14 -82 59.5 -136t136.5 -80z" />
+<glyph unicode="" d="M1108 902l113 113l-21 85l-92 28l-113 -113zM1100 625v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125zM436 341l161 50l412 412l-114 113l-405 -405z" />
+<glyph unicode="" d="M1100 453v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5z M813 431l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209z" />
+<glyph unicode="" d="M1100 569v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5h300q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69z M625 348l566 567l-136 137l-430 -431l-147 147l-136 -136z" />
+<glyph unicode="" d="M900 303v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198l-300 300l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296z" />
+<glyph unicode="" d="M900 0l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100z" />
+<glyph unicode="" d="M1200 0l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100z" />
+<glyph unicode="" d="M1200 0l-500 488v-488l-564 550l564 550v-487l500 487v-1100z" />
+<glyph unicode="" d="M1100 550l-900 550v-1100z" />
+<glyph unicode="" d="M500 150v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM900 150v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -21 14.5 -35.5t35.5 -14.5h200 q21 0 35.5 14.5t14.5 35.5z" />
+<glyph unicode="" d="M1100 150v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35z" />
+<glyph unicode="" d="M500 0v488l-500 -488v1100l500 -487v487l564 -550z" />
+<glyph unicode="" d="M1050 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488l-500 -488v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="" d="M650 1064l-550 -564h1100zM1200 350v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="" d="M777 7l240 240l-353 353l353 353l-240 240l-592 -594z" />
+<glyph unicode="" d="M513 -46l-241 240l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 900v-200h-200v-200h200v-200h200v200h200v200h-200v200h-200z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM300 700v-200h600v200h-600z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM247 741l141 -141l-142 -141l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141 l-141 142z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM546 623l-102 102l-174 -174l276 -277l411 411l-175 174z" />
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 500h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3 q-105 0 -172 -56t-67 -183h144q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5zM500 400 [...]
+<glyph unicode="" d="M600 1197q162 0 299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5t80 299.5t217.5 217.5t299.5 80zM500 900v-100h200v100h-200zM400 700v-100h100v-200h-100v-100h400v100h-100v300h-300z" />
+<glyph unicode="" d="M1200 700v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194v200h194q15 60 36 104.5t55.5 86t88 69t126.5 40.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203zM700 500v-206q149 48 201 206h-201v200h200 q-25 74 -76 127.5t-124 76.5v-204h-200v203q-75 -24 -130 -77.5t-79 -125.5h209v-200h-210q24 -73 79.5 -127.5t130.5 -78.5v206h200z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM844 735 l-135 -135l135 -135l-109 -109l-135 135l-135 -135l-109 109l135 135l-135 135l109 109l135 -135l135 135z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM896 654 l-346 -345l-228 228l141 141l87 -87l204 205z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM248 385l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5q0 -115 62 -215zM955 809l-564 -564q97 -59 209 -59q171 0 292.5 121.5 t121.5 292.5q0 112 -59 209z" />
+<glyph unicode="" d="M1200 400h-600v-301l-600 448l600 453v-300h600v-300z" />
+<glyph unicode="" d="M600 400h-600v300h600v300l600 -453l-600 -448v301z" />
+<glyph unicode="" d="M1098 600h-298v-600h-300v600h-296l450 600z" />
+<glyph unicode="" d="M998 600l-449 -600l-445 600h296v600h300v-600h298z" />
+<glyph unicode="" d="M600 199v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453z" />
+<glyph unicode="" d="M1200 1200h-400l129 -129l-294 -294l142 -142l294 294l129 -129v400zM565 423l-294 -294l129 -129h-400v400l129 -129l294 294z" />
+<glyph unicode="" d="M871 730l129 -130h-400v400l129 -129l295 295l142 -141zM200 600h400v-400l-129 130l-295 -295l-142 141l295 295z" />
+<glyph unicode="" d="M600 1177q118 0 224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5t45.5 224.5t123 184t184 123t224.5 45.5zM686 549l58 302q4 20 -8 34.5t-33 14.5h-207q-20 0 -32 -14.5t-8 -34.5 l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5zM700 400h-200v-100h200v100z" />
+<glyph unicode="" d="M1200 900h-111v6t-1 15t-3 18l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6h-111v-100h100v-200h400v300h200v-300h400v200h100v100z M731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269zM481 900h-281q-3 0 14 48t35 96l18 47zM100 0h400v400h-400v-400zM700 400h400v-400h-400v400z" />
+<glyph unicode="" d="M0 121l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55l-201 -202 v143zM692 611q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5t86.5 76.5q55 66 36 [...]
+<glyph unicode="" d="M1261 600l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30l-26 40l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5 t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30zM600 240q64 0 123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54q49 -74 49 -163q0 -124 -88 - [...]
+<glyph unicode="" d="M906 1200l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43l-26 40l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148zM1261 600l-26 -40q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5 t-124 -100t-146.5 -79l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52zM513 264l37 141q-107 18 -178.5 101.5t-71.5 193.5q0 85 46 158 [...]
+<glyph unicode="" d="M-47 0h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 33 -48 36t-48 -29l-642 -1066q-21 -32 -7.5 -66t50.5 -34zM700 200v100h-200v-100h-345l445 723l445 -723h-345zM700 700h-200v-100l100 -300l100 300v100z" />
+<glyph unicode="" d="M800 711l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -21 -13 -29t-32 1l-94 78h-222l-94 -78q-19 -9 -32 -1t-13 29v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41q0 20 11 44.5t26 38.5 l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339z" />
+<glyph unicode="" d="M941 800l-600 -600h-341v200h259l600 600h241v198l300 -295l-300 -300v197h-159zM381 678l141 142l-181 180h-341v-200h259zM1100 598l300 -295l-300 -300v197h-241l-181 181l141 142l122 -123h159v198z" />
+<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="" d="M400 900h-300v300h300v-300zM1100 900h-300v300h300v-300zM1100 800v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5t-58 109.5t-31.5 116t-15 104t-3 83v200h300v-250q0 -113 6 -145 q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300z" />
+<glyph unicode="" d="M902 184l226 227l-578 579l-580 -579l227 -227l352 353z" />
+<glyph unicode="" d="M650 218l578 579l-226 227l-353 -353l-352 353l-227 -227z" />
+<glyph unicode="" d="M1198 400v600h-796l215 -200h381v-400h-198l299 -283l299 283h-200zM-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196z" />
+<glyph unicode="" d="M1050 1200h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35 q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43l-100 475q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5z" />
+<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="" d="M201 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000zM1501 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
+<glyph unicode="" d="M900 303v197h-600v-197l-300 297l300 298v-198h600v198l300 -298z" />
+<glyph unicode="" d="M31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM100 300h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM900 200h-100v-100h100v100z M1100 200h-100v-100h100v100z" />
+<glyph unicode="" d="M1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35zM325 800l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35q-56 337 -56 351v250v5 q0 13 0.5 18.5t2.5 13t8 10.5t15 3h200zM-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5z" />
+<glyph unicode="" d="M445 1180l-45 -233l-224 78l78 -225l-233 -44l179 -156l-179 -155l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180z" />
+<glyph unicode="" d="M700 1200h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400q0 -75 100 -75h61q123 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5zM700 925l-50 -225h450 v-125l-250 -375h-214l-136 100h-100v375l150 212l100 213h50v-175zM0 800v-600h200v600h-200z" />
+<glyph unicode="" d="M700 0h-50q-27 0 -51 20t-38 48l-96 198l-145 196q-20 26 -20 63v400q0 75 100 75h61q123 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5zM200 400h-200v600h200 v-600zM700 275l-50 225h450v125l-250 375h-214l-136 -100h-100v-375l150 -212l100 -213h50v175z" />
+<glyph unicode="" d="M364 873l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM408 792v-503 l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83zM208 200h-200v600h200v-600z" />
+<glyph unicode="" d="M475 1104l365 -230q7 -4 16.5 -10.5t26 -26t16.5 -36.5v-526q0 -13 -85.5 -93.5t-93.5 -80.5h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-84 0 -139 39t-55 111t54 110t139 37h302l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6zM370 946 l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100h222q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l106 89v502l-342 237zM1199 201h-200v600h200v-600z" />
+<glyph unicode="" d="M1100 473v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90zM911 400h-503l-236 339 l83 86l183 -146q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6v7.5v7v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294zM1000 200v-200h-600v200h600z" />
+<glyph unicode="" d="M305 1104v200h600v-200h-600zM605 310l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15l-230 -362q-15 -31 7 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85l-1 -302q0 -84 38.5 -138t110.5 -54t111 55t39 139v106z M905 804v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146l-83 86l237 339h503z" />
+<glyph unicode="" d="M603 1195q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM598 701h-298v-201h300l-2 -194l402 294l-402 298v-197z" />
+<glyph unicode="" d="M597 1195q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5zM200 600l400 -294v194h302v201h-300v197z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM300 600h200v-300h200v300h200l-300 400z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM500 900v-300h-200l300 -400l300 400h-200v300h-200z" />
+<glyph unicode="" d="M603 1195q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5zM627 1101q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6 q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59 [...]
+<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70 [...]
+<glyph unicode="" d="M1100 1200v-100h-1000v100h1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
+<glyph unicode="" d="M329 729l142 142l-200 200l129 129h-400v-400l129 129zM1200 1200v-400l-129 129l-200 -200l-142 142l200 200l-129 129h400zM271 129l129 -129h-400v400l129 -129l200 200l142 -142zM1071 271l129 129v-400h-400l129 129l-200 200l142 142z" />
+<glyph unicode="" d="M596 1192q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM596 1010q-171 0 -292.5 -121.5t-121.5 -292.5q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5zM455 905 q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5t16 38.5t39 16.5zM708 821l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t- [...]
+<glyph unicode="" d="M649 54l-16 22q-90 125 -293 323q-71 70 -104.5 105.5t-77 89.5t-61 99t-17.5 91q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-203 -198 -293 -323zM844 524l12 12 q64 62 97.5 97t64.5 79t31 72q0 71 -48 119t-105 48q-74 0 -132 -82l-118 -171l-114 174q-51 79 -123 79q-60 0 -109.5 -49t-49.5 -118q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203z" />
+<glyph unicode="" d="M476 406l19 -17l105 105l-212 212l389 389l247 -247l-95 -96l18 -18q46 -46 77 -99l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159q0 -93 66 -159zM123 193l141 -141q66 -66 159 -66q95 0 159 66 l283 283q66 66 66 159t-66 159l-141 141q-12 12 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159q0 -94 66 -160z" />
+<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM900 1000h-600v-700h600v700zM600 46q43 0 73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5t-73.5 -30.5t-30.5 -73.5 t30.5 -73.5t73.5 -30.5z" />
+<glyph unicode="" d="M700 1029v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5h139q5 -77 48.5 -126.5t117.5 -64.5v335l-27 7q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5 t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5zM600 755v274q-61 -8 -97.5 -37.5t-36.5 -102.5q0 -29 8 -51t16.5 -34t29.5 -22.5t [...]
+<glyph unicode="" d="M866 300l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5t-30 142.5h-221v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5 t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -11 2.5 -24.5t5.5 -24t9.5 -26.5t10.5 -25t14 -27.5t14 -25.5t15.5 -27t13.5 -24h242v-100 [...]
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1200l298 -300h-198v-900h-200v900h-198z" />
+<glyph unicode="" d="M400 300h198l-298 -300l-298 300h198v900h200v-900zM1000 1200v-500h-100v100h-100v-100h-100v500h300zM901 1100h-100v-200h100v200zM700 500h300v-200h-99v-100h-100v100h99v100h-200v100zM800 100h200v-100h-300v200h100v-100z" />
+<glyph unicode="" d="M400 300h198l-298 -300l-298 300h198v900h200v-900zM1000 1200v-200h-99v-100h-100v100h99v100h-200v100h300zM800 800h200v-100h-300v200h100v-100zM700 500h300v-500h-100v100h-100v-100h-100v500zM801 200h100v200h-100v-200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1100h-100v100h200v-500h-100v400zM1100 500v-500h-100v100h-200v400h300zM1001 400h-100v-200h100v200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM1100 1200v-500h-100v100h-200v400h300zM1001 1100h-100v-200h100v200zM900 400h-100v100h200v-500h-100v400z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="" d="M300 0l298 300h-198v900h-200v-900h-198zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="" d="M400 1100h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5v300q0 165 117.5 282.5t282.5 117.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5 t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="" d="M700 0h-300q-163 0 -281.5 117.5t-118.5 282.5v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5 t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5zM400 800v-500l333 250z" />
+<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM900 300v500q0 41 -29.5 70.5t-70.5 29.5h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5zM800 700h-500l250 -333z" />
+<glyph unicode="" d="M1100 700v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5zM900 300v500q0 41 -29.5 70.5t-70.5 29.5h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5 t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5zM550 733l-250 -333h500z" />
+<glyph unicode="" d="M500 1100h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200zM700 550l-400 -350v200h-300v300h300v200z" />
+<glyph unicode="" d="M403 2l9 -1q13 0 26 16l538 630q15 19 6 36q-8 18 -32 16h-300q1 4 78 219.5t79 227.5q2 17 -6 27l-8 8h-9q-16 0 -25 -15q-4 -5 -98.5 -111.5t-228 -257t-209.5 -238.5q-17 -19 -7 -40q10 -19 32 -19h302q-155 -438 -160 -458q-5 -21 4 -32z" />
+<glyph unicode="" d="M800 200h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185zM900 200v200h-300v300h300v200l400 -350z" />
+<glyph unicode="" d="M1200 700l-149 149l-342 -353l-213 213l353 342l-149 149h500v-500zM1022 571l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5v-300 q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM600 794 q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="" d="M700 800v400h-300v-400h-300l445 -500l450 500h-295zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M400 700v-300h300v300h295l-445 500l-450 -500h300zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M405 400l596 596l-154 155l-442 -442l-150 151l-155 -155zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M409 1103l-97 97l-212 -212l97 -98zM650 861l-149 149l-212 -212l149 -149l-238 -248h700v699zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M539 950l-149 -149l212 -212l149 148l248 -237v700h-699zM297 709l-97 -97l212 -212l98 97zM25 300h1048q11 0 19 -7.5t8 -17.5v-275h-1100v275q0 11 7 18t18 7zM1000 200h-100v-50h100v50z" />
+<glyph unicode="" d="M1200 1199v-1079l-475 272l-310 -393v416h-392zM1166 1148l-672 -712v-226z" />
+<glyph unicode="" d="M1100 1000v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1200h-100v-200h100v200z" />
+<glyph unicode="" d="M578 500h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120zM700 1200h-100v-200h100v200zM1300 538l-475 -476l-244 244l123 123l120 -120l353 352z" />
+<glyph unicode="" d="M529 500h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170zM700 1200h-100v-200h100v200zM1167 6l-170 170l-170 -170l-127 127l170 170l-170 170l127 127l170 -170l170 170l127 -128 l-170 -169l170 -170z" />
+<glyph unicode="" d="M700 500h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200zM700 1000h-100v200h100v-200zM1000 600h-200v-300h-200l300 -300l300 300h-200v300z" />
+<glyph unicode="" d="M602 500h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200zM700 1000h-100v200h100v-200zM1000 300h200l-300 300l-300 -300h200v-300h200v300z" />
+<glyph unicode="" d="M1200 900v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h1200zM0 800v-550q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200zM100 500h400v-200h-400v200z" />
+<glyph unicode="" d="M500 1000h400v198l300 -298l-300 -298v198h-400v200zM100 800v200h100v-200h-100zM400 800h-100v200h100v-200zM700 300h-400v-198l-300 298l300 298v-198h400v-200zM800 500h100v-200h-100v200zM1000 500v-200h100v200h-100z" />
+<glyph unicode="" d="M1200 50v1106q0 31 -18 40.5t-44 -7.5l-276 -117q-25 -16 -43.5 -50.5t-18.5 -65.5v-359q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5zM550 1200l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447l-100 203v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300z" />
+<glyph unicode="" d="M1100 106v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394 q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5z" />
+<glyph unicode="" d="M675 1000l-100 100h-375l-100 -100h400l200 -200v-98l295 98h105v200h-425zM500 300v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5zM100 800h300v-200h-300v200zM700 565l400 133 v-163l-400 -133v163zM100 500h300v-200h-300v200zM805 300l295 98v-298h-425l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="" d="M179 1169l-162 -162q-1 -11 -0.5 -32.5t16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q16 17 13 40.5t-22 37.5l-192 136q-19 14 -45 12t-42 -19l-119 -118q-143 103 -267 227q-126 126 -227 268l118 118 q17 17 20 41.5t-11 44.5l-139 194q-14 19 -36.5 22t-40.5 -14z" />
+<glyph unicode="" d="M1200 712v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40t-53.5 -36.5t-31 -27.5l-9 -10v-200q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38 t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5zM800 650l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -15 -35.5t-35 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 [...]
+<glyph unicode="" d="M175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250zM1200 100v-100h-1100v100h1100z" />
+<glyph unicode="" d="M600 1100h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300v1000q0 41 29.5 70.5t70.5 29.5zM1000 800h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300v700q0 41 29.5 70.5t70.5 29.5zM400 0v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400h300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-300h200v-100h-200v-100h300v300h-200v100h200v100h-300zM800 800h-200v-500h200v100h100v300h-100 v100zM800 700v-300h-100v300h100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM400 600h-100v200h-100v-500h100v200h100v-200h100v500h-100v-200zM800 800h-200v-500h200v100h100v300h-100 v100zM800 700v-300h-100v300h100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-500h300v100h-200v300h200v100h-300zM600 800v-500h300v100h-200v300h200v100h-300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM500 700l-300 -150l300 -150v300zM600 400l300 150l-300 150v-300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM900 800v-500h-700v500h700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM800 700h-130 q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM200 800v-300h200v-100h-200v-100h300v300h-200v100h200v100h-300zM800 300h100v500h-200v-100h100v-400z M601 300h100v100h-100v-100z" />
+<glyph unicode="" d="M1200 800v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212zM1000 900h-900v-700h900v700zM300 700v100h-100v-500h300v400h-200zM800 300h100v500h-200v-100h100v-400zM401 400h-100v200h100v-200z M601 300h100v100h-100v-100z" />
+<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM1000 900h-900v-700h900v700zM400 700h-200v100h300v-300h-99v-100h-100v100h99v200zM800 700h-100v100h200v-500h-100v400zM201 400h100v-100 h-100v100zM701 300h-100v100h100v-100z" />
+<glyph unicode="" d="M600 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM600 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM800 700h-300 v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="" d="M596 1196q162 0 299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299t80 299t217 217t299 80zM596 1014q-171 0 -292.5 -121.5t-121.5 -292.5t121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5zM800 700v-100 h-100v100h-200v-100h200v-100h-200v-100h-100v400h300zM800 400h-100v100h100v-100z" />
+<glyph unicode="" d="M800 300h128q120 0 205 86t85 208q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57h222v300h400v-300zM700 200h200l-300 -300 l-300 300h200v300h200v-300z" />
+<glyph unicode="" d="M600 714l403 -403q94 26 154.5 104t60.5 178q0 121 -85 207.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5q0 -80 56.5 -137t135.5 -57h8zM700 -100h-200v300h-200l300 300 l300 -300h-200v-300z" />
+<glyph unicode="" d="M700 200h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-155l-75 -45h350l-75 45v155z" />
+<glyph unicode="" d="M700 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -12t1 -11q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5 q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350z" />
+<glyph unicode="💼" d="M800 1000h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100zM500 1000h200v100h-200v-100zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="📅" d="M1100 900v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150h1100zM0 800v-750q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100zM100 600h100v-100h-100v100zM300 600h100v-100h-100v100z M500 600h100v-100h-100v100zM700 600h100v-100h-100v100zM900 600h100v-100h-100v100zM100 400h100v-100h-100v100zM300 400h100v-100h-100v100zM500 400h100v-100h-100v100zM700 400h100v-100h-100v100zM900 400h100v-100h-100 [...]
+<glyph unicode="📌" d="M902 1185l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207l-380 -303l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15z" />
+<glyph unicode="📎" d="M518 119l69 -60l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163t35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84 t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -79.5 -17t-67.5 -51l-388 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t4 [...]
+<glyph unicode="📷" d="M1200 200v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5z M1000 700h-100v100h100v-100zM844 500q0 -100 -72 -172t-172 -72t-172 72t-72 172t72 172t172 72t172 -72t72 -172zM706 500q0 44 -31 75t-75 31t-75 -31t-31 -75t31 -75t75 -31t75 31t31 75z" />
+<glyph unicode="🔒" d="M900 800h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
+<glyph unicode="🔔" d="M1062 400h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-22 -9 -63 -23t-167.5 -37t-251.5 -23t-245.5 20.5t-178.5 41.5l-58 20q-18 7 -31 27.5t-13 40.5q0 21 13.5 35.5t33.5 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94 q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327zM600 104q-54 0 -103 6q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6z" />
+<glyph unicode="🔖" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
+<glyph unicode="🔥" d="M400 755q2 -12 8 -41.5t8 -43t6 -39.5t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85t5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5 q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 [...]
+<glyph unicode="🔧" d="M948 778l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138z" />
+</font>
+</defs></svg>
\ No newline at end of file
diff --git a/src/docs/assets/fonts/glyphicons-halflings-regular.ttf b/src/docs/assets/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..be784dc
Binary files /dev/null and b/src/docs/assets/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/src/docs/assets/fonts/glyphicons-halflings-regular.woff b/src/docs/assets/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..2cc3e48
Binary files /dev/null and b/src/docs/assets/fonts/glyphicons-halflings-regular.woff differ
diff --git a/src/docs/assets/img/apple-touch-icon-144-precomposed.png b/src/docs/assets/img/apple-touch-icon-144-precomposed.png
new file mode 100644
index 0000000..622a865
Binary files /dev/null and b/src/docs/assets/img/apple-touch-icon-144-precomposed.png differ
diff --git a/src/docs/assets/img/favicon.png b/src/docs/assets/img/favicon.png
new file mode 100644
index 0000000..4e4560f
Binary files /dev/null and b/src/docs/assets/img/favicon.png differ
diff --git a/src/docs/assets/img/masthead-pattern.png b/src/docs/assets/img/masthead-pattern.png
new file mode 100644
index 0000000..75c46a1
Binary files /dev/null and b/src/docs/assets/img/masthead-pattern.png differ
diff --git a/src/docs/assets/js/bootstrap-tour.js b/src/docs/assets/js/bootstrap-tour.js
new file mode 100644
index 0000000..1874061
--- /dev/null
+++ b/src/docs/assets/js/bootstrap-tour.js
@@ -0,0 +1,802 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+(function($, window) {
+ var Tour, document;
+ document = window.document;
+ Tour = (function() {
+ function Tour(options) {
+ var storage;
+ try {
+ storage = window.localStorage;
+ } catch (_error) {
+ storage = false;
+ }
+ this._options = $.extend({
+ name: 'tour',
+ steps: [],
+ container: 'body',
+ autoscroll: true,
+ keyboard: true,
+ storage: storage,
+ debug: false,
+ backdrop: false,
+ backdropPadding: 0,
+ redirect: true,
+ orphan: false,
+ duration: false,
+ delay: false,
+ basePath: '',
+ template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">« Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next »</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> [...]
+ afterSetState: function(key, value) {},
+ afterGetState: function(key, value) {},
+ afterRemoveState: function(key) {},
+ onStart: function(tour) {},
+ onEnd: function(tour) {},
+ onShow: function(tour) {},
+ onShown: function(tour) {},
+ onHide: function(tour) {},
+ onHidden: function(tour) {},
+ onNext: function(tour) {},
+ onPrev: function(tour) {},
+ onPause: function(tour, duration) {},
+ onResume: function(tour, duration) {}
+ }, options);
+ this._force = false;
+ this._inited = false;
+ this.backdrop = {
+ overlay: null,
+ $element: null,
+ $background: null,
+ backgroundShown: false,
+ overlayElementShown: false
+ };
+ this;
+ }
+
+ Tour.prototype.addSteps = function(steps) {
+ var step, _i, _len;
+ for (_i = 0, _len = steps.length; _i < _len; _i++) {
+ step = steps[_i];
+ this.addStep(step);
+ }
+ return this;
+ };
+
+ Tour.prototype.addStep = function(step) {
+ this._options.steps.push(step);
+ return this;
+ };
+
+ Tour.prototype.getStep = function(i) {
+ if (this._options.steps[i] != null) {
+ return $.extend({
+ id: "step-" + i,
+ path: '',
+ placement: 'right',
+ title: '',
+ content: '<p></p>',
+ next: i === this._options.steps.length - 1 ? -1 : i + 1,
+ prev: i - 1,
+ animation: true,
+ container: this._options.container,
+ autoscroll: this._options.autoscroll,
+ backdrop: this._options.backdrop,
+ backdropPadding: this._options.backdropPadding,
+ redirect: this._options.redirect,
+ orphan: this._options.orphan,
+ duration: this._options.duration,
+ delay: this._options.delay,
+ template: this._options.template,
+ onShow: this._options.onShow,
+ onShown: this._options.onShown,
+ onHide: this._options.onHide,
+ onHidden: this._options.onHidden,
+ onNext: this._options.onNext,
+ onPrev: this._options.onPrev,
+ onPause: this._options.onPause,
+ onResume: this._options.onResume
+ }, this._options.steps[i]);
+ }
+ };
+
+ Tour.prototype.init = function(force) {
+ this._force = force;
+ if (this.ended()) {
+ this._debug('Tour ended, init prevented.');
+ return this;
+ }
+ this.setCurrentStep();
+ this._initMouseNavigation();
+ this._initKeyboardNavigation();
+ this._onResize((function(_this) {
+ return function() {
+ return _this.showStep(_this._current);
+ };
+ })(this));
+ if (this._current !== null) {
+ this.showStep(this._current);
+ }
+ this._inited = true;
+ return this;
+ };
+
+ Tour.prototype.start = function(force) {
+ var promise;
+ if (force == null) {
+ force = false;
+ }
+ if (!this._inited) {
+ this.init(force);
+ }
+ if (this._current === null) {
+ promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
+ this._callOnPromiseDone(promise, this.showStep, 0);
+ }
+ return this;
+ };
+
+ Tour.prototype.next = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showNextStep);
+ };
+
+ Tour.prototype.prev = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showPrevStep);
+ };
+
+ Tour.prototype.goTo = function(i) {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this.showStep, i);
+ };
+
+ Tour.prototype.end = function() {
+ var endHelper, promise;
+ endHelper = (function(_this) {
+ return function(e) {
+ $(document).off("click.tour-" + _this._options.name);
+ $(document).off("keyup.tour-" + _this._options.name);
+ $(window).off("resize.tour-" + _this._options.name);
+ _this._setState('end', 'yes');
+ _this._inited = false;
+ _this._force = false;
+ _this._clearTimer();
+ if (_this._options.onEnd != null) {
+ return _this._options.onEnd(_this);
+ }
+ };
+ })(this);
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, endHelper);
+ };
+
+ Tour.prototype.ended = function() {
+ return !this._force && !!this._getState('end');
+ };
+
+ Tour.prototype.restart = function() {
+ this._removeState('current_step');
+ this._removeState('end');
+ return this.start();
+ };
+
+ Tour.prototype.pause = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = true;
+ this._duration -= new Date().getTime() - this._start;
+ window.clearTimeout(this._timer);
+ this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
+ if (step.onPause != null) {
+ return step.onPause(this, this._duration);
+ }
+ };
+
+ Tour.prototype.resume = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = false;
+ this._start = new Date().getTime();
+ this._duration = this._duration || step.duration;
+ this._timer = window.setTimeout((function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this), this._duration);
+ this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
+ if ((step.onResume != null) && this._duration !== step.duration) {
+ return step.onResume(this, this._duration);
+ }
+ };
+
+ Tour.prototype.hideStep = function(i) {
+ var hideStepHelper, promise, step;
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ this._clearTimer();
+ promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
+ hideStepHelper = (function(_this) {
+ return function(e) {
+ var $element;
+ $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");
+ if (step.reflex) {
+ $element.removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
+ }
+ if (step.backdrop) {
+ _this._hideBackdrop();
+ }
+ if (step.onHidden != null) {
+ return step.onHidden(_this);
+ }
+ };
+ })(this);
+ this._callOnPromiseDone(promise, hideStepHelper);
+ return promise;
+ };
+
+ Tour.prototype.showStep = function(i) {
+ var promise, showStepHelper, skipToPrevious, step;
+ if (this.ended()) {
+ this._debug('Tour ended, showStep prevented.');
+ return this;
+ }
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ skipToPrevious = i < this._current;
+ promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ showStepHelper = (function(_this) {
+ return function(e) {
+ var current_path, 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);
+ current_path = [document.location.pathname, document.location.hash].join('');
+ if (_this._isRedirect(path, current_path)) {
+ _this._redirect(step, path);
+ return;
+ }
+ if (_this._isOrphan(step)) {
+ if (!step.orphan) {
+ _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
+ if (skipToPrevious) {
+ _this._showPrevStep();
+ } else {
+ _this._showNextStep();
+ }
+ return;
+ }
+ _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
+ }
+ if (step.backdrop) {
+ _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
+ }
+ showPopoverAndOverlay = function() {
+ if (_this.getCurrentStep() !== i) {
+ return;
+ }
+ if ((step.element != null) && step.backdrop) {
+ _this._showOverlayElement(step);
+ }
+ _this._showPopover(step, i);
+ if (step.onShown != null) {
+ step.onShown(_this);
+ }
+ return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
+ };
+ if (step.autoscroll) {
+ _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ } else {
+ showPopoverAndOverlay();
+ }
+ if (step.duration) {
+ return _this.resume();
+ }
+ };
+ })(this);
+ if (step.delay) {
+ this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, showStepHelper);
+ };
+ })(this), step.delay);
+ } else {
+ this._callOnPromiseDone(promise, showStepHelper);
+ }
+ return promise;
+ };
+
+ Tour.prototype.getCurrentStep = function() {
+ return this._current;
+ };
+
+ Tour.prototype.setCurrentStep = function(value) {
+ if (value != null) {
+ this._current = value;
+ this._setState('current_step', value);
+ } else {
+ this._current = this._getState('current_step');
+ this._current = this._current === null ? null : parseInt(this._current, 10);
+ }
+ return this;
+ };
+
+ Tour.prototype._setState = function(key, value) {
+ var e, keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ try {
+ this._options.storage.setItem(keyName, value);
+ } catch (_error) {
+ e = _error;
+ if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+ this._debug('LocalStorage quota exceeded. State storage failed.');
+ }
+ }
+ return this._options.afterSetState(keyName, value);
+ } else {
+ if (this._state == null) {
+ this._state = {};
+ }
+ return this._state[key] = value;
+ }
+ };
+
+ Tour.prototype._removeState = function(key) {
+ var keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ this._options.storage.removeItem(keyName);
+ return this._options.afterRemoveState(keyName);
+ } else {
+ if (this._state != null) {
+ return delete this._state[key];
+ }
+ }
+ };
+
+ Tour.prototype._getState = function(key) {
+ var keyName, value;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ value = this._options.storage.getItem(keyName);
+ } else {
+ if (this._state != null) {
+ value = this._state[key];
+ }
+ }
+ if (value === void 0 || value === 'null') {
+ value = null;
+ }
+ this._options.afterGetState(key, value);
+ return value;
+ };
+
+ Tour.prototype._showNextStep = function() {
+ var promise, showNextStepHelper, step;
+ step = this.getStep(this._current);
+ showNextStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.next);
+ };
+ })(this);
+ promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0);
+ return this._callOnPromiseDone(promise, showNextStepHelper);
+ };
+
+ Tour.prototype._showPrevStep = function() {
+ var promise, showPrevStepHelper, step;
+ step = this.getStep(this._current);
+ showPrevStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.prev);
+ };
+ })(this);
+ promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0);
+ return this._callOnPromiseDone(promise, showPrevStepHelper);
+ };
+
+ Tour.prototype._debug = function(text) {
+ if (this._options.debug) {
+ return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
+ }
+ };
+
+ Tour.prototype._isRedirect = function(path, currentPath) {
+ return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && path.replace(/\?.*$/, '').replace(/\/?$/, '') !== currentPath.replace(/\/?$/, '')));
+ };
+
+ Tour.prototype._redirect = function(step, path) {
+ if ($.isFunction(step.redirect)) {
+ return step.redirect.call(this, path);
+ } else if (step.redirect === true) {
+ this._debug("Redirect to " + path);
+ return document.location.href = path;
+ }
+ };
+
+ Tour.prototype._isOrphan = function(step) {
+ return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');
+ };
+
+ Tour.prototype._isLast = function() {
+ return this._current < this._options.steps.length - 1;
+ };
+
+ Tour.prototype._showPopover = function(step, i) {
+ var $element, $tip, isOrphan, options;
+ $(".tour-" + this._options.name).remove();
+ options = $.extend({}, this._options);
+ isOrphan = this._isOrphan(step);
+ step.template = this._template(step, i);
+ if (isOrphan) {
+ step.element = 'body';
+ step.placement = 'top';
+ }
+ $element = $(step.element);
+ $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");
+ if (step.options) {
+ $.extend(options, step.options);
+ }
+ if (step.reflex && !isOrphan) {
+ $element.addClass('tour-step-element-reflex');
+ $element.off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name);
+ $element.on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this));
+ }
+ $element.popover({
+ placement: step.placement,
+ trigger: 'manual',
+ title: step.title,
+ content: step.content,
+ html: true,
+ animation: step.animation,
+ container: step.container,
+ template: step.template,
+ selector: step.element
+ }).popover('show');
+ $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
+ $tip.attr('id', step.id);
+ this._reposition($tip, step);
+ if (isOrphan) {
+ return this._center($tip);
+ }
+ };
+
+ Tour.prototype._template = function(step, i) {
+ var $navigation, $next, $prev, $resume, $template;
+ $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
+ $navigation = $template.find('.popover-navigation');
+ $prev = $navigation.find('[data-role="prev"]');
+ $next = $navigation.find('[data-role="next"]');
+ $resume = $navigation.find('[data-role="pause-resume"]');
+ if (this._isOrphan(step)) {
+ $template.addClass('orphan');
+ }
+ $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
+ if (step.prev < 0) {
+ $prev.addClass('disabled');
+ }
+ if (step.next < 0) {
+ $next.addClass('disabled');
+ }
+ if (!step.duration) {
+ $resume.remove();
+ }
+ return $template.clone().wrap('<div>').parent().html();
+ };
+
+ Tour.prototype._reflexEvent = function(reflex) {
+ if ({}.toString.call(reflex) === '[object Boolean]') {
+ return 'click';
+ } else {
+ return reflex;
+ }
+ };
+
+ Tour.prototype._reposition = function($tip, step) {
+ var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
+ offsetWidth = $tip[0].offsetWidth;
+ offsetHeight = $tip[0].offsetHeight;
+ tipOffset = $tip.offset();
+ originalLeft = tipOffset.left;
+ originalTop = tipOffset.top;
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
+ if (offsetBottom < 0) {
+ tipOffset.top = tipOffset.top + offsetBottom;
+ }
+ offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
+ if (offsetRight < 0) {
+ tipOffset.left = tipOffset.left + offsetRight;
+ }
+ if (tipOffset.top < 0) {
+ tipOffset.top = 0;
+ }
+ if (tipOffset.left < 0) {
+ tipOffset.left = 0;
+ }
+ $tip.offset(tipOffset);
+ if (step.placement === 'bottom' || step.placement === 'top') {
+ if (originalLeft !== tipOffset.left) {
+ return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
+ }
+ } else {
+ if (originalTop !== tipOffset.top) {
+ return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
+ }
+ }
+ };
+
+ Tour.prototype._center = function($tip) {
+ return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
+ };
+
+ Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
+ 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);
+ if (!$element.length) {
+ return callback();
+ }
+ $window = $(window);
+ offsetTop = $element.offset().top;
+ windowHeight = $window.height();
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
+ counter = 0;
+ return $('body, html').stop(true, true).animate({
+ scrollTop: Math.ceil(scrollTop)
+ }, (function(_this) {
+ return function() {
+ if (++counter === 2) {
+ callback();
+ return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._onResize = function(callback, timeout) {
+ return $(window).on("resize.tour-" + this._options.name, function() {
+ clearTimeout(timeout);
+ return timeout = setTimeout(callback, 100);
+ });
+ };
+
+ Tour.prototype._initMouseNavigation = function() {
+ var _this;
+ _this = this;
+ return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".pop [...]
+ return function(e) {
+ e.preventDefault();
+ return _this.next();
+ };
+ })(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();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ return _this.end();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) {
+ var $this;
+ e.preventDefault();
+ $this = $(this);
+ $this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
+ if (_this._paused) {
+ return _this.resume();
+ } else {
+ return _this.pause();
+ }
+ });
+ };
+
+ Tour.prototype._initKeyboardNavigation = function() {
+ if (!this._options.keyboard) {
+ return;
+ }
+ return $(document).on("keyup.tour-" + this._options.name, (function(_this) {
+ return function(e) {
+ if (!e.which) {
+ return;
+ }
+ switch (e.which) {
+ case 39:
+ e.preventDefault();
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ break;
+ case 37:
+ e.preventDefault();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
+ break;
+ case 27:
+ e.preventDefault();
+ return _this.end();
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._makePromise = function(result) {
+ if (result && $.isFunction(result.then)) {
+ return result;
+ } else {
+ return null;
+ }
+ };
+
+ Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
+ if (promise) {
+ return promise.then((function(_this) {
+ return function(e) {
+ return cb.call(_this, arg);
+ };
+ })(this));
+ } else {
+ return cb.call(this, arg);
+ }
+ };
+
+ Tour.prototype._showBackdrop = function(element) {
+ if (this.backdrop.backgroundShown) {
+ return;
+ }
+ this.backdrop = $('<div>', {
+ "class": 'tour-backdrop'
+ });
+ this.backdrop.backgroundShown = true;
+ return $('body').append(this.backdrop);
+ };
+
+ Tour.prototype._hideBackdrop = function() {
+ this._hideOverlayElement();
+ return this._hideBackground();
+ };
+
+ Tour.prototype._hideBackground = function() {
+ if (this.backdrop) {
+ this.backdrop.remove();
+ this.backdrop.overlay = null;
+ return this.backdrop.backgroundShown = false;
+ }
+ };
+
+ Tour.prototype._showOverlayElement = function(step) {
+ var $element, elementData;
+ $element = $(step.element);
+ if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.overlayElementShown = true;
+ this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$background = $('<div>', {
+ "class": 'tour-step-background'
+ });
+ elementData = {
+ width: $element.innerWidth(),
+ height: $element.innerHeight(),
+ offset: $element.offset()
+ };
+ this.backdrop.$background.appendTo('body');
+ if (step.backdropPadding) {
+ elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
+ }
+ return this.backdrop.$background.width(elementData.width).height(elementData.height).offset(elementData.offset);
+ };
+
+ Tour.prototype._hideOverlayElement = function() {
+ if (!this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.$element.removeClass('tour-step-backdrop');
+ this.backdrop.$background.remove();
+ this.backdrop.$element = null;
+ this.backdrop.$background = null;
+ return this.backdrop.overlayElementShown = false;
+ };
+
+ Tour.prototype._applyBackdropPadding = function(padding, data) {
+ if (typeof padding === 'object') {
+ if (padding.top == null) {
+ padding.top = 0;
+ }
+ if (padding.right == null) {
+ padding.right = 0;
+ }
+ if (padding.bottom == null) {
+ padding.bottom = 0;
+ }
+ if (padding.left == null) {
+ padding.left = 0;
+ }
+ data.offset.top = data.offset.top - padding.top;
+ data.offset.left = data.offset.left - padding.left;
+ data.width = data.width + padding.left + padding.right;
+ data.height = data.height + padding.top + padding.bottom;
+ } else {
+ data.offset.top = data.offset.top - padding;
+ data.offset.left = data.offset.left - padding;
+ data.width = data.width + (padding * 2);
+ data.height = data.height + (padding * 2);
+ }
+ return data;
+ };
+
+ Tour.prototype._clearTimer = function() {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ return this._duration = null;
+ };
+
+ return Tour;
+
+ })();
+ return window.Tour = Tour;
+})(jQuery, window);
diff --git a/src/docs/assets/vendor/bootstrap.min.css b/src/docs/assets/vendor/bootstrap.min.css
new file mode 100644
index 0000000..a553c4f
--- /dev/null
+++ b/src/docs/assets/vendor/bootstrap.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Bootstrap v3.0.0
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world by @mdo and @fat.
+ *//*! normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b [...]
\ No newline at end of file
diff --git a/src/docs/assets/vendor/jquery.smoothscroll.js b/src/docs/assets/vendor/jquery.smoothscroll.js
new file mode 100644
index 0000000..9c71e50
--- /dev/null
+++ b/src/docs/assets/vendor/jquery.smoothscroll.js
@@ -0,0 +1,47 @@
+/*! http://mths.be/smoothscroll v1.5.2 by @mathias */
+;
+(function(document, $) {
+
+ var $scrollElement = (function() {
+ // Find out what to scroll (html or body)
+ var $html = $(document.documentElement),
+ $body = $(document.body),
+ bodyScrollTop;
+ if ($html.scrollTop()) {
+ return $html;
+ } else {
+ bodyScrollTop = $body.scrollTop();
+ // If scrolling the body doesn’t do anything
+ if ($body.scrollTop(bodyScrollTop + 1).scrollTop() == bodyScrollTop) {
+ return $html;
+ } else {
+ // We actually scrolled, so undo it
+ return $body.scrollTop(bodyScrollTop);
+ }
+ }
+ }());
+
+ $.fn.smoothScroll = function(speed) {
+ speed = ~~speed || 400;
+ // Look for links to anchors (on any page)
+ return this.find('a[href*="#"]').click(function(event) {
+ var hash = this.hash,
+ $hash = $(hash); // The in-document element the link points to
+ // If it’s a link to an anchor in the same document
+ if (location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') && location.hostname === this.hostname) {
+ // If the anchor actually exists…
+ if ($hash.length) {
+ // …don’t jump to the link right away…
+ event.preventDefault();
+ // …and smoothly scroll to it
+ $scrollElement.stop().animate({
+ 'scrollTop': $hash.offset().top
+ }, speed, function() {
+ location.hash = hash;
+ });
+ }
+ }
+ }).end();
+ };
+
+}(document, jQuery));
\ No newline at end of file
diff --git a/src/docs/assets/vendor/pygments-manni.css b/src/docs/assets/vendor/pygments-manni.css
new file mode 100644
index 0000000..1264b87
--- /dev/null
+++ b/src/docs/assets/vendor/pygments-manni.css
@@ -0,0 +1,66 @@
+.hll { background-color: #ffffcc }
+ /*{ background: #f0f3f3; }*/
+.c { color: #999; } /* Comment */
+.err { color: #AA0000; background-color: #FFAAAA } /* Error */
+.k { color: #006699; } /* Keyword */
+.o { color: #555555 } /* Operator */
+.cm { color: #0099FF; font-style: italic } /* Comment.Multiline */
+.cp { color: #009999 } /* Comment.Preproc */
+.c1 { color: #999; } /* Comment.Single */
+.cs { color: #999; } /* Comment.Special */
+.gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #003300; } /* Generic.Heading */
+.gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */
+.go { color: #AAAAAA } /* Generic.Output */
+.gp { color: #000099; } /* Generic.Prompt */
+.gs { } /* Generic.Strong */
+.gu { color: #003300; } /* Generic.Subheading */
+.gt { color: #99CC66 } /* Generic.Traceback */
+.kc { color: #006699; } /* Keyword.Constant */
+.kd { color: #006699; } /* Keyword.Declaration */
+.kn { color: #006699; } /* Keyword.Namespace */
+.kp { color: #006699 } /* Keyword.Pseudo */
+.kr { color: #006699; } /* Keyword.Reserved */
+.kt { color: #007788; } /* Keyword.Type */
+.m { color: #FF6600 } /* Literal.Number */
+.s { color: #d44950 } /* Literal.String */
+.na { color: #4f9fcf } /* Name.Attribute */
+.nb { color: #336666 } /* Name.Builtin */
+.nc { color: #00AA88; } /* Name.Class */
+.no { color: #336600 } /* Name.Constant */
+.nd { color: #9999FF } /* Name.Decorator */
+.ni { color: #999999; } /* Name.Entity */
+.ne { color: #CC0000; } /* Name.Exception */
+.nf { color: #CC00FF } /* Name.Function */
+.nl { color: #9999FF } /* Name.Label */
+.nn { color: #00CCFF; } /* Name.Namespace */
+.nt { color: #2f6f9f; } /* Name.Tag */
+.nv { color: #003333 } /* Name.Variable */
+.ow { color: #000000; } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mf { color: #FF6600 } /* Literal.Number.Float */
+.mh { color: #FF6600 } /* Literal.Number.Hex */
+.mi { color: #FF6600 } /* Literal.Number.Integer */
+.mo { color: #FF6600 } /* Literal.Number.Oct */
+.sb { color: #CC3300 } /* Literal.String.Backtick */
+.sc { color: #CC3300 } /* Literal.String.Char */
+.sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
+.s2 { color: #CC3300 } /* Literal.String.Double */
+.se { color: #CC3300; } /* Literal.String.Escape */
+.sh { color: #CC3300 } /* Literal.String.Heredoc */
+.si { color: #AA0000 } /* Literal.String.Interpol */
+.sx { color: #CC3300 } /* Literal.String.Other */
+.sr { color: #33AAAA } /* Literal.String.Regex */
+.s1 { color: #CC3300 } /* Literal.String.Single */
+.ss { color: #FFCC33 } /* Literal.String.Symbol */
+.bp { color: #336666 } /* Name.Builtin.Pseudo */
+.vc { color: #003333 } /* Name.Variable.Class */
+.vg { color: #003333 } /* Name.Variable.Global */
+.vi { color: #003333 } /* Name.Variable.Instance */
+.il { color: #FF6600 } /* Literal.Number.Integer.Long */
+
+.css .o,
+.css .o + .nt,
+.css .nt + .nt { color: #999; }
diff --git a/src/docs/index.html b/src/docs/index.html
new file mode 100644
index 0000000..18ad4ed
--- /dev/null
+++ b/src/docs/index.html
@@ -0,0 +1,107 @@
+---
+layout: default
+title: Bootstrap Tour
+slug: home
+---
+
+<header id="home">
+ <div class="jumbotron masthead">
+ <h1>Bootstrap Tour</h1>
+ <p class="lead">
+ The easiest way to show people how to use your website.
+ <br><small>Not using Bootstrap? It works anyway!</small>
+ </p>
+ <p>
+ <button type="button" id="demo" class="btn btn-default btn-lg" data-demo>
+ <span class="glyphicon glyphicon-play"></span>
+ Start the demo
+ </button>
+ </p>
+ <ul class="social">
+ <li>
+ <iframe class="github-btn" src="http://ghbtns.com/github-btn.html?user=sorich87&repo=bootstrap-tour&type=watch&count=true" width="100" height="20" title="Star on GitHub"></iframe>
+ </li>
+ <li>
+ <iframe class="github-btn" src="http://ghbtns.com/github-btn.html?user=sorich87&repo=bootstrap-tour&type=fork&count=true" width="102" height="20" title="Fork on GitHub"></iframe>
+ </li>
+ <li class="tweet-btn">
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://bootstraptour.com/" data-count="horizontal">Tweet</a>
+ </li>
+ </ul>
+ </div>
+</header>
+<section id="usage">
+ <div class="container">
+ <div class="page-header">
+ <h2>The How-to <small>It's simple!</small></h2>
+ </div>
+ <h3>Add the dependencies</h3>
+ <p>If you are using Bootstrap, include <code>bootstrap-tour.min.css</code> and <code>bootstrap-tour.min.js</code>:</p>
+{% highlight html %}
+<link href="bootstrap.min.css" rel="stylesheet">
+<link href="bootstrap-tour.min.css" rel="stylesheet">
+...
+<script src="jquery.min.js"></script>
+<script src="bootstrap.min.js"></script>
+<script src="bootstrap-tour.min.js"></script>
+{% endhighlight %}
+ <p>Otherwise, just include <code>bootstrap-tour-standalone.min.css</code> and <code>bootstrap-tour-standalone.min.js</code>:</p>
+{% highlight html %}
+<link href="bootstrap-tour-standalone.min.css" rel="stylesheet">
+...
+<script src="jquery.min.js"></script>
+<script src="bootstrap-tour-standalone.min.js"></script>
+{% endhighlight %}
+ <h3>Setup your tour:</h3>
+{% highlight javascript %}
+// Instance the tour
+var tour = new Tour({
+ steps: [
+ {
+ element: "#my-element",
+ title: "Title of my step",
+ content: "Content of my step"
+ },
+ {
+ element: "#my-other-element",
+ title: "Title of my step",
+ content: "Content of my step"
+ }
+]});
+
+// Initialize the tour
+tour.init();
+
+// Start the tour
+tour.start();
+{% endhighlight %}
+ <p>Do you want to do more? <a href="api">Read the full documentation.</a></p>
+ </div>
+</section>
+<section id="contributing">
+ <div class="container">
+ <div class="page-header">
+ <h2>The Team <small>It's handmade!</small></h2>
+ </div>
+ <div class="row">
+ <div class="col-sm-4">
+ <a href="https://github.com/sorich87"><img src="" class="gravatar img-circle" data-email="sorich87 at gmail.com"> Ulrich Sossou</a>
+ </div>
+ <div class="col-sm-4">
+ <a href="https://github.com/LostCrew"><img src="" class="gravatar img-circle" data-email="emanuele at lostcrew.it"> Emanuele Marchi</a>
+ </div>
+ <div class="col-sm-4">
+ <a href="https://github.com/emmenko"><img src="" class="gravatar img-circle" data-email="emmenko at gmail.com"> Nicola Molinari</a>
+ </div>
+ </div>
+ </div>
+</section>
+<section id="license">
+ <div class="container">
+ <div class="page-header">
+ <h2>The License <small>It's free!</small></h2>
+ </div>
+ <p>Code licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache License v2.0</a>.<br>
+ Documentation licensed under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</p>
+ </div>
+</section>
diff --git a/src/less/bootstrap-tour-standalone.less b/src/less/bootstrap-tour-standalone.less
new file mode 100644
index 0000000..b09d3ba
--- /dev/null
+++ b/src/less/bootstrap-tour-standalone.less
@@ -0,0 +1,15 @@
+/*!
+ * Bootstrap v3.1.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+// Core variables and mixins
+ at bootstrap-path: "../../bower_components/bootstrap/less/";
+
+ at import "@{bootstrap-path}variables.less";
+ at import "@{bootstrap-path}mixins.less";
+ at import "@{bootstrap-path}buttons.less";
+ at import "@{bootstrap-path}button-groups.less";
+ at import "@{bootstrap-path}popovers.less";
+ at import "bootstrap-tour.less";
diff --git a/src/less/bootstrap-tour.less b/src/less/bootstrap-tour.less
new file mode 100644
index 0000000..4292840
--- /dev/null
+++ b/src/less/bootstrap-tour.less
@@ -0,0 +1,60 @@
+.tour-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1100;
+ background-color: #000;
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.tour-step-backdrop {
+ position: relative;
+ z-index: 1101;
+ background: inherit;
+
+ > td {
+ position: relative;
+ z-index: 1101;
+ }
+}
+
+.tour-step-background {
+ position: absolute !important;
+ z-index: 1100;
+ background: inherit;
+ border-radius: 6px;
+}
+
+.popover[class*="tour-"] {
+ z-index: 1100;
+
+ .popover-navigation {
+ padding: 9px 14px;
+
+ *[data-role="end"] {
+ float: right;
+ }
+
+ *[data-role="prev"],
+ *[data-role="next"],
+ *[data-role="end"] {
+ cursor: pointer;
+
+ &.disabled {
+ cursor: default;
+ }
+ }
+ }
+
+ &.orphan {
+ position: fixed;
+ margin-top: 0;
+
+ .arrow {
+ display: none;
+ }
+ }
+}
diff --git a/test/bootstrap-tour.js b/test/bootstrap-tour.js
new file mode 100644
index 0000000..1874061
--- /dev/null
+++ b/test/bootstrap-tour.js
@@ -0,0 +1,802 @@
+/* ========================================================================
+ * bootstrap-tour - v0.10.1
+ * http://bootstraptour.com
+ * ========================================================================
+ * Copyright 2012-2013 Ulrich Sossou
+ *
+ * ========================================================================
+ * Licensed under the Apache License, Version 2.0 (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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================================
+ */
+
+(function($, window) {
+ var Tour, document;
+ document = window.document;
+ Tour = (function() {
+ function Tour(options) {
+ var storage;
+ try {
+ storage = window.localStorage;
+ } catch (_error) {
+ storage = false;
+ }
+ this._options = $.extend({
+ name: 'tour',
+ steps: [],
+ container: 'body',
+ autoscroll: true,
+ keyboard: true,
+ storage: storage,
+ debug: false,
+ backdrop: false,
+ backdropPadding: 0,
+ redirect: true,
+ orphan: false,
+ duration: false,
+ delay: false,
+ basePath: '',
+ template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">« Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next »</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> [...]
+ afterSetState: function(key, value) {},
+ afterGetState: function(key, value) {},
+ afterRemoveState: function(key) {},
+ onStart: function(tour) {},
+ onEnd: function(tour) {},
+ onShow: function(tour) {},
+ onShown: function(tour) {},
+ onHide: function(tour) {},
+ onHidden: function(tour) {},
+ onNext: function(tour) {},
+ onPrev: function(tour) {},
+ onPause: function(tour, duration) {},
+ onResume: function(tour, duration) {}
+ }, options);
+ this._force = false;
+ this._inited = false;
+ this.backdrop = {
+ overlay: null,
+ $element: null,
+ $background: null,
+ backgroundShown: false,
+ overlayElementShown: false
+ };
+ this;
+ }
+
+ Tour.prototype.addSteps = function(steps) {
+ var step, _i, _len;
+ for (_i = 0, _len = steps.length; _i < _len; _i++) {
+ step = steps[_i];
+ this.addStep(step);
+ }
+ return this;
+ };
+
+ Tour.prototype.addStep = function(step) {
+ this._options.steps.push(step);
+ return this;
+ };
+
+ Tour.prototype.getStep = function(i) {
+ if (this._options.steps[i] != null) {
+ return $.extend({
+ id: "step-" + i,
+ path: '',
+ placement: 'right',
+ title: '',
+ content: '<p></p>',
+ next: i === this._options.steps.length - 1 ? -1 : i + 1,
+ prev: i - 1,
+ animation: true,
+ container: this._options.container,
+ autoscroll: this._options.autoscroll,
+ backdrop: this._options.backdrop,
+ backdropPadding: this._options.backdropPadding,
+ redirect: this._options.redirect,
+ orphan: this._options.orphan,
+ duration: this._options.duration,
+ delay: this._options.delay,
+ template: this._options.template,
+ onShow: this._options.onShow,
+ onShown: this._options.onShown,
+ onHide: this._options.onHide,
+ onHidden: this._options.onHidden,
+ onNext: this._options.onNext,
+ onPrev: this._options.onPrev,
+ onPause: this._options.onPause,
+ onResume: this._options.onResume
+ }, this._options.steps[i]);
+ }
+ };
+
+ Tour.prototype.init = function(force) {
+ this._force = force;
+ if (this.ended()) {
+ this._debug('Tour ended, init prevented.');
+ return this;
+ }
+ this.setCurrentStep();
+ this._initMouseNavigation();
+ this._initKeyboardNavigation();
+ this._onResize((function(_this) {
+ return function() {
+ return _this.showStep(_this._current);
+ };
+ })(this));
+ if (this._current !== null) {
+ this.showStep(this._current);
+ }
+ this._inited = true;
+ return this;
+ };
+
+ Tour.prototype.start = function(force) {
+ var promise;
+ if (force == null) {
+ force = false;
+ }
+ if (!this._inited) {
+ this.init(force);
+ }
+ if (this._current === null) {
+ promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
+ this._callOnPromiseDone(promise, this.showStep, 0);
+ }
+ return this;
+ };
+
+ Tour.prototype.next = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showNextStep);
+ };
+
+ Tour.prototype.prev = function() {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this._showPrevStep);
+ };
+
+ Tour.prototype.goTo = function(i) {
+ var promise;
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, this.showStep, i);
+ };
+
+ Tour.prototype.end = function() {
+ var endHelper, promise;
+ endHelper = (function(_this) {
+ return function(e) {
+ $(document).off("click.tour-" + _this._options.name);
+ $(document).off("keyup.tour-" + _this._options.name);
+ $(window).off("resize.tour-" + _this._options.name);
+ _this._setState('end', 'yes');
+ _this._inited = false;
+ _this._force = false;
+ _this._clearTimer();
+ if (_this._options.onEnd != null) {
+ return _this._options.onEnd(_this);
+ }
+ };
+ })(this);
+ promise = this.hideStep(this._current);
+ return this._callOnPromiseDone(promise, endHelper);
+ };
+
+ Tour.prototype.ended = function() {
+ return !this._force && !!this._getState('end');
+ };
+
+ Tour.prototype.restart = function() {
+ this._removeState('current_step');
+ this._removeState('end');
+ return this.start();
+ };
+
+ Tour.prototype.pause = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = true;
+ this._duration -= new Date().getTime() - this._start;
+ window.clearTimeout(this._timer);
+ this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
+ if (step.onPause != null) {
+ return step.onPause(this, this._duration);
+ }
+ };
+
+ Tour.prototype.resume = function() {
+ var step;
+ step = this.getStep(this._current);
+ if (!(step && step.duration)) {
+ return this;
+ }
+ this._paused = false;
+ this._start = new Date().getTime();
+ this._duration = this._duration || step.duration;
+ this._timer = window.setTimeout((function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this), this._duration);
+ this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
+ if ((step.onResume != null) && this._duration !== step.duration) {
+ return step.onResume(this, this._duration);
+ }
+ };
+
+ Tour.prototype.hideStep = function(i) {
+ var hideStepHelper, promise, step;
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ this._clearTimer();
+ promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
+ hideStepHelper = (function(_this) {
+ return function(e) {
+ var $element;
+ $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");
+ if (step.reflex) {
+ $element.removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
+ }
+ if (step.backdrop) {
+ _this._hideBackdrop();
+ }
+ if (step.onHidden != null) {
+ return step.onHidden(_this);
+ }
+ };
+ })(this);
+ this._callOnPromiseDone(promise, hideStepHelper);
+ return promise;
+ };
+
+ Tour.prototype.showStep = function(i) {
+ var promise, showStepHelper, skipToPrevious, step;
+ if (this.ended()) {
+ this._debug('Tour ended, showStep prevented.');
+ return this;
+ }
+ step = this.getStep(i);
+ if (!step) {
+ return;
+ }
+ skipToPrevious = i < this._current;
+ promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
+ showStepHelper = (function(_this) {
+ return function(e) {
+ var current_path, 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);
+ current_path = [document.location.pathname, document.location.hash].join('');
+ if (_this._isRedirect(path, current_path)) {
+ _this._redirect(step, path);
+ return;
+ }
+ if (_this._isOrphan(step)) {
+ if (!step.orphan) {
+ _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
+ if (skipToPrevious) {
+ _this._showPrevStep();
+ } else {
+ _this._showNextStep();
+ }
+ return;
+ }
+ _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
+ }
+ if (step.backdrop) {
+ _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
+ }
+ showPopoverAndOverlay = function() {
+ if (_this.getCurrentStep() !== i) {
+ return;
+ }
+ if ((step.element != null) && step.backdrop) {
+ _this._showOverlayElement(step);
+ }
+ _this._showPopover(step, i);
+ if (step.onShown != null) {
+ step.onShown(_this);
+ }
+ return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
+ };
+ if (step.autoscroll) {
+ _this._scrollIntoView(step.element, showPopoverAndOverlay);
+ } else {
+ showPopoverAndOverlay();
+ }
+ if (step.duration) {
+ return _this.resume();
+ }
+ };
+ })(this);
+ if (step.delay) {
+ this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
+ window.setTimeout((function(_this) {
+ return function() {
+ return _this._callOnPromiseDone(promise, showStepHelper);
+ };
+ })(this), step.delay);
+ } else {
+ this._callOnPromiseDone(promise, showStepHelper);
+ }
+ return promise;
+ };
+
+ Tour.prototype.getCurrentStep = function() {
+ return this._current;
+ };
+
+ Tour.prototype.setCurrentStep = function(value) {
+ if (value != null) {
+ this._current = value;
+ this._setState('current_step', value);
+ } else {
+ this._current = this._getState('current_step');
+ this._current = this._current === null ? null : parseInt(this._current, 10);
+ }
+ return this;
+ };
+
+ Tour.prototype._setState = function(key, value) {
+ var e, keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ try {
+ this._options.storage.setItem(keyName, value);
+ } catch (_error) {
+ e = _error;
+ if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+ this._debug('LocalStorage quota exceeded. State storage failed.');
+ }
+ }
+ return this._options.afterSetState(keyName, value);
+ } else {
+ if (this._state == null) {
+ this._state = {};
+ }
+ return this._state[key] = value;
+ }
+ };
+
+ Tour.prototype._removeState = function(key) {
+ var keyName;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ this._options.storage.removeItem(keyName);
+ return this._options.afterRemoveState(keyName);
+ } else {
+ if (this._state != null) {
+ return delete this._state[key];
+ }
+ }
+ };
+
+ Tour.prototype._getState = function(key) {
+ var keyName, value;
+ if (this._options.storage) {
+ keyName = "" + this._options.name + "_" + key;
+ value = this._options.storage.getItem(keyName);
+ } else {
+ if (this._state != null) {
+ value = this._state[key];
+ }
+ }
+ if (value === void 0 || value === 'null') {
+ value = null;
+ }
+ this._options.afterGetState(key, value);
+ return value;
+ };
+
+ Tour.prototype._showNextStep = function() {
+ var promise, showNextStepHelper, step;
+ step = this.getStep(this._current);
+ showNextStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.next);
+ };
+ })(this);
+ promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0);
+ return this._callOnPromiseDone(promise, showNextStepHelper);
+ };
+
+ Tour.prototype._showPrevStep = function() {
+ var promise, showPrevStepHelper, step;
+ step = this.getStep(this._current);
+ showPrevStepHelper = (function(_this) {
+ return function(e) {
+ return _this.showStep(step.prev);
+ };
+ })(this);
+ promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0);
+ return this._callOnPromiseDone(promise, showPrevStepHelper);
+ };
+
+ Tour.prototype._debug = function(text) {
+ if (this._options.debug) {
+ return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
+ }
+ };
+
+ Tour.prototype._isRedirect = function(path, currentPath) {
+ return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && path.replace(/\?.*$/, '').replace(/\/?$/, '') !== currentPath.replace(/\/?$/, '')));
+ };
+
+ Tour.prototype._redirect = function(step, path) {
+ if ($.isFunction(step.redirect)) {
+ return step.redirect.call(this, path);
+ } else if (step.redirect === true) {
+ this._debug("Redirect to " + path);
+ return document.location.href = path;
+ }
+ };
+
+ Tour.prototype._isOrphan = function(step) {
+ return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');
+ };
+
+ Tour.prototype._isLast = function() {
+ return this._current < this._options.steps.length - 1;
+ };
+
+ Tour.prototype._showPopover = function(step, i) {
+ var $element, $tip, isOrphan, options;
+ $(".tour-" + this._options.name).remove();
+ options = $.extend({}, this._options);
+ isOrphan = this._isOrphan(step);
+ step.template = this._template(step, i);
+ if (isOrphan) {
+ step.element = 'body';
+ step.placement = 'top';
+ }
+ $element = $(step.element);
+ $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");
+ if (step.options) {
+ $.extend(options, step.options);
+ }
+ if (step.reflex && !isOrphan) {
+ $element.addClass('tour-step-element-reflex');
+ $element.off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name);
+ $element.on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) {
+ return function() {
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ };
+ })(this));
+ }
+ $element.popover({
+ placement: step.placement,
+ trigger: 'manual',
+ title: step.title,
+ content: step.content,
+ html: true,
+ animation: step.animation,
+ container: step.container,
+ template: step.template,
+ selector: step.element
+ }).popover('show');
+ $tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
+ $tip.attr('id', step.id);
+ this._reposition($tip, step);
+ if (isOrphan) {
+ return this._center($tip);
+ }
+ };
+
+ Tour.prototype._template = function(step, i) {
+ var $navigation, $next, $prev, $resume, $template;
+ $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
+ $navigation = $template.find('.popover-navigation');
+ $prev = $navigation.find('[data-role="prev"]');
+ $next = $navigation.find('[data-role="next"]');
+ $resume = $navigation.find('[data-role="pause-resume"]');
+ if (this._isOrphan(step)) {
+ $template.addClass('orphan');
+ }
+ $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
+ if (step.prev < 0) {
+ $prev.addClass('disabled');
+ }
+ if (step.next < 0) {
+ $next.addClass('disabled');
+ }
+ if (!step.duration) {
+ $resume.remove();
+ }
+ return $template.clone().wrap('<div>').parent().html();
+ };
+
+ Tour.prototype._reflexEvent = function(reflex) {
+ if ({}.toString.call(reflex) === '[object Boolean]') {
+ return 'click';
+ } else {
+ return reflex;
+ }
+ };
+
+ Tour.prototype._reposition = function($tip, step) {
+ var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
+ offsetWidth = $tip[0].offsetWidth;
+ offsetHeight = $tip[0].offsetHeight;
+ tipOffset = $tip.offset();
+ originalLeft = tipOffset.left;
+ originalTop = tipOffset.top;
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
+ if (offsetBottom < 0) {
+ tipOffset.top = tipOffset.top + offsetBottom;
+ }
+ offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
+ if (offsetRight < 0) {
+ tipOffset.left = tipOffset.left + offsetRight;
+ }
+ if (tipOffset.top < 0) {
+ tipOffset.top = 0;
+ }
+ if (tipOffset.left < 0) {
+ tipOffset.left = 0;
+ }
+ $tip.offset(tipOffset);
+ if (step.placement === 'bottom' || step.placement === 'top') {
+ if (originalLeft !== tipOffset.left) {
+ return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
+ }
+ } else {
+ if (originalTop !== tipOffset.top) {
+ return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
+ }
+ }
+ };
+
+ Tour.prototype._center = function($tip) {
+ return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
+ };
+
+ Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
+ 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);
+ if (!$element.length) {
+ return callback();
+ }
+ $window = $(window);
+ offsetTop = $element.offset().top;
+ windowHeight = $window.height();
+ scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
+ this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
+ counter = 0;
+ return $('body, html').stop(true, true).animate({
+ scrollTop: Math.ceil(scrollTop)
+ }, (function(_this) {
+ return function() {
+ if (++counter === 2) {
+ callback();
+ return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._onResize = function(callback, timeout) {
+ return $(window).on("resize.tour-" + this._options.name, function() {
+ clearTimeout(timeout);
+ return timeout = setTimeout(callback, 100);
+ });
+ };
+
+ Tour.prototype._initMouseNavigation = function() {
+ var _this;
+ _this = this;
+ return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".pop [...]
+ return function(e) {
+ e.preventDefault();
+ return _this.next();
+ };
+ })(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();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ return _this.end();
+ };
+ })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) {
+ var $this;
+ e.preventDefault();
+ $this = $(this);
+ $this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
+ if (_this._paused) {
+ return _this.resume();
+ } else {
+ return _this.pause();
+ }
+ });
+ };
+
+ Tour.prototype._initKeyboardNavigation = function() {
+ if (!this._options.keyboard) {
+ return;
+ }
+ return $(document).on("keyup.tour-" + this._options.name, (function(_this) {
+ return function(e) {
+ if (!e.which) {
+ return;
+ }
+ switch (e.which) {
+ case 39:
+ e.preventDefault();
+ if (_this._isLast()) {
+ return _this.next();
+ } else {
+ return _this.end();
+ }
+ break;
+ case 37:
+ e.preventDefault();
+ if (_this._current > 0) {
+ return _this.prev();
+ }
+ break;
+ case 27:
+ e.preventDefault();
+ return _this.end();
+ }
+ };
+ })(this));
+ };
+
+ Tour.prototype._makePromise = function(result) {
+ if (result && $.isFunction(result.then)) {
+ return result;
+ } else {
+ return null;
+ }
+ };
+
+ Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
+ if (promise) {
+ return promise.then((function(_this) {
+ return function(e) {
+ return cb.call(_this, arg);
+ };
+ })(this));
+ } else {
+ return cb.call(this, arg);
+ }
+ };
+
+ Tour.prototype._showBackdrop = function(element) {
+ if (this.backdrop.backgroundShown) {
+ return;
+ }
+ this.backdrop = $('<div>', {
+ "class": 'tour-backdrop'
+ });
+ this.backdrop.backgroundShown = true;
+ return $('body').append(this.backdrop);
+ };
+
+ Tour.prototype._hideBackdrop = function() {
+ this._hideOverlayElement();
+ return this._hideBackground();
+ };
+
+ Tour.prototype._hideBackground = function() {
+ if (this.backdrop) {
+ this.backdrop.remove();
+ this.backdrop.overlay = null;
+ return this.backdrop.backgroundShown = false;
+ }
+ };
+
+ Tour.prototype._showOverlayElement = function(step) {
+ var $element, elementData;
+ $element = $(step.element);
+ if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.overlayElementShown = true;
+ this.backdrop.$element = $element.addClass('tour-step-backdrop');
+ this.backdrop.$background = $('<div>', {
+ "class": 'tour-step-background'
+ });
+ elementData = {
+ width: $element.innerWidth(),
+ height: $element.innerHeight(),
+ offset: $element.offset()
+ };
+ this.backdrop.$background.appendTo('body');
+ if (step.backdropPadding) {
+ elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
+ }
+ return this.backdrop.$background.width(elementData.width).height(elementData.height).offset(elementData.offset);
+ };
+
+ Tour.prototype._hideOverlayElement = function() {
+ if (!this.backdrop.overlayElementShown) {
+ return;
+ }
+ this.backdrop.$element.removeClass('tour-step-backdrop');
+ this.backdrop.$background.remove();
+ this.backdrop.$element = null;
+ this.backdrop.$background = null;
+ return this.backdrop.overlayElementShown = false;
+ };
+
+ Tour.prototype._applyBackdropPadding = function(padding, data) {
+ if (typeof padding === 'object') {
+ if (padding.top == null) {
+ padding.top = 0;
+ }
+ if (padding.right == null) {
+ padding.right = 0;
+ }
+ if (padding.bottom == null) {
+ padding.bottom = 0;
+ }
+ if (padding.left == null) {
+ padding.left = 0;
+ }
+ data.offset.top = data.offset.top - padding.top;
+ data.offset.left = data.offset.left - padding.left;
+ data.width = data.width + padding.left + padding.right;
+ data.height = data.height + padding.top + padding.bottom;
+ } else {
+ data.offset.top = data.offset.top - padding;
+ data.offset.left = data.offset.left - padding;
+ data.width = data.width + (padding * 2);
+ data.height = data.height + (padding * 2);
+ }
+ return data;
+ };
+
+ Tour.prototype._clearTimer = function() {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ return this._duration = null;
+ };
+
+ return Tour;
+
+ })();
+ return window.Tour = Tour;
+})(jQuery, window);
--
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