mirror of
https://github.com/webslides/WebSlides.git
synced 2025-09-20 18:02:46 +02:00
Compare commits
90 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8f7707b996 | ||
|
dc0386cb49 | ||
|
21f7ba37ca | ||
|
9bbbd7362b | ||
|
6818e4c99c | ||
|
23ad0338cb | ||
|
440e1bf37e | ||
|
f086aefe71 | ||
|
8ae6954e5f | ||
|
09bdc31b64 | ||
|
5c4d2f8769 | ||
|
d03e0cad41 | ||
|
be0e498b2b | ||
|
12bac7afc5 | ||
|
78b6e74b82 | ||
|
91c00a2fe3 | ||
|
e65826dbb6 | ||
|
508cdafea5 | ||
|
50af5fe3df | ||
|
ebe42090ac | ||
|
14e1b5b7b8 | ||
|
2e6471e2ee | ||
|
bc64fcef7e | ||
|
e50193ebb1 | ||
|
b04f15f696 | ||
|
3d6fcef762 | ||
|
32ff8afa88 | ||
|
f082ff12ac | ||
|
62c6aba478 | ||
|
5a262f5460 | ||
|
4379122d1d | ||
|
83f1110947 | ||
|
b7503b1e9e | ||
|
521d708c22 | ||
|
33b1f3ad7a | ||
|
f6ccd39158 | ||
|
6674a4f203 | ||
|
f80106a4ab | ||
|
4fb151c886 | ||
|
b7a01c4319 | ||
|
28c08541e5 | ||
|
6bc8ab582d | ||
|
19cd5bdbc7 | ||
|
bb5ae82995 | ||
|
36c1f1997b | ||
|
5687d61a83 | ||
|
0d2c8c09d1 | ||
|
9e5a127590 | ||
|
7b86354ad4 | ||
|
a67f7d9d0b | ||
|
2234a02252 | ||
|
02834a3b4d | ||
|
3e13eb4025 | ||
|
28756b308f | ||
|
acd3eed1b7 | ||
|
8ac52b4936 | ||
|
130c5d2321 | ||
|
0055ecd7d8 | ||
|
4ba77ba3fc | ||
|
25be14fbd4 | ||
|
2c21b8f5d9 | ||
|
2ef3f986ed | ||
|
76ea07d8db | ||
|
9bfab33676 | ||
|
3251b1b7d8 | ||
|
3782a40a8a | ||
|
585ab60b43 | ||
|
62ffaf95d6 | ||
|
4ca297f8ae | ||
|
0ce1d02d8b | ||
|
c5133158ad | ||
|
ff3c2d066a | ||
|
8e47ffead4 | ||
|
2a46012a58 | ||
|
c1777f593f | ||
|
b99fdc8c47 | ||
|
0e02056ca7 | ||
|
72d057293e | ||
|
75de8a46ac | ||
|
ae36e15588 | ||
|
6e7ce46ebc | ||
|
ea164b6d69 | ||
|
7b495cccf1 | ||
|
049d083744 | ||
|
86cc1f494f | ||
|
d4e6662ab6 | ||
|
01fa93d50e | ||
|
70bde2f4ae | ||
|
2e93754ba1 | ||
|
4e562e2978 |
20
.editorconfig
Normal file
20
.editorconfig
Normal file
@@ -0,0 +1,20 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 233
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# IDE files #
|
||||
#############
|
||||
.idea/
|
||||
|
||||
# Third Party #
|
||||
###############
|
||||
node_modules/
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
76
CHANGELOG.md
76
CHANGELOG.md
@@ -1,13 +1,77 @@
|
||||
## 0.2 (2017-02-22)
|
||||
# 1.2.0 (2017-03-02)
|
||||
|
||||
- Auto slide (Demo: why-webslides.html)
|
||||
## New Features
|
||||
|
||||
## 0.1.1 (2017-02-11)
|
||||
- [[#48](https://github.com/jlantunez/webslides/issues/48)] Allows to navigate with AvPag & RePag to allow presentation devices to work.
|
||||
- [[#49](https://github.com/jlantunez/webslides/issues/49)] Allowing to go to first and last slides by using home and end keys respectively.
|
||||
- [[#50](https://github.com/jlantunez/webslides/issues/50)] Using the keyboard on inputs and editable content won't trigger any events that might cause navigation.
|
||||
- [[#47](https://github.com/jlantunez/webslides/issues/47)] Allowing options to be configured. [Read More](/docs/technical.md#options).
|
||||
|
||||
# 1.1.0 (2017-02-28)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed a bug which caused Chrome on OSX to stutter a lot on vertical transitioning due to elastic scroll bounce.
|
||||
- [[#28](https://github.com/jlantunez/webslides/issues/28)] Fixed scroll on Firefox.
|
||||
- [[#38](https://github.com/jlantunez/webslides/issues/38)] Fixed a bug in Safari which lead to unexpected behaviour using any form of movements.
|
||||
- [[#10](https://github.com/jlantunez/webslides/issues/10)] Fixed animation flash on Safari.
|
||||
|
||||
## New Features
|
||||
|
||||
- [[#1](https://github.com/jlantunez/webslides/issues/1)] Adding option to click to go to the next slide. Read more [here](https://github.com/jlantunez/webslides/blob/master/docs/click-to-nav.md).
|
||||
- [[#1](https://github.com/jlantunez/webslides/issues/1)] Improved sliding with mouse scroll and touchpad. It's now possible to use scroll to move an horizontal presentation.
|
||||
It's also possible to scroll horizontally on horizontal presentations to move forward/backwards the presentation.
|
||||
|
||||
## Regression
|
||||
|
||||
- Introduced a minor bug on iOS Safari which leads to the bottom part of the page not being visible on the first scroll. This is likely a browser bug but it has been unearthed in this version due to a much needed improvement on scrolling behaviour bugs. We're trying to investigate a bit more and will provide a fix ASAP.
|
||||
|
||||
# 1.0.0 (2017-02-23)
|
||||
|
||||
This release is a special one since it sets up in the path of a better development environment. Although it's far from
|
||||
perfect, it's a solid beginning.
|
||||
|
||||
All the code has been migrated from **jQuery** with ES5 to **vanilla JavaScript with ES2015 (or ES6) and is fully modular**.
|
||||
This means that WebSlides is a (base module)[src/js/modules/webslides.js] with a solid API (few public methods) and
|
||||
it's extended by (plugins)[src/js/plugins]. This leads to more granularity and less code to dive through while fixing a
|
||||
bug.
|
||||
|
||||
**The benefit from this approach is that now it's really easy to extend WebSlides** to achieve what you need. You can also
|
||||
overwrite current plugins. Say you don't like the current navigation with arrows and want to create a menu instead, you
|
||||
can just write that for yourself with your custom needs and register it as `nav` and it will overwrite our nav with
|
||||
your code.
|
||||
|
||||
We hope this leads to a better environment in which WebSlides can grow better.
|
||||
|
||||
All the technical specs live now in [this document](docs/technical.md).
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed a bug with back/next buttons on the browser which lead the nav bar to not work.
|
||||
|
||||
## New Features
|
||||
|
||||
- Linking to slides without window open.
|
||||
- Added custom events to listen for. `ws:init` whenever webslides is ready and `ws:slide-change` whenever a slide changes.
|
||||
- Added play/stop methods.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- This "stable" release drops the jQuery requirement and leans on ES2015 for the architecture. Hence, it's no longer possible
|
||||
to use the library as before.
|
||||
|
||||
# 0.2.0 (2017-02-22)
|
||||
|
||||
## New Features
|
||||
|
||||
- Adding autoslide option.
|
||||
|
||||
# 0.1.1 (2017-02-11)
|
||||
|
||||
- Transform the library into an object.
|
||||
- .tabs removed.
|
||||
- webslides-lite.js removed.
|
||||
- `.tabs` removed.
|
||||
- `webslides-lite.js` removed.
|
||||
|
||||
## 0.1 (2017-01-08)
|
||||
# 0.1.0 (2017-01-08)
|
||||
|
||||
- Initial release.
|
||||
|
109
README.md
109
README.md
@@ -1,71 +1,74 @@
|
||||
# WebSlides = Good Karma
|
||||
Finally, everything you need to make HTML presentations in a beautiful way. Just the essentials. You can create your own presentation instantly. Simply choose a demo and customize it in minutes — [https://webslides.tv/demos](https://webslides.tv/demos).
|
||||
|
||||
A new release (at least) every 8th day of the month. Version 0.1: Jan 8, 2017.
|
||||
[](http://opensource.org/licenses/MIT)
|
||||
[](https://twitter.com/webslides)
|
||||
|
||||
Finally, everything you need to make HTML presentations in a beautiful way. Just the essentials. You can create your own presentation instantly. A new release (at least) every 8th day of the month — [https://webslides.tv/demos](https://webslides.tv/demos).
|
||||
|
||||
* * *
|
||||
### Download
|
||||
Simply choose a demo and customize it in minutes. Latest version: [webslides.tv/webslides-latest.zip](https://webslides.tv/webslides-latest.zip).
|
||||
* * *
|
||||
|
||||
|
||||
### Why WebSlides?
|
||||
Good karma and productivity. Just a basic knowledge of HTML and CSS is required. Designers, marketers, and journalists can now focus on the content.
|
||||
|
||||
### Features
|
||||
## Features
|
||||
|
||||
- Navigation (horizontal and vertical sliding): touchpad, keyboard shortcuts, and swipe.
|
||||
- Slide counter.
|
||||
- Permalinks: go to a specific slide.
|
||||
- Autoslide
|
||||
- Autoslide.
|
||||
- Click to nav. [Read more](docs/click-to-nav.md)!
|
||||
- Simple CSS alignments. Put content wherever you want (vertical centering...)
|
||||
- 40+ components: background images/videos, quotes, cards, covers...
|
||||
- Flexible blocks with auto-fill and equal height.
|
||||
- Fonts: Roboto, Maitree (Serif), and San Francisco.
|
||||
- Vertical rhythm (use multiples of 8).
|
||||
|
||||
### Markup
|
||||
### Key Navigation
|
||||
|
||||
There's a handful of keys that can be used to achieve navigation within WebSlides. Here's the list:
|
||||
|
||||
* `←`: If WebSlides is not vertical, it will go to the previous slide.
|
||||
* `→`: If WebSlides is not vertical, it will go to the next slide.
|
||||
* `↑`: If WebSlides is vertical, it will go to the previous slide.
|
||||
* `↓`: If WebSlides is vertical, it will go to the next slide.
|
||||
* `Page Up`: Go to the previous slide.
|
||||
* `Page Down`: Go to the next slide.
|
||||
* `Space`: Go to the next slide.
|
||||
* `Home`: Go to the first slide.
|
||||
* `End`: Go to the last slide.
|
||||
|
||||
## Markup
|
||||
|
||||
- Code is clean and scalable. It uses intuitive markup with popular naming conventions. There's no need to overuse classes or nesting.
|
||||
- Each parent <code><section></code> in the #webslides element is an individual slide.
|
||||
- Each parent `<section>` in the `#webslides` element is an individual slide.
|
||||
|
||||
<pre><article id="webslides">
|
||||
<section>
|
||||
<h1>Slide 1</h1>
|
||||
</section>
|
||||
<section class="bg-black aligncenter">
|
||||
<span class="code-comment"><!-- .wrap = container 1200px --></span>
|
||||
<div class="wrap">
|
||||
<h1>Slide 2</h1>
|
||||
</div>
|
||||
</section>
|
||||
</article></pre>
|
||||
|
||||
#### Vertical Sliding
|
||||
|
||||
<pre><article id="webslides" class="vertical"></pre>
|
||||
|
||||
### How it works
|
||||
|
||||
You need to add the follow javascript to initialize the webslides object.
|
||||
|
||||
```javascript
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
```html
|
||||
<article id="webslides">
|
||||
<section>
|
||||
<h1>Slide 1</h1>
|
||||
</section>
|
||||
<section class="bg-black aligncenter">
|
||||
<!-- .wrap = container 1200px -->
|
||||
<div class="wrap">
|
||||
<h1>Slide 2</h1>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
```
|
||||
|
||||
#### Auto slide
|
||||
### Vertical Sliding
|
||||
|
||||
```javascript
|
||||
var slide = jQuery('#webslides').webslides({interval: 5000});
|
||||
```
|
||||
Now you can use the slide with these functions:
|
||||
|
||||
```javascript
|
||||
// Moving to next slide
|
||||
slide.nextSlide();
|
||||
// Moving to previous slide
|
||||
slide.previousSlide();
|
||||
// Moving to a specific slide
|
||||
slide.goToSlide(n);
|
||||
```html
|
||||
<article id="webslides" class="vertical">
|
||||
```
|
||||
|
||||
### What's in the download?
|
||||
|
||||
The download includes demos and images (devices and logos).
|
||||
The download includes demos and images (devices and logos).
|
||||
All content is for demo purposes only. Images are property of their respective owners.
|
||||
|
||||
```
|
||||
@@ -84,16 +87,17 @@ webslides/
|
||||
|
||||
### CSS Syntax (classes)
|
||||
|
||||
- Typography: .text-landing, .text-data, .text-intro...
|
||||
- Background Colors: .bg-primary, .bg-apple, .bg-blue...
|
||||
- Background Images: .background,.background-center-bottom...
|
||||
- Cards: .card-50, .card-40...
|
||||
- Flexible Blocks: .flexblock.clients, .flexblock.metrics...
|
||||
- Typography: `.text-landing`, `.text-data`, `.text-intro`...
|
||||
- Background Colors: `.bg-primary`, `.bg-apple`, `.bg-blue`...
|
||||
- Background Images: `.background`,`.background-center-bottom`...
|
||||
- Cards: `.card-50`, `.card-40`...
|
||||
- Flexible Blocks: `.flexblock.clients`, `.flexblock.metrics`...
|
||||
|
||||
|
||||
### Extensions
|
||||
|
||||
You can add:
|
||||
|
||||
- [Unsplash](https://unsplash.com) photos
|
||||
- [animate.css](https://daneden.github.io/animate.css)
|
||||
- [particles.js](https://github.com/VincentGarreau/particles.js)
|
||||
@@ -101,11 +105,18 @@ You can add:
|
||||
|
||||
### License
|
||||
|
||||
WebSlides is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
WebSlides is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
Use it to make something cool.
|
||||
|
||||
### Dive In!
|
||||
|
||||
Please check out:
|
||||
|
||||
- Want to get techie? Read [our technical docs](docs/technical.md)
|
||||
- Do not miss [our demos](https://webslides.tv/)
|
||||
|
||||
### Credits
|
||||
|
||||
- WebSlides was created by [@jlantunez](https://twitter.com/jlantunez) using [Cactus](https://github.com/eudicots/Cactus).
|
||||
- Thanks [@LuisSacristan](https://twitter.com/luissacristan) for the javascript code :)
|
||||
- Javascript: [@Belelros](https://twitter.com/Belelros) and [@LuisSacristan](https://twitter.com/luissacristan).
|
||||
- Based on [SimpleSlides](https://github.com/jennschiffer/SimpleSlides), by [@JennSchiffer](https://twitter.com/jennschiffer).
|
||||
|
@@ -1933,16 +1933,13 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -319,7 +319,11 @@
|
||||
<section>
|
||||
<div class="wrap">
|
||||
<h1>Navigation</h1>
|
||||
|
||||
<ul class="tabs">
|
||||
<li class="tab current" data-tab="tab-1">ul.tabs</li>
|
||||
<li class="tab" data-tab="tab-2">columns</li>
|
||||
</ul>
|
||||
<div id="tab-1" class="tab-content current">
|
||||
<nav role="navigation">
|
||||
<ul>
|
||||
<li><a href="">About</a></li>
|
||||
@@ -368,8 +372,9 @@
|
||||
</ul>
|
||||
</nav>
|
||||
<p>nav.navbar</p>
|
||||
|
||||
<hr>
|
||||
</div>
|
||||
<!-- end .tab-content -->
|
||||
<div id="tab-2" class="tab-content">
|
||||
<div class="grid">
|
||||
<div class="column">
|
||||
<h3>Company</h3>
|
||||
@@ -430,6 +435,8 @@
|
||||
<!-- .end .column -->
|
||||
</div>
|
||||
<!-- .end .grid -->
|
||||
</div>
|
||||
<!-- end .tab-content -->
|
||||
</div>
|
||||
<!-- .end .wrap -->
|
||||
</section>
|
||||
@@ -1796,8 +1803,49 @@
|
||||
<section class="aligncenter">
|
||||
<!--.wrap = container 1200px -->
|
||||
<div class="wrap">
|
||||
<h1 class="text-landing">Tell a Story</h1>
|
||||
<p class="text-intro"><strong>Hi, this is WebSlides</strong>. HTML presentations made simple. <br>I'm a cute solution with clean markup and <strong>lovely CSS</strong>.</p>
|
||||
<div id="tab-3" class="tab-content current">
|
||||
<h1 class="text-landing">Tell a Story</h1>
|
||||
<p class="text-intro"><strong>Hi, this is WebSlides</strong>. HTML presentations made simple. <br>I'm a cute solution with clean markup and <strong>lovely CSS</strong>.</p>
|
||||
</div>
|
||||
<div id="tab-4" class="tab-content">
|
||||
<ul class="flexblock features">
|
||||
<li>
|
||||
<div>
|
||||
<svg class="fa-heart-o">
|
||||
<use xlink:href="#fa-heart-o"></use>
|
||||
</svg>
|
||||
<h2>Indexed content</h2>
|
||||
Sharing is caring.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<h2>
|
||||
<svg class="fa-magic">
|
||||
<use xlink:href="#fa-magic"></use>
|
||||
</svg>
|
||||
Just essential features
|
||||
</h2>
|
||||
Keyboard navigation...
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<svg class="fa-bolt">
|
||||
<use xlink:href="#fa-bolt"></use>
|
||||
</svg>
|
||||
<h2>
|
||||
Prototype faster
|
||||
</h2>
|
||||
with clean code
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="tabs">
|
||||
<li class="tab current" data-tab="tab-3">Purpose</li>
|
||||
<li class="tab" data-tab="tab-4">Benefits</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<section class="bg-black aligncenter">
|
||||
@@ -3078,16 +3126,14 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -92,7 +92,7 @@
|
||||
</header>
|
||||
|
||||
<main role="main">
|
||||
<article id="webslides" class="vertical">
|
||||
<article> <!-- Slideshow? id="webslides" -->
|
||||
|
||||
<!-- Quick Guide
|
||||
- Each parent <section> in the <article id="webslides"> element is an individual slide.
|
||||
@@ -107,7 +107,7 @@
|
||||
<h1><strong>WebSlides Demos</strong></h1>
|
||||
<p class="text-intro">All of these presentations are free and responsive.<br>
|
||||
40+ <a href="../demos/components.html" title="WebSlides Components">components</a> with a solid <a href="../demos/classes.html" title="WebSlides Classes">CSS architecture</a>.</p>
|
||||
<p>Share your slides using <a target="_blank" href="https://twitter.com/search?f=tweets&q=%23webslides&src=typd" title="#WebSlides on Twitter">#WebSlides</a>.</p>
|
||||
<p>Share your slides using <a href="https://twitter.com/search?q=%23webslides&src=typd" title="#WebSlides on Twitter">#WebSlides</a>.</p>
|
||||
</div>
|
||||
<!-- .end .wrap -->
|
||||
</section>
|
||||
@@ -222,16 +222,14 @@
|
||||
</main>
|
||||
<!-- end main -->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -843,16 +843,14 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1918,16 +1918,13 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1858,16 +1858,14 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="../static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -66,7 +66,6 @@
|
||||
<!-- Android -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#333333">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header role="banner">
|
||||
@@ -339,13 +338,11 @@
|
||||
</main>
|
||||
<!--main-->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="../static/js/svg-icons.js"></script>
|
||||
<script src="../static/js/webslides.js"></script>
|
||||
|
||||
<!-- Autoslide 5 seconds. If you don't want autoslide, remove: {interval: 5000} -->
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides({interval: 5000});
|
||||
<!-- Autoslide 5 seconds. If you don't want autoslide, remove: {autoslide: 5000} -->
|
||||
<script>
|
||||
window.ws = new WebSlides({ autoslide: 5000 });
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
|
16
docs/click-to-nav.md
Normal file
16
docs/click-to-nav.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## Click To Nav plugin
|
||||
|
||||
This plugin is included by default but disabled. In order to enable it, the option `changeOnClick` must be passed as
|
||||
`true`.
|
||||
|
||||
```javascript
|
||||
const ws = new WebSlides({ changeOnClick: true });
|
||||
```
|
||||
|
||||
This will make every click to navigate to the next slide except for clicks that happens on the following elements:
|
||||
|
||||
* `input`.
|
||||
* `select` or `option`.
|
||||
* `button`.
|
||||
* `a`.
|
||||
* Any element with the attribute `data-prevent-nav`.
|
147
docs/technical.md
Normal file
147
docs/technical.md
Normal file
@@ -0,0 +1,147 @@
|
||||
## Project Setup
|
||||
|
||||
This project assumes you have NodeJS. You should also have npm installed as well (this usually comes packaged with Node). Once you have it cloned, you should run `npm install` to get all the dependencies.
|
||||
|
||||
Finally, run one of the following commands in the cloned directory:
|
||||
|
||||
- `npm run dev`: This starts a dev server with autoreload on the port `8080`.
|
||||
- `npm run build`: This creates the `dist` files.
|
||||
|
||||
## JavaScript
|
||||
|
||||
In order to bootstrap the WebSlides you'll need to create a instance of it:
|
||||
|
||||
```javascript
|
||||
const ws = new WebSlides();
|
||||
```
|
||||
|
||||
That'll make everything run without any hassle.
|
||||
|
||||
### Options
|
||||
|
||||
WebSlides constructor accepts an object with options.
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
|-----------|----------------|-----------|-------------------------------------------------------------------------------|
|
||||
| `autoslide` | `number` or `boolean` | `false` | Amount of milliseconds to wait to go to next slide automatically. |
|
||||
| `changeOnClick` | `boolean` | `false` | If true, clicking on the page will go to the next slide unless it's a clickable element. See [ClickToNav docs](./click-to-nav.md) for more info. |
|
||||
| `minWheelDelta` | `number` | `40` | Controls the amount of scroll needed to trigger a navigation. Lower this number to decrease the scroll resistance. |
|
||||
| `scrollWait` | `number` | `450` | Controls the amount of time needed to wait for a scroll transition to happen again. |
|
||||
| `slideOffset` | `number` | `50` | Amount of sliding needed to trigger a new navigation. |
|
||||
|
||||
|
||||
```javascript
|
||||
const ws = new WebSlides({
|
||||
autoslide = false,
|
||||
changeOnClick = false,
|
||||
minWheelDelta = 40,
|
||||
scrollWait = 450,
|
||||
slideOffset = 50
|
||||
});
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
Do you want to get your hands dirty? This is the API for the WebSlides module:
|
||||
|
||||
<dl>
|
||||
<dt><a href="#goToSlide">goToSlide(slideIndex, opt_forward)</a></dt>
|
||||
<dd><p>Goes to a given slide.</p></dd>
|
||||
<dt><a href="#goNext">goNext()</a></dt>
|
||||
<dd><p>Goes to the next slide.</p></dd>
|
||||
<dt><a href="#goPrev">goPrev()</a></dt>
|
||||
<dd><p>Goes to the previous slide.</p>
|
||||
<dt><a href="#play">play()</a></dt>
|
||||
<dd><p>Starts autosliding.</p>
|
||||
<dt><a href="#stop">stop()</a></dt>
|
||||
<dd><p>Stops autosliding.</p>
|
||||
</dd>
|
||||
<dt><a href="#registerPlugin">registerPlugin(key, cto)</a></dt>
|
||||
<dd><p>Registers a plugin to be loaded when the instance is created. It allows
|
||||
(on purpose) to replace default plugins.
|
||||
Those being:</p>
|
||||
<ul>
|
||||
<li>Navigation</li>
|
||||
<li>Hash</li>
|
||||
<li>Keyboard</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<a name="goToSlide"></a>
|
||||
|
||||
### `goToSlide(slideI, forward)`
|
||||
Goes to a given slide.
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| slideIndex | <code>number</code> | The slide index. |
|
||||
| forward | <code>boolean</code> | Whether we're forcing moving forward/backwards. This parameter is used only from the `goNext`, `goPrev` functions to adjust the scroll animations. |
|
||||
|
||||
<a name="goNext"></a>
|
||||
|
||||
### `goNext()`
|
||||
Goes to the next slide. If the page is vertical, it will animate the scroll down.
|
||||
|
||||
<a name="goPrev"></a>
|
||||
|
||||
### `goPrev()`
|
||||
Goes to the previous slide. If the page is vertical, it will animate the scroll up
|
||||
|
||||
<a name="play"></a>
|
||||
|
||||
### `play(time)`
|
||||
Autoplays slides. If time is omitted, it will use the default time passed to the constructor. This is useful if you don't want to autoslide from the beginning but you want to add a button to do it.
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| time | <code>number</code> | Amount of milliseconds to wait to go to next slide automatically. |
|
||||
|
||||
<a name="stop"></a>
|
||||
|
||||
### `stop()`
|
||||
Stops autosliding.
|
||||
|
||||
<a name="registerPlugin"></a>
|
||||
|
||||
### `registerPlugin(key, cto)`
|
||||
Registers a plugin to be loaded when the instance is created. It allows
|
||||
(on purpose) to replace default plugins.
|
||||
|
||||
Those being:
|
||||
|
||||
- ClickNav
|
||||
- Grid
|
||||
- Hash
|
||||
- Keyboard
|
||||
- Navigation
|
||||
- Scroll
|
||||
- Touch
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| key | <code>string</code> | They key under which it'll be stored inside of the instance, inside the plugins dict. |
|
||||
| cto | <code>function</code> | Plugin constructor. |
|
||||
|
||||
### Plugin development
|
||||
|
||||
Almost every single feature of WebSlides is a plugin that can be overwritten and you are able to create your custom plugins. Just call `registerPlugin` (as seen above) **before creating** the instance:
|
||||
|
||||
```javascript
|
||||
// Adding the constructor to WebSlides
|
||||
WebSlides.registerPlugin('myPlugin', MyPlugin);
|
||||
|
||||
// Starting WebSlides
|
||||
// Your plugin will be constructed at this time and it will receive the webslides instance as the only parameter.
|
||||
const ws = new WebSlides();
|
||||
// You can also access ws.plugins.myPlugin now
|
||||
```
|
||||
|
||||
This allows you to rewrite the navigation to use a menu (for example) or add that missing piece of functionality you'd like to see. See [this part of the code](../src/js/modules/webslides.js#L11) to see all the plugins we're using and the name they're using.
|
||||
|
||||
Make sure to let us know so it could get added to the repo!
|
||||
|
||||
### Roadmap
|
||||
|
||||
* Fix crossbrowser issues. Safari mostly.
|
||||
* Write tests
|
12
index.html
12
index.html
@@ -94,7 +94,6 @@
|
||||
|
||||
<main role="main">
|
||||
<article id="webslides" class="vertical">
|
||||
|
||||
<!-- Quick Guide
|
||||
- Each parent <section> in the <article id="webslides"> element is an individual slide.
|
||||
- Vertical sliding = <article id="webslides" class="vertical">
|
||||
@@ -384,17 +383,14 @@
|
||||
</div>
|
||||
</footer> -->
|
||||
|
||||
<!-- jQuery (required for slides to work) -->
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="static/js/svg-icons.js"></script>
|
||||
<script src="static/js/webslides.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var slide = jQuery('#webslides').webslides();
|
||||
<script>
|
||||
window.ws = new WebSlides();
|
||||
</script>
|
||||
|
||||
<!-- OPTIONAL - svg-icons.js (fontastic.me - Font Awesome as svg icons) -->
|
||||
<script defer src="static/js/svg-icons.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
58
package.json
Normal file
58
package.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "webslides",
|
||||
"version": "1.2.0",
|
||||
"description": "Making HTML presentations easy",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jlantunez/webslides.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webslides",
|
||||
"presentation",
|
||||
"css"
|
||||
],
|
||||
"author": "Jose Luís Antúnez <jlantunez@gmail.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Luís Sacristán"
|
||||
},
|
||||
{
|
||||
"name": "Antonio Laguna",
|
||||
"email": "a.laguna@funcion13.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jlantunez/webslides/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jlantunez/webslides#readme",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.23.0",
|
||||
"babel-core": "^6.23.1",
|
||||
"babel-loader": "^6.3.2",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"npm-run-all": "^4.0.2",
|
||||
"rimraf": "^2.6.0",
|
||||
"smart-banner-webpack-plugin": "^3.0.1",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "rimraf static/js/webslide*",
|
||||
"build": "npm-run-all --parallel build:*",
|
||||
"build:main": "webpack",
|
||||
"build:main.min": "webpack --output-filename [name].min.js -p",
|
||||
"dev": "webpack-dev-server"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
[
|
||||
"es2015",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
3
src/js/full.js
Normal file
3
src/js/full.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import WebSlides from './modules/webslides';
|
||||
|
||||
window.WebSlides = WebSlides;
|
82
src/js/modules/slide.js
Normal file
82
src/js/modules/slide.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import DOM from '../utils/dom';
|
||||
|
||||
const CLASSES = {
|
||||
SLIDE: 'slide',
|
||||
CURRENT: 'current'
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for the Slide section.
|
||||
*/
|
||||
export default class Slide {
|
||||
/**
|
||||
* Bootstraps the slide by saving some data, adding a class and hiding it.
|
||||
* @param {Element} el Section element.
|
||||
* @param {number} i Zero based index of the slide.
|
||||
*/
|
||||
constructor(el, i) {
|
||||
/**
|
||||
* @type {Element}
|
||||
*/
|
||||
this.el = el;
|
||||
/**
|
||||
* The section's parent.
|
||||
* @type {Node}
|
||||
*/
|
||||
this.parent = el.parentNode;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.i = i;
|
||||
|
||||
this.el.id = 'section-' + (i + 1);
|
||||
this.el.classList.add(CLASSES.SLIDE);
|
||||
|
||||
// Hide slides by default
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the node and removes the class that makes it "active".
|
||||
*/
|
||||
hide() {
|
||||
DOM.hide(this.el);
|
||||
this.el.classList.remove(CLASSES.CURRENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the node and adds the class that makes it "active".
|
||||
*/
|
||||
show() {
|
||||
DOM.show(this.el);
|
||||
this.el.classList.add(CLASSES.CURRENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the section to the bottom of the section's list.
|
||||
*/
|
||||
moveAfterLast() {
|
||||
const last = this.parent.childNodes[this.parent.childElementCount - 1];
|
||||
|
||||
this.parent.insertBefore(this.el, last.nextSibling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the section to the top of the section's list.
|
||||
*/
|
||||
moveBeforeFirst() {
|
||||
const first = this.parent.childNodes[0];
|
||||
|
||||
this.parent.insertBefore(this.el, first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an element is a valid candidate to be a slide by ensuring
|
||||
* it's a "section" element.
|
||||
* @param {Element} el Element to be checked.
|
||||
* @return {boolean} Whether is candidate or not.
|
||||
*/
|
||||
static isCandidate(el) {
|
||||
return el.nodeType === 1 && el.tagName === 'SECTION';
|
||||
}
|
||||
}
|
381
src/js/modules/webslides.js
Normal file
381
src/js/modules/webslides.js
Normal file
@@ -0,0 +1,381 @@
|
||||
import Plugins from '../plugins/plugins';
|
||||
import Slide from './slide';
|
||||
import DOM from '../utils/dom';
|
||||
import scrollTo from '../utils/scroll-to';
|
||||
|
||||
const CLASSES = {
|
||||
VERTICAL: 'vertical'
|
||||
};
|
||||
|
||||
// Default plugins
|
||||
const PLUGINS = {
|
||||
'clickNav': Plugins.ClickNav,
|
||||
'grid': Plugins.Grid,
|
||||
'hash': Plugins.Hash,
|
||||
'keyboard': Plugins.Keyboard,
|
||||
'nav': Plugins.Navigation,
|
||||
'scroll': Plugins.Scroll,
|
||||
'touch': Plugins.Touch
|
||||
};
|
||||
|
||||
export default class WebSlides {
|
||||
/**
|
||||
* Options for WebSlides
|
||||
* @param {number|boolean} autoslide If a number is provided, it will allow
|
||||
* autosliding by said amount of miliseconds.
|
||||
* @param {boolean} changeOnClick If true, it will allow
|
||||
* clicking on any place to change the slide.
|
||||
* @param {number} minWheelDelta Controls the amount of needed scroll to
|
||||
* trigger navigation.
|
||||
* @param {number} scrollWait Controls the amount of time to wait till
|
||||
* navigation can occur again with scroll.
|
||||
* @param {number} slideOffset Controls the amount of needed touch delta to
|
||||
* trigger navigation.
|
||||
*/
|
||||
constructor({
|
||||
autoslide = false,
|
||||
changeOnClick = false,
|
||||
minWheelDelta = 40,
|
||||
scrollWait = 450,
|
||||
slideOffset = 50
|
||||
} = {}) {
|
||||
/**
|
||||
* WebSlide element.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.el = document.getElementById('webslides');
|
||||
/**
|
||||
* Moving flag.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isMoving = false;
|
||||
/**
|
||||
* Slide's array.
|
||||
* @type {?Array<Slide>}
|
||||
*/
|
||||
this.slides = null;
|
||||
/**
|
||||
* Current slide's index.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.currentSlideI_ = -1;
|
||||
/**
|
||||
* Current slide reference.
|
||||
* @type {?Slide}
|
||||
* @private
|
||||
*/
|
||||
this.currentSlide_ = null;
|
||||
/**
|
||||
* Max slide index.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.maxSlide_ = 0;
|
||||
/**
|
||||
* Whether the layout is going to be vertical or horizontal.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isVertical = this.el.classList.contains(CLASSES.VERTICAL);
|
||||
/**
|
||||
* Plugin's dictionary.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.plugins = {};
|
||||
/**
|
||||
* Interval ID reference for the autoslide.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
this.interval_ = null;
|
||||
/**
|
||||
* Options dictionary.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.options = {
|
||||
autoslide,
|
||||
changeOnClick,
|
||||
minWheelDelta,
|
||||
scrollWait,
|
||||
slideOffset
|
||||
};
|
||||
|
||||
if (!this.el) {
|
||||
throw new Error('Couldn\'t find the webslides container!');
|
||||
}
|
||||
|
||||
// Bootstrapping
|
||||
this.removeChildren_();
|
||||
this.grabSlides_();
|
||||
this.createPlugins_();
|
||||
this.initSlides_();
|
||||
this.play();
|
||||
// Finished
|
||||
this.onInit_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all children elements inside of the main container that are not
|
||||
* eligible to be a Slide Element.
|
||||
* @private
|
||||
*/
|
||||
removeChildren_() {
|
||||
const nodes = this.el.childNodes;
|
||||
let i = nodes.length;
|
||||
|
||||
while (i--) {
|
||||
const node = nodes[i];
|
||||
|
||||
if (!Slide.isCandidate(node)) {
|
||||
this.el.removeChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all the registered plugins and store the instances inside of the
|
||||
* the webslide instance.
|
||||
* @private
|
||||
*/
|
||||
createPlugins_() {
|
||||
Object.keys(PLUGINS).forEach(pluginName => {
|
||||
const pluginCto = PLUGINS[pluginName];
|
||||
this.plugins[pluginName] = new pluginCto(this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the WebSlide instance has finished initialising.
|
||||
* @private
|
||||
* @fires WebSlide#ws:init
|
||||
*/
|
||||
onInit_() {
|
||||
DOM.fireEvent(this.el, 'ws:init');
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs the slides from the DOM and creates all the Slides modules.
|
||||
* @private
|
||||
*/
|
||||
grabSlides_() {
|
||||
this.slides = DOM.toArray(this.el.childNodes)
|
||||
.map((slide, i) => new Slide(slide, i));
|
||||
|
||||
this.maxSlide_ = this.slides.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to a given slide.
|
||||
* @param {!number} slideI The slide index.
|
||||
* @param {?boolean} forward Whether we're forcing moving forward/backwards.
|
||||
* This parameter is used only from the goNext, goPrev functions to adjust the
|
||||
* scroll animations.
|
||||
*/
|
||||
goToSlide(slideI, forward = null) {
|
||||
if (this.isValidIndexSlide_(slideI) &&
|
||||
!this.isMoving &&
|
||||
this.currentSlideI_ !== slideI) {
|
||||
this.isMoving = true;
|
||||
let isMovingForward = false;
|
||||
|
||||
if (forward !== null) {
|
||||
isMovingForward = forward;
|
||||
} else {
|
||||
if (this.currentSlideI_ >= 0) {
|
||||
isMovingForward = slideI > this.currentSlideI_;
|
||||
}
|
||||
}
|
||||
const nextSlide = this.slides[slideI];
|
||||
|
||||
if (this.currentSlide_ !== null && this.isVertical &&
|
||||
(!this.plugins.touch || !this.plugins.touch.isEnabled)) {
|
||||
this.scrollTransitionToSlide_(
|
||||
isMovingForward, nextSlide, this.onSlideChange_);
|
||||
} else {
|
||||
this.transitionToSlide_(
|
||||
isMovingForward, nextSlide, this.onSlideChange_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions to a slide, doing the scroll animation.
|
||||
* @param {boolean} isMovingForward Whether we're going forward or backwards.
|
||||
* @param {Slide} nextSlide Next slide.
|
||||
* @param {Function} callback Callback to be called upon finishing. This is an
|
||||
* async function so it'll happen once the scroll animation finishes.
|
||||
* @private
|
||||
* @see scrollTo
|
||||
*/
|
||||
scrollTransitionToSlide_(isMovingForward, nextSlide, callback) {
|
||||
this.el.style.overflow = 'none';
|
||||
|
||||
if (!isMovingForward) {
|
||||
nextSlide.moveBeforeFirst();
|
||||
nextSlide.show();
|
||||
scrollTo(this.currentSlide_.el.offsetTop, 0);
|
||||
} else {
|
||||
nextSlide.show();
|
||||
}
|
||||
|
||||
scrollTo(nextSlide.el.offsetTop, 500, () => {
|
||||
this.currentSlide_.hide();
|
||||
|
||||
if (isMovingForward) {
|
||||
this.currentSlide_.moveAfterLast();
|
||||
}
|
||||
|
||||
this.el.style.overflow = 'auto';
|
||||
setTimeout(() => { callback.call(this, nextSlide); }, 150);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions to a slide, without doing the scroll animation.
|
||||
* @param {boolean} isMovingForward Whether we're going forward or backwards.
|
||||
* @param {Slide} nextSlide Next slide.
|
||||
* @param {Function} callback Callback to be called upon finishing. This is a
|
||||
* sync function so it'll happen on run time.
|
||||
* @private
|
||||
*/
|
||||
transitionToSlide_(isMovingForward, nextSlide, callback) {
|
||||
scrollTo(0, 0);
|
||||
|
||||
if (!isMovingForward) {
|
||||
nextSlide.moveBeforeFirst();
|
||||
}
|
||||
|
||||
if (this.currentSlide_) {
|
||||
if (isMovingForward) {
|
||||
this.currentSlide_.moveAfterLast();
|
||||
}
|
||||
|
||||
this.currentSlide_.hide();
|
||||
}
|
||||
|
||||
nextSlide.show();
|
||||
callback.call(this, nextSlide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever a slide is changed, this function gets called. It updates the
|
||||
* references to the current slide, disables the moving flag and fires
|
||||
* a custom event.
|
||||
* @param {Slide} slide The slide we're transitioning to.
|
||||
* @fires WebSlide#ws:slide-change
|
||||
* @private
|
||||
*/
|
||||
onSlideChange_(slide) {
|
||||
this.currentSlide_ = slide;
|
||||
this.currentSlideI_ = slide.i;
|
||||
this.isMoving = false;
|
||||
|
||||
DOM.fireEvent(this.el, 'ws:slide-change', {
|
||||
slides: this.maxSlide_,
|
||||
currentSlide0: this.currentSlideI_,
|
||||
currentSlide: this.currentSlideI_ + 1
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to the next slide.
|
||||
*/
|
||||
goNext() {
|
||||
let nextIndex = this.currentSlideI_ + 1;
|
||||
|
||||
if (nextIndex >= this.maxSlide_) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
this.goToSlide(nextIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to the previous slide.
|
||||
*/
|
||||
goPrev() {
|
||||
let prevIndex = this.currentSlideI_ - 1;
|
||||
|
||||
if (prevIndex < 0) {
|
||||
prevIndex = this.maxSlide_ - 1;
|
||||
}
|
||||
|
||||
this.goToSlide(prevIndex, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given number is a valid index to go to.
|
||||
* @param {number} i The index to check.
|
||||
* @return {boolean} Whether you can move to that slide or not.
|
||||
* @private
|
||||
*/
|
||||
isValidIndexSlide_(i) {
|
||||
return i >= 0 && i < this.maxSlide_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the shown slide on load. It'll fetch it from the Hash if present
|
||||
* and, otherwise, it'll default to the first one.
|
||||
* @private
|
||||
* @see Hash.getSlideNumber
|
||||
*/
|
||||
initSlides_() {
|
||||
let slideNumber = this.plugins.hash.constructor.getSlideNumber();
|
||||
|
||||
// Not valid
|
||||
if (slideNumber === null ||
|
||||
slideNumber >= this.maxSlide_) {
|
||||
slideNumber = 0;
|
||||
}
|
||||
|
||||
// Keeping the order
|
||||
if (slideNumber !== 0) {
|
||||
let i = 0;
|
||||
while(i < slideNumber) {
|
||||
this.slides[i].moveAfterLast();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this.goToSlide(slideNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a plugin to be loaded when the instance is created. It allows
|
||||
* (on purpose) to replace default plugins.
|
||||
* Those being:
|
||||
* - Navigation
|
||||
* - Hash
|
||||
* - Keyboard
|
||||
* @param {!string} key They key under which it'll be stored inside of the
|
||||
* instance, inside the plugins dict.
|
||||
* @param {!Function} cto Plugin constructor.
|
||||
*/
|
||||
static registerPlugin(key, cto) {
|
||||
PLUGINS[key] = cto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts autosliding all the slides if it's not currently doing it and the
|
||||
* autoslide option was a number greater than 0.
|
||||
* @param {?number} time Amount of milliseconds to wait to go to next slide
|
||||
* automatically.
|
||||
*/
|
||||
play(time) {
|
||||
time = time || this.options.autoslide;
|
||||
|
||||
if (!this.interval_ && typeof time === 'number' && time > 0) {
|
||||
this.interval_ = setInterval(this.goNext.bind(this), time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops autosliding all the slides.
|
||||
*/
|
||||
stop() {
|
||||
if (this.interval_) {
|
||||
clearInterval(this.interval_);
|
||||
this.interval_ = null;
|
||||
}
|
||||
}
|
||||
}
|
39
src/js/plugins/click-nav.js
Normal file
39
src/js/plugins/click-nav.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const CLICKABLE_ELS = [
|
||||
'INPUT',
|
||||
'SELECT',
|
||||
'OPTION',
|
||||
'BUTTON',
|
||||
'A',
|
||||
'TEXTAREA'
|
||||
];
|
||||
|
||||
export default class ClickNav {
|
||||
/**
|
||||
* ClickNav plugin that allows to click on the page to get to the next slide.
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
if (wsInstance.options.changeOnClick) {
|
||||
this.ws_.el.addEventListener('click', this.onClick_.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to the click event. It will go to the next slide unless the element
|
||||
* has a data-prevent-nav attribute or is on the list of CLICKABLE_ELS.
|
||||
* @param {MouseEvent} event The click event.
|
||||
* @private
|
||||
*/
|
||||
onClick_(event) {
|
||||
if (CLICKABLE_ELS.indexOf(event.target.tagName) < 0 &&
|
||||
typeof event.target.dataset.preventNav === 'undefined') {
|
||||
this.ws_.goNext();
|
||||
}
|
||||
}
|
||||
}
|
44
src/js/plugins/grid.js
Normal file
44
src/js/plugins/grid.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import Keys from '../utils/keys';
|
||||
|
||||
export default class Grid {
|
||||
/**
|
||||
* Grid plugin that shows a grid on top of the WebSlides for easy prototyping.
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
const CSS = `body.baseline {
|
||||
background: url(../images/baseline.png) left top .8rem/.8rem;
|
||||
}`;
|
||||
const head = document.head || document.getElementsByTagName('head')[0];
|
||||
const style = document.createElement('style');
|
||||
|
||||
style.type = 'text/css';
|
||||
|
||||
if (style.styleSheet){
|
||||
style.styleSheet.cssText = CSS;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(CSS));
|
||||
}
|
||||
|
||||
head.appendChild(style);
|
||||
|
||||
document.addEventListener('keydown', this.onKeyPress_.bind(this), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to the keydown event. It reacts to ENTER key to toggle the class.
|
||||
* @param {KeyboardEvent} event The key event.
|
||||
* @private
|
||||
*/
|
||||
onKeyPress_(event) {
|
||||
if (event.which === Keys.ENTER) {
|
||||
document.body.toggleClass('baseline');
|
||||
}
|
||||
}
|
||||
}
|
71
src/js/plugins/hash.js
Normal file
71
src/js/plugins/hash.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const HASH = '#slide';
|
||||
const slideRegex = /#slide=(\d+)/;
|
||||
|
||||
/**
|
||||
* Static class with methods to manipulate and extract info from the hash of
|
||||
* the URL.
|
||||
*/
|
||||
export default class Hash {
|
||||
/**
|
||||
* Listens to the slide change event and the hash change events.
|
||||
* @param wsInstance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
wsInstance.el.addEventListener('ws:slide-change', Hash.onSlideChange_);
|
||||
window.addEventListener('hashchange', this.onHashChange_.bind(this), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* hashchange event handler, makes the WebSlide instance navigate to the
|
||||
* needed slide.
|
||||
*/
|
||||
onHashChange_() {
|
||||
const newSlideIndex = Hash.getSlideNumber();
|
||||
|
||||
if (newSlideIndex !== null) {
|
||||
this.ws_.goToSlide(newSlideIndex);
|
||||
}
|
||||
}
|
||||
|
||||
static onSlideChange_(event) {
|
||||
Hash.setSlideNumber(event.detail.currentSlide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the slide number from the hash by a regex matching `#slide=` and gets
|
||||
* the number after it. If the number is invalid or less than 0, it will
|
||||
* return null as an invalid value.
|
||||
* @return {?number}
|
||||
*/
|
||||
static getSlideNumber() {
|
||||
let results = document.location.hash.match(slideRegex);
|
||||
let slide = 0;
|
||||
|
||||
if (Array.isArray(results)) {
|
||||
slide = parseInt(results[1], 10);
|
||||
}
|
||||
|
||||
if (typeof slide !== 'number' || slide < 0 || !Array.isArray(results)) {
|
||||
slide = null;
|
||||
} else {
|
||||
slide--; // Convert to 0 index
|
||||
}
|
||||
|
||||
return slide;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will update the hash (if it's different) so it reflects the slide
|
||||
* number being visible.
|
||||
* @param {number} number The number of the slide we're transitioning to.
|
||||
*/
|
||||
static setSlideNumber(number) {
|
||||
if (Hash.getSlideNumber() !== (number - 1)) {
|
||||
history.pushState({
|
||||
slideI: number - 1
|
||||
}, `Slide ${number}`, `${HASH}=${number}`);
|
||||
}
|
||||
}
|
||||
}
|
74
src/js/plugins/keyboard.js
Normal file
74
src/js/plugins/keyboard.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import Keys from '../utils/keys';
|
||||
|
||||
export default class Keyboard {
|
||||
/**
|
||||
* Keyboard interaction plugin.
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
document.addEventListener('keydown', this.onKeyPress_.bind(this), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to the keydown event. It reacts to the arrows and space key
|
||||
* depending on the layout of the page.
|
||||
* @param {KeyboardEvent} event The key event.
|
||||
* @private
|
||||
*/
|
||||
onKeyPress_(event) {
|
||||
let method;
|
||||
let argument;
|
||||
|
||||
// Check if there's a focused element that might use the keyboard.
|
||||
if (document.activeElement) {
|
||||
const isContentEditable = document.activeElement
|
||||
.contentEditable !== 'inherit';
|
||||
const isInput = ['INPUT', 'SELECT', 'OPTION', 'TEXTAREA']
|
||||
.indexOf(document.activeElement.tagName) > -1;
|
||||
|
||||
if (isInput || isContentEditable) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
case Keys.AV_PAGE:
|
||||
case Keys.SPACE:
|
||||
method = this.ws_.goNext;
|
||||
break;
|
||||
case Keys.RE_PAGE:
|
||||
method = this.ws_.goPrev;
|
||||
break;
|
||||
case Keys.HOME:
|
||||
method = this.ws_.goToSlide;
|
||||
argument = 0;
|
||||
break;
|
||||
case Keys.END:
|
||||
method = this.ws_.goToSlide;
|
||||
argument = this.ws_.maxSlide_ - 1;
|
||||
break;
|
||||
case Keys.DOWN:
|
||||
method = this.ws_.isVertical ? this.ws_.goNext : null;
|
||||
break;
|
||||
case Keys.UP:
|
||||
method = this.ws_.isVertical ? this.ws_.goPrev : null;
|
||||
break;
|
||||
case Keys.LEFT:
|
||||
method = !this.ws_.isVertical ? this.ws_.goPrev : null;
|
||||
break;
|
||||
case Keys.RIGHT:
|
||||
method = !this.ws_.isVertical ? this.ws_.goNext : null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (method) {
|
||||
method.call(this.ws_, argument);
|
||||
}
|
||||
}
|
||||
}
|
120
src/js/plugins/navigation.js
Normal file
120
src/js/plugins/navigation.js
Normal file
@@ -0,0 +1,120 @@
|
||||
import DOM from '../utils/dom';
|
||||
|
||||
const ELEMENT_ID = {
|
||||
NAV: 'navigation',
|
||||
NEXT: 'next',
|
||||
PREV: 'previous',
|
||||
COUNTER: 'counter'
|
||||
};
|
||||
|
||||
const LABELS = {
|
||||
VERTICAL: {
|
||||
NEXT: '↓',
|
||||
PREV: '↑'
|
||||
},
|
||||
HORIZONTAL: {
|
||||
NEXT: '→',
|
||||
PREV: '←'
|
||||
}
|
||||
};
|
||||
|
||||
export default class Navigation {
|
||||
/**
|
||||
* The Navigation constructor. It'll create all the nodes needed for the
|
||||
* navigation such as the arrows and the counter.
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
const arrowLabels = wsInstance.isVertical ?
|
||||
LABELS.VERTICAL : LABELS.HORIZONTAL;
|
||||
/**
|
||||
* Navigation element.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.el = DOM.createNode('div', 'navigation');
|
||||
/**
|
||||
* Next button.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.next = Navigation.createArrow(ELEMENT_ID.NEXT, arrowLabels.NEXT);
|
||||
/**
|
||||
* Prev button.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.prev = Navigation.createArrow(ELEMENT_ID.PREV, arrowLabels.PREV);
|
||||
/**
|
||||
* Counter Element.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.counter = DOM.createNode('span', ELEMENT_ID.COUNTER);
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
this.el.appendChild(this.next);
|
||||
this.el.appendChild(this.prev);
|
||||
this.el.appendChild(this.counter);
|
||||
|
||||
this.ws_.el.appendChild(this.el);
|
||||
this.bindEvents_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all events for the navigation.
|
||||
* @private
|
||||
*/
|
||||
bindEvents_() {
|
||||
this.ws_.el.addEventListener(
|
||||
'ws:slide-change', this.onSlideChanged_.bind(this));
|
||||
this.next.addEventListener('click', this.onButtonClicked_.bind(this));
|
||||
this.prev.addEventListener('click', this.onButtonClicked_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the counter inside the navigation.
|
||||
* @param {string|number} current Current slide number.
|
||||
* @param {string|number} max Max slide number.
|
||||
*/
|
||||
updateCounter(current, max) {
|
||||
this.counter.textContent = `${current} / ${max}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an arrow to navigate.
|
||||
* @param {!String} id Desired ID for the arrow.
|
||||
* @param {!String} text Desired text for the arrow.
|
||||
* @return {Element} The arrow element.
|
||||
*/
|
||||
static createArrow(id, text) {
|
||||
const arrow = DOM.createNode('a', id, text);
|
||||
arrow.href = '#';
|
||||
arrow.title = 'Arrow Keys';
|
||||
|
||||
return arrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slide Change event handler. Will update the text on the navigation.
|
||||
* @param {CustomEvent} event
|
||||
* @private
|
||||
*/
|
||||
onSlideChanged_(event) {
|
||||
this.updateCounter(event.detail.currentSlide, event.detail.slides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on the next/prev buttons.
|
||||
* @param {MouseEvent} event
|
||||
* @private
|
||||
*/
|
||||
onButtonClicked_(event) {
|
||||
event.preventDefault();
|
||||
if (event.target === this.next) {
|
||||
this.ws_.goNext();
|
||||
} else {
|
||||
this.ws_.goPrev();
|
||||
}
|
||||
}
|
||||
}
|
17
src/js/plugins/plugins.js
Normal file
17
src/js/plugins/plugins.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import ClickNav from './click-nav';
|
||||
import Grid from './grid';
|
||||
import Hash from './hash';
|
||||
import Keyboard from './keyboard';
|
||||
import Navigation from './navigation';
|
||||
import Scroll from './scroll';
|
||||
import Touch from './touch';
|
||||
|
||||
export default {
|
||||
ClickNav,
|
||||
Grid,
|
||||
Hash,
|
||||
Keyboard,
|
||||
Navigation,
|
||||
Scroll,
|
||||
Touch
|
||||
};
|
103
src/js/plugins/scroll.js
Normal file
103
src/js/plugins/scroll.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import MobileDetector from '../utils/mobile-detector';
|
||||
|
||||
export default class Scroll {
|
||||
/**
|
||||
* Scroll handler for the WebSlides.
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
/**
|
||||
* Where the scroll is going to happen. The WebSlides element.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.scrollContainer_ = wsInstance.el;
|
||||
/**
|
||||
* Whether movement is happening up or down.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isGoingUp_ = false;
|
||||
/**
|
||||
* Whether movement is happening left or right.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isGoingLeft_ = false;
|
||||
/**
|
||||
* Timeout id holder.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
this.timeout_ = null;
|
||||
|
||||
if (!MobileDetector.isAny()) {
|
||||
this.scrollContainer_.addEventListener(
|
||||
'wheel', this.onMouseWheel_.bind(this));
|
||||
|
||||
if (!wsInstance.isVertical) {
|
||||
wsInstance.el.addEventListener(
|
||||
'ws:slide-change', this.onSlideChange_.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the slides change, set an inner timeout to avoid prematurely
|
||||
* changing to the next slide again.
|
||||
* @private
|
||||
*/
|
||||
onSlideChange_() {
|
||||
this.timeout_ = setTimeout(
|
||||
() => { this.timeout_ = null; },
|
||||
this.ws_.options.scrollWait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to the wheel event. Detects whether is going up or down and decides
|
||||
* if it needs to move the slide based on the amount of delta.
|
||||
* @param {WheelEvent} event The Wheel Event.
|
||||
* @private
|
||||
*/
|
||||
onMouseWheel_(event) {
|
||||
if (this.ws_.isMoving || this.timeout_) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const { deltaY: wheelDeltaY, deltaX: wheelDeltaX } = event;
|
||||
const isVertical = this.ws_.isVertical;
|
||||
const isHorizontalMovement = Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY);
|
||||
this.isGoingUp_ = wheelDeltaY < 0;
|
||||
this.isGoingLeft_ = wheelDeltaX < 0;
|
||||
|
||||
|
||||
// If we're mainly moving horizontally, prevent default
|
||||
if (isHorizontalMovement) {
|
||||
if (!isVertical) {
|
||||
event.preventDefault();
|
||||
} else {
|
||||
// If we're moving horizontally but this is vertical, return to avoid
|
||||
// unwanted navigation.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(wheelDeltaY) >= this.ws_.options.minWheelDelta ||
|
||||
Math.abs(wheelDeltaX) >= this.ws_.options.minWheelDelta) {
|
||||
if ((isHorizontalMovement && this.isGoingLeft_) ||
|
||||
(!isHorizontalMovement && this.isGoingUp_)) {
|
||||
this.ws_.goPrev();
|
||||
} else {
|
||||
this.ws_.goNext();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
149
src/js/plugins/touch.js
Normal file
149
src/js/plugins/touch.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import MobileDetector from '../utils/mobile-detector';
|
||||
|
||||
const EVENTS = {
|
||||
touch: {
|
||||
START: 'touchstart',
|
||||
MOVE: 'touchmove',
|
||||
END: 'touchend'
|
||||
},
|
||||
pointer: {
|
||||
START: 'pointerdown',
|
||||
MOVE: 'pointermove',
|
||||
END: 'pointerup'
|
||||
}
|
||||
};
|
||||
|
||||
export default class Touch {
|
||||
/**
|
||||
* @param {WebSlides} wsInstance The WebSlides instance
|
||||
*/
|
||||
constructor(wsInstance) {
|
||||
/**
|
||||
* @type {WebSlides}
|
||||
* @private
|
||||
*/
|
||||
this.ws_ = wsInstance;
|
||||
|
||||
/**
|
||||
* Start position for the X coord.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.startX_ = 0;
|
||||
|
||||
/**
|
||||
* Start position for the Y coord.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.startY_ = 0;
|
||||
|
||||
/**
|
||||
* Start position for the X coord.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.endX_ = 0;
|
||||
|
||||
/**
|
||||
* Start position for the Y coord.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.endY_ = 0;
|
||||
|
||||
/**
|
||||
* Whether is enabled or not. Only enabled for touch devices.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isEnabled = false;
|
||||
|
||||
let events;
|
||||
|
||||
if (MobileDetector.isAny()) {
|
||||
// Likely IE
|
||||
if (window.PointerEvent && (
|
||||
MobileDetector.isWindows() || MobileDetector.isWindowsPhone())) {
|
||||
events = EVENTS.pointer;
|
||||
} else {
|
||||
events = EVENTS.touch;
|
||||
}
|
||||
|
||||
this.isEnabled = true;
|
||||
document.addEventListener(events.START, this.onStart_.bind(this), false);
|
||||
document.addEventListener(events.MOVE, this.onMove_.bind(this), false);
|
||||
document.addEventListener(events.MOVE, this.onMove_.bind(this), false);
|
||||
document.addEventListener(events.END, this.onStop_.bind(this), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start touch handler. Saves starting points.
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
onStart_(event) {
|
||||
const info = Touch.normalizeEventInfo(event);
|
||||
|
||||
this.startX_ = info.x;
|
||||
this.startY_ = info.y;
|
||||
this.endX_ = info.x;
|
||||
this.endY_ = info.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move touch handler. Saves end points.
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
onMove_(event) {
|
||||
const info = Touch.normalizeEventInfo(event);
|
||||
|
||||
this.endX_ = info.x;
|
||||
this.endY_ = info.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop touch handler. Checks if it needs to make any actions.
|
||||
* @private
|
||||
*/
|
||||
onStop_() {
|
||||
const diffX = this.startX_ - this.endX_;
|
||||
const diffY = this.startY_ - this.endY_;
|
||||
|
||||
// It's an horizontal drag
|
||||
if (Math.abs(diffX) > Math.abs(diffY)) {
|
||||
if (diffX < -this.ws_.options.slideOffset) {
|
||||
this.ws_.goPrev();
|
||||
} else if(diffX > this.ws_.options.slideOffset) {
|
||||
this.ws_.goNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an event to deal with differences between PointerEvent and
|
||||
* TouchEvent.
|
||||
* @param event
|
||||
* @return {*}
|
||||
*/
|
||||
static normalizeEventInfo(event) {
|
||||
let x;
|
||||
let y;
|
||||
let touchEvent = { pageX : 0, pageY : 0};
|
||||
|
||||
if (typeof event.changedTouches !== 'undefined'){
|
||||
touchEvent = event.changedTouches[0];
|
||||
}
|
||||
else if (typeof event.originalEvent !== 'undefined' &&
|
||||
typeof event.originalEvent.changedTouches !== 'undefined'){
|
||||
touchEvent = event.originalEvent.changedTouches[0];
|
||||
}
|
||||
|
||||
x = event.offsetX || event.layerX || touchEvent.pageX;
|
||||
y = event.offsetY || event.layerY || touchEvent.pageY;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
};
|
38
src/js/utils/custom-event.js
Normal file
38
src/js/utils/custom-event.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const NativeCustomEvent = window.CustomEvent;
|
||||
|
||||
/**
|
||||
* Check for the usage of native support for CustomEvents which is lacking
|
||||
* completely on IE.
|
||||
* @return {boolean} Whether it can be used or not.
|
||||
*/
|
||||
function canIuseNativeCustom () {
|
||||
try {
|
||||
const p = new NativeCustomEvent('t', { detail: { a: 'b' } });
|
||||
return 't' === p.type && 'b' === p.detail.a;
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lousy polyfill for the Custom Event constructor for IE.
|
||||
* @param {!string} type The type of the event.
|
||||
* @param {?Object} params Additional information for the event.
|
||||
* @return {Event}
|
||||
* @constructor
|
||||
*/
|
||||
const IECustomEvent = function CustomEvent(type, params) {
|
||||
const e = document.createEvent('CustomEvent');
|
||||
|
||||
if (params) {
|
||||
e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
|
||||
} else {
|
||||
e.initCustomEvent(type, false, false, undefined);
|
||||
}
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
const WSCustomEvent = canIuseNativeCustom() ? NativeCustomEvent : IECustomEvent;
|
||||
|
||||
export default WSCustomEvent;
|
68
src/js/utils/dom.js
Normal file
68
src/js/utils/dom.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import WSCustomEvent from './custom-event';
|
||||
|
||||
|
||||
/**
|
||||
* Static class for DOM helper.
|
||||
*/
|
||||
export default class DOM {
|
||||
/**
|
||||
* Creates a node with optional parameters.
|
||||
* @param {string} tag The name of the tag of the needed element.
|
||||
* @param {string} id The desired id for the element. It defaults to an
|
||||
* empty string.
|
||||
* @param {string} text The desired text to go inside of the element. It defaults
|
||||
* to an empty string.
|
||||
* @return {Element}
|
||||
*/
|
||||
static createNode(tag, id = '', text = '') {
|
||||
const node = document.createElement(tag);
|
||||
node.id = id;
|
||||
|
||||
if (text) {
|
||||
node.textContent = text;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides an element setting the display to none.
|
||||
* @param {Element} el Element to be hidden.
|
||||
*/
|
||||
static hide(el) {
|
||||
el.style.display = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an element by removing the display property. This is only intended
|
||||
* to be used in conjunction with DOM.hide.
|
||||
* @param {Element} el Element to be shown.
|
||||
*/
|
||||
static show(el) {
|
||||
el.style.display = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a custom event on the given target.
|
||||
* @param {Element} target The target of the event.
|
||||
* @param {string} eventType The event type.
|
||||
* @param {Object} eventInfo Optional parameter to provide additional data
|
||||
* to the event.
|
||||
*/
|
||||
static fireEvent(target, eventType, eventInfo = {}) {
|
||||
const event = new WSCustomEvent(eventType, {
|
||||
detail: eventInfo
|
||||
});
|
||||
|
||||
target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an iterable to an array.
|
||||
* @param {*} iterable Element to convert to array
|
||||
* @return {Array} the element casted to an array.
|
||||
*/
|
||||
static toArray(iterable) {
|
||||
return [].slice.call(iterable);
|
||||
}
|
||||
}
|
19
src/js/utils/easing.js
Normal file
19
src/js/utils/easing.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Swing easing function.
|
||||
* @param {number} p The percentage of time that has passed.
|
||||
* @return {number}
|
||||
*/
|
||||
function swing (p) {
|
||||
return 0.5 - Math.cos(p * Math.PI) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear easing function.
|
||||
* @param {number} p The percentage of time that has passed.
|
||||
* @return {number}
|
||||
*/
|
||||
function linear(p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
export default { swing, linear };
|
14
src/js/utils/keys.js
Normal file
14
src/js/utils/keys.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const Keys = {
|
||||
ENTER: 13,
|
||||
SPACE: 32,
|
||||
RE_PAGE: 33,
|
||||
AV_PAGE: 34,
|
||||
END: 35,
|
||||
HOME: 36,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40
|
||||
};
|
||||
|
||||
export default Keys;
|
64
src/js/utils/mobile-detector.js
Normal file
64
src/js/utils/mobile-detector.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const UA = window.navigator.userAgent;
|
||||
|
||||
export default class MobileDetector {
|
||||
/**
|
||||
* Whether the device is Android or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isAndroid() {
|
||||
return !!UA.match(/Android/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is BlackBerry or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isBlackBerry() {
|
||||
return !!UA.match(/BlackBerry/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is iOS or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isiOS() {
|
||||
return !!UA.match(/iPhone/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is Opera or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isOpera() {
|
||||
return !!UA.match(/Opera Mini/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is Windows or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isWindows() {
|
||||
return !!UA.match(/IEMobile/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is Windows Phone or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isWindowsPhone() {
|
||||
return !!UA.match(/Windows Phone/i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device is any mobile device or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isAny() {
|
||||
return MobileDetector.isAndroid() ||
|
||||
MobileDetector.isBlackBerry() ||
|
||||
MobileDetector.isiOS() ||
|
||||
MobileDetector.isOpera() ||
|
||||
MobileDetector.isWindows() ||
|
||||
MobileDetector.isWindowsPhone();
|
||||
}
|
||||
}
|
44
src/js/utils/scroll-to.js
Normal file
44
src/js/utils/scroll-to.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import Easings from './easing';
|
||||
|
||||
let SCROLLABLE_CONTAINER = document.getElementById('webslides');
|
||||
|
||||
/**
|
||||
* Smoothly scrolls to a given Y position using Easing.Swing. It'll run a
|
||||
* callback upon finishing.
|
||||
* @param {number} y Offset of the page to scroll to.
|
||||
* @param {number} duration Duration of the animation. 500ms by default.
|
||||
* @param {function} cb Callback function to call upon completion.
|
||||
*/
|
||||
export default function scrollTo(y, duration = 500, cb = () => {}) {
|
||||
const delta = y - SCROLLABLE_CONTAINER.scrollTop;
|
||||
const startLocation = SCROLLABLE_CONTAINER.scrollTop;
|
||||
const increment = 16;
|
||||
|
||||
if (!duration) {
|
||||
SCROLLABLE_CONTAINER.scrollTop = y;
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
const animateScroll = elapsedTime => {
|
||||
elapsedTime += increment;
|
||||
const percent = Math.min(1, elapsedTime / duration);
|
||||
const easingP = Easings.swing(
|
||||
percent,
|
||||
elapsedTime * percent,
|
||||
y,
|
||||
delta,
|
||||
duration);
|
||||
|
||||
SCROLLABLE_CONTAINER.scrollTop = Math.floor(startLocation +
|
||||
(easingP * delta));
|
||||
|
||||
if (elapsedTime < duration) {
|
||||
setTimeout(() => animateScroll(elapsedTime), increment);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
animateScroll(0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -81,27 +81,27 @@ hr:after {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
bbbr,
|
||||
abbr,
|
||||
acronym {
|
||||
border-bottom: 1px dotted #333;
|
||||
}
|
||||
|
||||
mark,
|
||||
ins {
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
color: inherit;
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
}
|
||||
|
||||
::-webkit-selection {
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
background-color: rgba(221,238,255, 0.8);
|
||||
}
|
||||
|
||||
pre {
|
||||
@@ -119,7 +119,7 @@ code,[class*="bg-"] pre {
|
||||
}
|
||||
|
||||
.bg-white code{
|
||||
background: rgba(0, 20, 80, 0.03);
|
||||
background: rgba(0, 20, 80, 0.03);
|
||||
}
|
||||
/*================================================
|
||||
Slides - Backgrounds <section class="bg-primary">
|
||||
@@ -218,7 +218,7 @@ Slides - Backgrounds <section class="bg-primary">
|
||||
|
||||
/*Covers/Longforms...*/
|
||||
.bg-trans-gradient{
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%,rgba(0,0,0,0) 100%);
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%,rgba(0,0,0,0) 100%);
|
||||
}
|
||||
|
||||
/*Horizontal Gradient*/
|
||||
@@ -250,9 +250,9 @@ background: linear-gradient(to top, rgba(0,0,0,0.8) 0%,rgba(0,0,0,0) 100%);
|
||||
|
||||
/*Gray Gradient (horizontal)*/
|
||||
.bg-gradient-gray{
|
||||
background: linear-gradient(90deg,#f7f9fb 0,#dee2e6 100%);
|
||||
color: #333;
|
||||
text-shadow: none;
|
||||
background: linear-gradient(90deg,#f7f9fb 0,#dee2e6 100%);
|
||||
color: #333;
|
||||
text-shadow: none;
|
||||
}
|
||||
/*Border/Frame*/
|
||||
.frame {
|
||||
@@ -261,7 +261,7 @@ text-shadow: none;
|
||||
|
||||
/*Layer/Box Shadow*/
|
||||
.shadow,.pre {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
.shadow:before,.shadow:after {
|
||||
box-shadow: 0 16px 24px rgba(0, 20, 80, 0.3);
|
||||
@@ -274,13 +274,13 @@ TYPOGRAPHY
|
||||
/* -- Horizontal separator -- */
|
||||
|
||||
.text-separator:before {
|
||||
background-color: rgba(170, 0, 0, 0.8);
|
||||
background-color: rgba(170, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* -- Pull Quote (Right/Left) -- */
|
||||
|
||||
[class*="text-pull-"] {
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.5);
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
/* -- Context -- */
|
||||
|
||||
@@ -374,7 +374,7 @@ nav li.email a:hover {
|
||||
}
|
||||
|
||||
/*=========================================
|
||||
Features & Clients List
|
||||
Features & Clients List
|
||||
=========================================== */
|
||||
|
||||
.features li,.clients li {
|
||||
@@ -387,7 +387,7 @@ Features & Clients List
|
||||
}
|
||||
|
||||
.features li:hover,.clients li:hover {
|
||||
box-shadow: 0 8px 16px rgba(0,20,80,.02),0 4px 16px rgba(0,0,0,.08);
|
||||
box-shadow: 0 8px 16px rgba(0,20,80,.02),0 4px 16px rgba(0,0,0,.08);
|
||||
}
|
||||
/*.features li span,.features li svg{color: #44d;}*/
|
||||
|
||||
@@ -410,7 +410,7 @@ box-shadow: 0 8px 16px rgba(0,20,80,.02),0 4px 16px rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
/*===========================================
|
||||
flexblock.steps
|
||||
flexblock.steps
|
||||
============================================= */
|
||||
|
||||
.steps li:nth-child(1) {
|
||||
@@ -519,7 +519,7 @@ Gallery li+.overlay+image
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 4px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.gallery li footer {
|
||||
border-top:1px solid rgba(0,20,80,0.1);
|
||||
border-top:1px solid rgba(0,20,80,0.1);
|
||||
}
|
||||
|
||||
.gallery li a {
|
||||
@@ -684,7 +684,7 @@ Cards
|
||||
/*== Figure Background === */
|
||||
|
||||
[class*="card-"][class*="bg-"] figure {
|
||||
background-color: rgba(0, 20, 80, 0.06);
|
||||
background-color: rgba(0, 20, 80, 0.06);
|
||||
}
|
||||
|
||||
/*== Ficaption Cards === */
|
||||
@@ -703,7 +703,7 @@ background-color: rgba(0, 20, 80, 0.06);
|
||||
border-image: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.4) 50%, transparent) 1 100%;
|
||||
-webkit-border-image: -webkit-linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.4) 50%, transparent) 1 100%;
|
||||
-moz-border-image: -moz-linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.4) 50%, transparent) 1 100%;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +737,7 @@ tr:nth-child(even)>td:hover {
|
||||
|
||||
|
||||
/*============================
|
||||
Browser (Screenshots)
|
||||
Browser (Screenshots)
|
||||
============================== */
|
||||
|
||||
.browser {
|
||||
@@ -783,10 +783,10 @@ input:focus::-moz-placeholder {
|
||||
input:focus::-webkit-input-placeholder {
|
||||
color: #ddd;
|
||||
}
|
||||
a.button,[class*="badge-"]
|
||||
a.button,[class*="badge-"],
|
||||
button[type="submit"],
|
||||
input {
|
||||
box-shadow: 0px 10px 16px -8px rgba(0, 20, 80, 0.3);
|
||||
box-shadow: 0 10px 16px -8px rgba(0, 20, 80, 0.3);
|
||||
}
|
||||
|
||||
button,
|
||||
@@ -797,7 +797,7 @@ button[type="submit"],
|
||||
input[type="submit"],
|
||||
.button,.button:hover,
|
||||
button[type="submit"]:hover,
|
||||
input[type="submit"]:hover
|
||||
input[type="submit"]:hover
|
||||
{
|
||||
border: 1px solid #44d;
|
||||
}
|
||||
@@ -810,7 +810,7 @@ input[type="submit"],
|
||||
text-shadow: 0 1px 0 #123;
|
||||
}
|
||||
.button:active,button[type="submit"]:active,input[type="submit"]:active {
|
||||
background-color: #17d;
|
||||
background-color: #17d;
|
||||
}
|
||||
.ghost,.ghost:hover {background: none;color: inherit;text-shadow: none;}
|
||||
.bg-primary select,
|
||||
@@ -818,7 +818,7 @@ background-color: #17d;
|
||||
.bg-primary .button,
|
||||
.bg-primary button,.bg-primary button:hover,
|
||||
.bg-primary input,
|
||||
[class*="bg-gradient-"] .button,[class*="bg-"] a.button.ghost
|
||||
[class*="bg-gradient-"] .button,[class*="bg-"] a.button.ghost
|
||||
{
|
||||
border-color: #fff;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
9
static/js/webslides.min.js
vendored
Normal file
9
static/js/webslides.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
37
webpack.config.babel.js
Normal file
37
webpack.config.babel.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const SmartBannerPlugin = require('smart-banner-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const src = path.join(__dirname, 'src');
|
||||
const pkg = require('./package.json');
|
||||
|
||||
module.exports = {
|
||||
context: src,
|
||||
entry: {
|
||||
webslides: './js/full.js'
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.join(__dirname, 'static/js'),
|
||||
publicPath: '/static/js/'
|
||||
},
|
||||
devServer: {
|
||||
contentBase: __dirname,
|
||||
host: '0.0.0.0'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: src
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new SmartBannerPlugin({
|
||||
banner: `Name: WebSlides\nVersion: ${pkg.version}\nDate: ${new Date().toISOString().slice(0,10)}\nDescription: ${pkg.description}\nURL: ${pkg.homepage}\nCredits: @jlantunez, @LuisSacristan, @Belelros`,
|
||||
raw: false,
|
||||
entryOnly: true
|
||||
})
|
||||
],
|
||||
};
|
Reference in New Issue
Block a user