Commit ccb06785 authored by Vadim Makeev's avatar Vadim Makeev

Now this repository is rather template. Shower.js is moved to core repo

parent c5b028b4
......@@ -7,3 +7,7 @@ end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = false
charset = utf-8
[package.json]
indent_style = space
indent_size = 2
\ No newline at end of file
[submodule "themes/ribbon"]
path = themes/ribbon
url = git://github.com/shower/ribbon.git
[submodule "themes/bright"]
path = themes/bright
url = git://github.com/shower/bright.git
.editorconfig
.gitignore
Contributing.md
\ No newline at end of file
module.exports = function(grunt) {
grunt.initConfig({
uglify: {
options: {
mangle: true,
banner: '/**\n * Shower HTML presentation engine: github.com/shower/shower\n * @copyright 2010–<%= grunt.template.today("yyyy") %> Vadim Makeev, pepelsbey.net\n * @license MIT license: github.com/shower/shower/wiki/MIT-License\n */\n'
},
build: {
src: 'shower.js',
dest: 'shower.min.js'
}
},
connect: {
ribbon: {
options: {
port: 7497
}
}
},
dalek: {
options: {
browser: ['chrome']
},
src: [
'tests/*.js'
]
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-dalek');
grunt.registerTask('default', ['uglify']);
grunt.registerTask('test', ['connect', 'dalek']);
};
\ No newline at end of file
# Shower
# Shower HTML presentation engine
HTML presentation engine. Follow [@shower_me](http://twitter.com/shower_me/) for support and updates.
Follow [@shower_me](https://twitter.com/shower_me) for support and updates
To see Shower in action:
- Open [shwr.me](http://shwr.me/)
- Click any slide to enter presentation mode
- Use arrow keys to navigate
- Press `Esc` to exit presentation mode
- Open [shwr.me](http://shwr.me/).
- Click any slide to enter presentation mode.
- Use arrow keys or presenter remote to navigate.
- Press `Esc` to exit presentation mode.
See more details on [using keyboard](https://github.com/shower/shower/wiki/Shortcuts) to control Shower.
See [Shortcuts](https://github.com/shower/shower/wiki/Shortcuts)” wiki page for more info on how to control your presentation, “[Print](https://github.com/shower/shower/wiki/Print)” on how to print your presentation to PDF and [the rest of the Wiki](https://github.com/shower/shower/wiki) for more information about Shower in English and Russian.
## Using Shower
There are two ways of using Shower: you can get full template [ZIP 1.7 MB](http://shwr.me/template.zip) or just an engine [ZIP 915 KB](http://shwr.me/shower.zip). See more details for beginners and advanced techniques in [Quick Start](https://github.com/shower/shower/wiki/Quick-Start) instructions.
There are two ways of starting with Shower: simple and advanced.
## Printing to PDF
### Simple way
You can print your presentation to PDF using Google Chrome “Print to PDF” option in print dialog or [wkhtmltopdf](http://code.google.com/p/wkhtmltopdf) or [Prince](http://princexml.com).
Example of Shower template printed by Prince: [PDF 435 KB](https://github.com/shower/template/blob/master/index.pdf?raw=true). See more details on printing in [documentation](https://github.com/shower/shower/wiki/Print).
1. Download [shwr.me/shower.zip](http://shwr.me/shower.zip) and unzip it.
2. Open `index.html` in any code or plain text editor, edit your slides in HTML.
3. Use `picture` folder to store pictures used in presentation.
4. Once finished, open `index.html` in browser, enter full screen and start presenting.
See “[Quick Start](https://github.com/shower/shower/wiki/Quick-Start)” page for more details.
### Advanced way
This way requires [Git](http://git-scm.com) and [Node.js](http://nodejs.org) with npm installed.
1. Clone Shower repository with Git `git clone git@github.com:shower/shower.git`.
2. Run `npm install` inside folder to install script and themes.
3. Open `index.html` in any code or plain text editor, edit your slides in HTML.
4. Use `picture` folder to store pictures used in presentation.
5. Once finished, open `index.html` in browser, enter full screen and start presenting.
If you prefer bower then clone Shower this way `git clone git@github.com:shower/shower.git -b bower` and run `bower install` on 2nd step.
See “[Quick Start](https://github.com/shower/shower/wiki/Quick-Start)” page for more details.
## Usage examples
- [Clear and Sharp](http://pepelsbey.net/pres/clear-and-sharp/)
- [CSS Management](http://pepelsbey.net/pres/css-management/)
- [CSS Management](http://pepelsbey.net/pres/knife-train/)
- [Push it!](http://pepelsbey.net/pres/push-it/)
- [Pre-fixes](http://pepelsbey.net/pres/pre-fixes/)
- [Web In Curves](http://pepelsbey.net/pres/web-in-curves/)
- [Sense Coding](http://pepelsbey.net/pres/sense-coding/)
- [Dynamic Graphics](http://pepelsbey.net/pres/dynamic-graphics/)
## Browser support
Supported desktop browsers: Chrome, Internet Explorer, Firefox, Opera, Safari. Only latest stable versions of mentioned browsers are supported.
Latest stable versions of Chrome, Internet Explorer, Firefox, Opera and Safari are supported.
## Contributing
You're always welcome to contibute. Fork project, make changes and send it as pull request. But it's better to file an [issue](https://github.com/shower/shower/issues) with your idea first. Read [contributing rules](https://github.com/shower/shower/blob/master/Contributing.md) for more details.
You’re always welcome to contibute. Fork project, make changes and send it as pull request. But it’s better to file an [issue](https://github.com/shower/shower/issues) with your idea first. Read [contributing rules](https://github.com/shower/shower/blob/master/Contributing.md) for more details.
Main contributors: [pepelsbey](https://github.com/pepelsbey), [jahson](https://github.com/jahson), [miripiruni](https://github.com/miripiruni), [kizu](https://github.com/kizu).
Main contributors: [pepelsbey](https://github.com/pepelsbey), [jahson](https://github.com/jahson), [miripiruni](https://github.com/miripiruni), [kizu](https://github.com/kizu), [artpolikarpov](https://github.com/artpolikarpov), [tonyganch](https://github.com/tonyganch).
---
Licensed under [MIT License](http://en.wikipedia.org/wiki/MIT_License), see [license page](https://github.com/shower/shower/wiki/MIT-License) for details.
\ No newline at end of file
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Shower Presentation Engine</title>
<meta charset="utf-8">
<meta name="viewport" content="width=792, user-scalable=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="stylesheet" href="node_modules/shower-ribbon/styles/screen.css">
</head>
<body class="list">
<header class="caption">
<h1>Shower Presentation Engine</h1>
<p>Yours Truly, Famous Inc.</p>
</header>
<section class="slide cover" id="Cover"><div>
<h2>Shower Presentation Engine</h2>
<p>Brought to you by <a href="http://pepelsbey.net">Vadim Makeev</a></p>
<img src="pictures/cover.jpg" alt="">
<!--
To apply styles to the certain slides
set slide ID to get needed elements
-->
<style>
#Cover h2 {
margin:30px 0 0;
color:#FFF;
text-align:center;
font-size:70px;
}
#Cover p {
margin:10px 0 0;
text-align:center;
color:#FFF;
font-style:italic;
font-size:20px;
}
#Cover p a {
color:#FFF;
}
</style>
</div></section>
<section class="slide"><div>
<h2>Shower Key Features</h2>
<ol>
<li>Built on HTML, CSS and vanilla JavaScript</li>
<li>All modern browsers are supported</li>
<li>Slide themes are separated from engine</li>
<li>Fully keyboard accessible</li>
<li>Printable to PDF</li>
</ol>
<p class="note">Shower ['ʃəuə] noun. A person or thing that shows.</p>
</div></section>
<section class="slide"><div>
<h2>Plain Text on Your Slides</h2>
<p>Lorem ipsum dolor sit amet, consectetur <a href="#4">adipisicing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, <em>quis nostrud</em> exercitation ullamco laboris <strong>nisi ut aliquip</strong> ex ea commodo consequat. Duis aute irure <i>dolor</i> in reprehenderit in voluptate velit esse cillum <b>dolore</b> eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in <code>&lt;culpa&gt;</code> qui officia deserunt mollit anim id est laborum.</p>
</div></section>
<section class="slide"><div>
<h2>All Kind of Lists</h2>
<ol>
<li>Simple lists are marked with bullets</li>
<li>Ordered lists begin with a number</li>
<li>You can even nest lists one inside another
<ul>
<li>Or mix their types</li>
<li>But do not go too far</li>
<li>Otherwise audience will be bored</li>
</ul>
</li>
<li>Look, seven rows exactly!</li>
</ol>
</div></section>
<section class="slide"><div>
<h2>Serious Citations</h2>
<figure>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.</p>
</blockquote>
<figcaption>Marcus Tullius Cicero</figcaption>
</figure>
</div></section>
<section class="slide"><div>
<h2>Code Samples</h2>
<pre>
<code>&lt;!DOCTYPE html&gt;</code>
<code>&lt;html lang="en"&gt;</code>
<code><mark>&lt;head&gt;</mark> <mark class="comment">&lt;!--Comment--&gt;</mark></code>
<code> &lt;title&gt;Shower&lt;/title&gt;</code>
<code> &lt;meta charset="<mark class="important">UTF-8</mark>"&gt;</code>
<code> &lt;link rel="stylesheet" href="screen.css"&gt;</code>
<code><mark>&lt;/head&gt;</mark></code>
</pre>
</div></section>
<section class="slide"><div>
<h2>Even Tables</h2>
<table>
<tr>
<th scope="col">Locavore</th>
<th>Umami</th>
<th>Helvetica</th>
<th>Vegan</th>
</tr>
<tr>
<th scope="row">Fingerstache</th>
<td>Kale</td>
<td>Chips</td>
<td>Keytar</td>
</tr>
<tr>
<th scope="row">Sriracha</th>
<td>Gluten-free</td>
<td>Ennui</td>
<td>Keffiyeh</td>
</tr>
<tr>
<th scope="row">Thundercats</th>
<td>Jean</td>
<td>Shorts</td>
<td>Biodiesel</td>
</tr>
<tr>
<th scope="row">Terry</th>
<td>Richardson</td>
<td>Swag</td>
<td>Blog</td>
</tr>
</table>
<p>It’s good to have information organized.</p>
</div></section>
<section class="slide cover" id="Picture"><div>
<h2>Pictures</h2>
<img src="pictures/picture.jpg" alt="">
<style>
#Picture h2 {
color:#FFF;
}
</style>
</div></section>
<section class="slide shout"><div>
<h2>You can even shout this way</h2>
</div></section>
<section class="slide"><div>
<h2>Inner Navigation</h2>
<ol>
<li>Lets you reveal list items one by one</li>
<li class="next">To keep some key points</li>
<li class="next">In secret from audience</li>
<li class="next">But it will work only once</li>
<li class="next">Nobody wants to see the same joke twice</li>
</ol>
</div></section>
<section class="slide shout" id="SeeMore"><div>
<h2><img src="pictures/logo.svg" alt=""> <a href="https://github.com/shower/shower">See more on GitHub</a></h2>
<style>
#Picture h2 {
color:#FFF;
}
#SeeMore h2 {
font-size:100px
}
#SeeMore img {
width:0.72em;
height:0.72em;
}
</style>
</div></section>
<p class="badge"><a href="https://github.com/shower/shower">Fork me on Github</a></p>
<!--
To hide progress bar from entire presentation
just remove “progress” element.
-->
<div class="progress"><div></div></div>
<script src="node_modules/shower-core/shower.min.js"></script>
<!-- Copyright © 2013 Yours Truly, Famous Inc. -->
<!-- Photos by John Carey, fiftyfootshadows.net -->
</body>
</html>
\ No newline at end of file
{
"name": "shower",
"description": "Shower HTML presentation engine",
"repository": "https://github.com/shower/shower/",
"version": "1.0.0",
"private": true,
"devDependencies": {
"dalekjs": "0.0.8",
"dalek-browser-chrome": "0.0.9",
"grunt": "~0.4.2",
"grunt-contrib-uglify": "~0.3.2",
"grunt-dalek": "~0.2.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-connect": "~0.6.0"
"author": {
"name": "Vadim Makeev",
"url": "http://pepelsbey.com/"
},
"homepage": "http://shwr.me/",
"repository": {
"type": "git",
"url": "git://github.com/shower/shower.git"
},
"bugs": {
"url": "http://github.com/shower/shower/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/shower/shower/wiki/MIT-License"
}
],
"keywords": [
"shower",
"presentation",
"template"
],
"dependencies": {
"shower-core": "^1.0.0",
"shower-ribbon": "^1.0.0",
"shower-bright": "^1.0.0"
}
}
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">
<path d="M50 500c-27.614 0-50-22.386-50-50v-400c0-27.614 22.386-50 50-50h400c27.615 0 50 22.386 50 50v219.224l-84.141-84.141-17.678 17.678 101.818 101.818v35.355l-119.496-119.496-17.678 17.678 137.174 137.174v35.397l-154.851-154.851-17.677 17.677 172.528 172.528v3.961c0 27.614-22.385 50-50 50h-109.618c4.128-39.908-9.142-81.331-39.788-111.978l-61.52-61.518-61.517-61.518c-24.497-24.498-24.568-64.143-.161-88.55 24.407-24.407 64.053-24.335 88.549.161l8.484 8.484v106.067l159.1-159.1h-106.067l-8.484-8.484c-53.893-53.893-141.111-54.051-194.808-.354s-53.539 140.916.354 194.809l61.518 61.518 61.519 61.518c16.006 16.006 21.584 38.48 16.719 58.945zm0 0" fill="#4B86C2"/>
</svg>
\ No newline at end of file
/**
* Shower HTML presentation engine: github.com/shower/shower
* @copyright 2010–2013 Vadim Makeev, pepelsbey.net
* @license MIT license: github.com/shower/shower/wiki/MIT-License
*/
window.shower = window.shower || (function(window, document, undefined) {
var shower = {},
url = window.location,
body = document.body,
slides = [],
progress = [],
timer,
isHistoryApiSupported = !!(window.history && window.history.pushState);
/**
* Slide constructor
*
* @param {Object} opts
* @param {String} opts.id html id attribute or automaticaly assigned order number
* @param {Number} opts.number slide number
* @param {Boolean} opts.hasInnerNavigation
* @param {Number} [opts.timing]
* @param {Number} [opts.innerLength]
* @param {Number} [opts.innerComplete = 0]
* @constructor
*/
function Slide(opts) {
for (var prop in opts) {
if (opts.hasOwnProperty(prop)) {
this[prop] = opts[prop];
}
}
}
Slide.prototype = {
/**
* Get slide number.
* @returns {Number}
*/
getSlideNumber : function() {
return this.number;
},
isLast : function() {
return shower.slideList.length === this.number + 1;
},
/**
* Check if inner navigation is finished
* @returns {boolean}
*/
isFinished : function() {
return this.innerComplete >= this.innerLength;
},
/**
* Start inner navigation by timer or just switch slide after timer.
* time sets in HTML: .slide[data-timing=MM:SS]
* @returns {Object} Current slide
*/
process : function(shower) {
if (this.timing) {
this.initTimer(shower);
return this;
}
this.next(shower);
return this;
},
/**
* Init timer for inner navigation or for just turn to next slide
* @param shower
* @returns {Object|Boolean} Current slide
*/
initTimer : function(shower) {
var slide = this;
if ( ! slide.timing) {
return false;
}
slide.stopTimer();
if (slide.isFinished()) {
timer = setInterval(function() {
slide.stopTimer();
shower.next();
},
slide.timing * (slide.innerLength || 1));
} else {
timer = setInterval(function() {
if (slide.isFinished()) {
slide.stopTimer();
shower.next();
} else {
slide.next(shower);
}
},
slide.timing);
}
return this;
},
/**
* Stop timer
*/
stopTimer : function() {
if (timer) {
clearInterval(timer);
timer = false;
}
return this;
},
/**
* Previous step of inner navigation or if current step is step 0 then go to previous slide.
* @returns {Object|Boolean} Current slide
*/
prev : function(shower) {
var prevSteps,
slide = this;
if ( ! slide.hasInnerNavigation || slide.isFinished() || slide.innerComplete === 0) {
shower.prev();
return false;
}
prevSteps = document.getElementById(slide.id).querySelectorAll('.next.active');
if ( ! prevSteps || prevSteps.length < 1) {
return false;
}
if (slide.innerComplete > 0) {
slide.innerComplete--;
prevSteps[prevSteps.length - 1].classList.remove('active');
} else {
shower.prev();
}
return this;
},
/**
* Next step of inner navigation or if current step is last then go to next slide.
* @returns {Object|Boolean} Current slide
*/
next : function(shower) {
var nextSteps,
slide = this;
if ( ! slide.hasInnerNavigation || slide.isFinished()) {
shower.next();
return false;
}
if ( ! slide.isFinished()) {
nextSteps = document.getElementById(slide.id).querySelectorAll('.next:not(.active)');
nextSteps[0].classList.add('active');
slide.innerComplete++;
}
return this;
}
};
/**
* Get value at named data store for the DOM element.
* @private
* @param {HTMLElement} element
* @param {String} name
* @returns {String}
*/
shower._getData = function(element, name) {
return element.dataset ? element.dataset[name] : element.getAttribute('data-' + name);
};
shower.slideList = [];
/**
* Shower initialization
* @param {String} [slideSelector]
* @param {String} [progressSelector]
* @returns {Object} shower
*/
shower.init = function(slideSelector, progressSelector) {
var timing;
slideSelector = slideSelector || '.slide';
progressSelector = progressSelector || 'div.progress div';
slides = document.querySelectorAll(slideSelector);
progress = document.querySelector(progressSelector);
for (var i = 0; i < slides.length; i++) {
// Slide IDs are optional.
// In case of missing ID we set it to the slide number
if ( ! slides[i].id) {
slides[i].id = i + 1;
}
timing = shower._getData(slides[i], 'timing');
// Parsing timing in [S] or [M:S] format
// and returning it in milliseconds
if (timing && /^(\d{1,2}:)?\d{1,3}$/.test(timing)) {
if (timing.indexOf(':') !== -1) {
timing = timing.split(':');
timing = (parseInt(timing[0], 10) * 60 + parseInt(timing[1], 10)) * 1000;
} else {
timing = parseInt(timing, 10) * 1000;
}
if (timing === 0) {
timing = false;
}
} else {
timing = false;
}
shower.slideList.push(new Slide({
id : slides[i].id,
number : i,
hasInnerNavigation : null !== slides[i].querySelector('.next'),
timing : timing,
innerLength : slides[i].querySelectorAll('.next').length,
innerComplete : 0
}));
}
return shower;
};
/**
* Get slide scale value.
* @private
* @returns {String}
*/
shower._getTransform = function() {
var denominator = Math.max(
body.clientWidth / window.innerWidth,
body.clientHeight / window.innerHeight
);
return 'scale(' + (1 / denominator) + ')';
};
/**
* Set CSS transform with prefixes to body.
* @private
* @returns {Boolean}
*/
shower._applyTransform = function(transform) {
[
'WebkitTransform',
'MozTransform',
'msTransform',
'OTransform',
'transform'
].forEach(function(prop) {
body.style[prop] = transform;
});
return true;
};
/**
* Check if arg is number.
* @private
* @param {String|Number} arg
* @returns {Boolean}
*/
shower._isNumber = function(arg) {
return ! isNaN(parseFloat(arg)) && isFinite(arg);
};
/**
* Normalize slide number.
* @private
* @param {Number} slideNumber slide number (sic!)
* @returns {Number}
*/
shower._normalizeSlideNumber = function(slideNumber) {
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
if (slideNumber < 0) {
slideNumber = 0;
}
if (slideNumber >= shower.slideList.length) {
slideNumber = shower.slideList.length - 1;
}
return slideNumber;
};
/**
* Get slide id from HTML element.
* @private
* @param {Node} el
* @returns {String}
*/
shower._getSlideIdByEl = function(el) {
while ('BODY' !== el.nodeName && 'HTML' !== el.nodeName) {
if (el.classList.contains('slide')) {
return el.id;
} else {
el = el.parentNode;
}
}
return '';
};
/**
* For touch devices: check if link is clicked.
*
* @TODO: add support for textarea/input/etc.
*
* @private
* @param {HTMLElement} e
* @returns {Boolean}
*/
shower._checkInteractiveElement = function(e) {
return 'A' === e.target.nodeName;
};
/**
* Get slide number by slideId.
* @param {String} slideId
* @returns {Number}
*/
shower.getSlideNumber = function(slideId) {
var i = shower.slideList.length - 1,
slideNumber;
if (slideId === '') {
slideNumber = 0;
}
// As fast as you can ;-)
// http://jsperf.com/for-vs-foreach/46
for (; i >= 0; --i) {
if (slideId === shower.slideList[i].id) {
slideNumber = i;
break;
}
}
return slideNumber;
};
/**
* Go to slide number.
* @param {Number} slideNumber slide number (sic!). Attention: starts from zero.
* @param {Function} [callback] runs only if you not in List mode.
* @returns {Number|Boolean}
*/
shower.go = function(slideNumber, callback) {
var slide;
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
if ( ! shower.slideList[slideNumber]) {
return false;
}
// Also triggers popstate and invoke shower.enter__Mode()
url.hash = shower.getSlideHash(slideNumber);
shower.updateProgress(slideNumber);
shower.updateActiveAndVisitedSlides(slideNumber);
if (shower.isSlideMode()) {
shower.showPresenterNotes(slideNumber);
slide = shower.slideList[slideNumber];
if (slide.timing) {
slide.initTimer(shower);
}
}
if (typeof(callback) === 'function') {
callback();
}
return slideNumber;
};
/**
* Show next slide or show next Inner navigation item.
* Returns false on a last slide, otherwise returns shower.
* @param {Function} [callback] runs only if shower.next() is successfully completed.
* @returns {Boolean}
*/
shower.next = function(callback) {
var currentSlideNumber = shower.getCurrentSlideNumber(),
nextSlide = shower.slideList[currentSlideNumber + 1];
// If don't exist next slide
if (! nextSlide) {
return false;
}
shower.go(currentSlideNumber + 1);
if (typeof(callback) === 'function') {
callback();
}
return this;
};
/**
*
* @param {Function} [callback]
*/
shower._turnNextSlide = function(callback) {
var currentSlideNumber = shower.getCurrentSlideNumber(),
slide = shower.slideList[currentSlideNumber];
if (shower.isSlideMode()) {
slide.stopTimer();
slide.next(shower);
} else {
shower.go(currentSlideNumber + 1);
}
if (typeof(callback) === 'function') {
callback();
}
return;
};
/**
* Show previous slide. Returns false on a first slide, otherwise returns shown slide number.
* @param {Function} [callback] runs only if shower.previous() is successfully completed.
* @returns {Boolean}
*/
shower.prev = shower.previous = function(callback) {
var currentSlideNumber = shower.getCurrentSlideNumber();
// Slides starts from 0
if (currentSlideNumber < 1) {
return false;
}
shower.go(currentSlideNumber - 1);
if (typeof(callback) === 'function') {
callback();
}
return true;
};
/**
* Show previous slide. Returns false on a first slide, otherwise returns shown slide number.
* @param {Function} [callback] runs only if shower.previous() is successfully completed.
* @returns {Boolean}
*/
shower._turnPreviousSlide = function(callback) {
var currentSlideNumber = shower.getCurrentSlideNumber(),
slide = shower.slideList[currentSlideNumber];
slide.stopTimer();
if (shower.isSlideMode()) {
slide.prev(shower);
} else {
shower.go(currentSlideNumber - 1);
}
if (typeof(callback) === 'function') {
callback();
}
return true;
};
/**
* Show first slide.
* @param {Function} [callback]
*/
shower.first = function(callback) {
var slide = shower.slideList[shower.getCurrentSlideNumber()];
slide && slide.timing && slide.stopTimer();
shower.go(0);
if (typeof(callback) === 'function') {
callback();
}
};
/**
* Show last slide.
* @param {Function} [callback]
*/
shower.last = function(callback) {
var slide = shower.slideList[shower.getCurrentSlideNumber()];
slide && slide.timing && slide.stopTimer();
shower.go(shower.slideList.length - 1);
if (typeof(callback) === 'function') {
callback();
}
};
/**
* Switch to slide view.
* @param {Function} [callback] runs only if shower.enterSlideMode() is successfully completed.
* @returns {Boolean}
*/
shower.enterSlideMode = function(callback) {
var currentSlideNumber = shower.getCurrentSlideNumber();
// Anyway: change body class (@TODO: refactoring)
body.classList.remove('list');
body.classList.add('full');
// Preparing URL for shower.go()
if (shower.isListMode() && isHistoryApiSupported) {
history.pushState(null, null, url.pathname + '?full' + shower.getSlideHash(currentSlideNumber));
}
shower._applyTransform(shower._getTransform());
if (typeof(callback) === 'function') {
callback();
}
return true;
};
/**
* Switch to list view.
* @param {Function} [callback] runs only if shower.enterListMode() is successfully completed.
* @returns {Boolean}
*/
shower.enterListMode = function(callback) {
var currentSlideNumber;
// Anyway: change body class (@TODO: refactoring)
body.classList.remove('full');
body.classList.add('list');
shower.clearPresenterNotes();
shower._applyTransform('none');
if (shower.isListMode()) {
return false;
}
currentSlideNumber = shower.getCurrentSlideNumber();
shower.slideList[currentSlideNumber].stopTimer();
if (shower.isSlideMode() && isHistoryApiSupported) {
history.pushState(null, null, url.pathname + shower.getSlideHash(currentSlideNumber));
}
shower.scrollToSlide(currentSlideNumber);
if (typeof(callback) === 'function') {
callback();
}
return true;
};
/**
* Toggle Mode: Slide and List.
* @param {Function} [callback]
*/
shower.toggleMode = function(callback) {
if (shower.isListMode()) {
shower.enterSlideMode();
} else {
shower.enterListMode();
}
if (typeof(callback) === 'function') {
callback();
}
return true;
};
/**
* Get current slide number. Starts from zero. Warning: when you have
* slide number 1 in URL this method will return 0.
* If there is no slide number in url, return -1.
* If there is a slide number in url, but the slide does not exist, return 0.
* @returns {Number}
*/
shower.getCurrentSlideNumber = function() {
var i = shower.slideList.length - 1,
currentSlideId = url.hash.substr(1);
if (currentSlideId === '') {
return -1;
}
// As fast as you can ;-)
// http://jsperf.com/for-vs-foreach/46
for (; i >= 0; --i) {
if (currentSlideId === shower.slideList[i].id) {
return i;
}
}
return 0;
};
/**
* Scroll to slide.
* @param {Number} slideNumber slide number (sic!)
* @returns {Boolean}
*/
shower.scrollToSlide = function(slideNumber) {
var currentSlide,
ret = false;
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
if (shower.isSlideMode()) {
throw new Error('You can\'t scroll to because you in slide mode. Please, switch to list mode.');
}
// @TODO: WTF?
if (-1 === slideNumber) {
return ret;
}
if (shower.slideList[slideNumber]) {
currentSlide = document.getElementById(shower.slideList[slideNumber].id);
window.scrollTo(0, currentSlide.offsetTop);
ret = true;
} else {
throw new Error('There is no slide with number ' + slideNumber);
}
return ret;
};
/**
* Check if it's List mode.
* @returns {Boolean}
*/
shower.isListMode = function() {
return isHistoryApiSupported ? ! /^full.*/.test(url.search.substr(1)) : body.classList.contains('list');
};
/**
* Check if it's Slide mode.
* @returns {Boolean}
*/
shower.isSlideMode = function() {
return isHistoryApiSupported ? /^full.*/.test(url.search.substr(1)) : body.classList.contains('full');
};
/**
* Update progress bar.
* @param {Number} slideNumber slide number (sic!)
* @returns {Boolean}
*/
shower.updateProgress = function(slideNumber) {
// if progress bar doesn't exist
if (null === progress) {
return false;
}
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
progress.style.width = (100 / (shower.slideList.length - 1) * shower._normalizeSlideNumber(slideNumber)).toFixed(2) + '%';
return true;
};
/**
* Update active and visited slides.
* @param {Number} slideNumber slide number (sic!)
* @returns {Boolean}
*/
shower.updateActiveAndVisitedSlides = function(slideNumber) {
var i,
slide,
l = shower.slideList.length;
slideNumber = shower._normalizeSlideNumber(slideNumber);
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
for (i = 0; i < l; ++i) {
slide = document.getElementById(shower.slideList[i].id);
if (i < slideNumber) {
slide.classList.remove('active');
slide.classList.add('visited');
} else if (i > slideNumber) {
slide.classList.remove('visited');
slide.classList.remove('active');
} else {
slide.classList.remove('visited');
slide.classList.add('active');
}
}
return true;
};
/**
* Clear presenter notes in console (only for Slide Mode).
*/
shower.clearPresenterNotes = function() {
if (shower.isSlideMode() && window.console && window.console.clear) {
console.clear();
}
};
/**
* Show presenter notes in console.
* @param {Number} slideNumber slide number (sic!). Attention: starts from zero.
*/
shower.showPresenterNotes = function(slideNumber) {
shower.clearPresenterNotes();
if (window.console) {
slideNumber = shower._normalizeSlideNumber(slideNumber);
var slideId = shower.slideList[slideNumber].id,
nextSlideId = shower.slideList[slideNumber + 1] ? shower.slideList[slideNumber + 1].id : null,
notes = document.getElementById(slideId).querySelector('footer');
if (notes && notes.innerHTML) {
console.info(notes.innerHTML.replace(/\n\s+/g,'\n'));
}
if (nextSlideId) {
var next = document.getElementById(nextSlideId).querySelector('h2');
if (next) {
next = next.innerHTML.replace(/^\s+|<[^>]+>/g,'');
console.info('NEXT: ' + next);
}
}
}
};
/**
* Get slide hash.
* @param {Number} slideNumber slide number (sic!). Attention: starts from zero.
* @returns {String}
*/
shower.getSlideHash = function(slideNumber) {
if ( ! shower._isNumber(slideNumber)) {
throw new Error('Gimme slide number as Number, baby!');
}
slideNumber = shower._normalizeSlideNumber(slideNumber);
return '#' + shower.slideList[slideNumber].id;
};
/**
* Wheel event listener
* @param e event
*/
shower.wheel = function (e) {
var body = document.querySelector('body'),
wheelDown,
lockedWheel = body.getAttribute('data-scroll') === 'locked';
if (!lockedWheel && !shower.isListMode()) {
body.setAttribute('data-scroll', 'locked');
if (e.deltaY === undefined) {
// Chrome, Opera, Safari
wheelDown = e.wheelDeltaY < 0;
} else {
// Firefox
wheelDown = e.deltaY > 0;
}
if (wheelDown) {
shower._turnNextSlide();
} else {
shower._turnPreviousSlide();
}
setTimeout(function () {
body.setAttribute('data-scroll', 'unlocked');
}, 200);
}
}
// Event handlers
window.addEventListener('DOMContentLoaded', function() {
var currentSlideNumber = shower.getCurrentSlideNumber(),
isSlideMode = body.classList.contains('full') || shower.isSlideMode();
if (currentSlideNumber === -1 && isSlideMode) {
shower.go(0);
} else if (currentSlideNumber === 0 || isSlideMode) {
shower.go(currentSlideNumber);
}
if (isSlideMode) {
shower.enterSlideMode();
}
}, false);
window.addEventListener('popstate', function() {
var currentSlideNumber = shower.getCurrentSlideNumber();
if (currentSlideNumber !== -1) {
shower.go(currentSlideNumber);
}
if (shower.isListMode()) {
shower.enterListMode();
} else {
shower.enterSlideMode();
}
}, false);
window.addEventListener('resize', function() {
if (shower.isSlideMode()) {
shower._applyTransform(shower._getTransform());
}
}, false);
document.addEventListener('keydown', function(e) {
var currentSlideNumber = shower.getCurrentSlideNumber(),
slide = shower.slideList[ currentSlideNumber !== -1 ? currentSlideNumber : 0 ],
slideNumber;
switch (e.which) {
case 80: // P Alt Cmd
if (shower.isListMode() && e.altKey && e.metaKey) {
e.preventDefault();
slideNumber = slide.number;
shower.go(slideNumber);
shower.enterSlideMode();
shower.showPresenterNotes(slideNumber);
slide.timing && slide.initTimer(shower);
}
break;
case 116: // F5 (Shift)
e.preventDefault();
if (shower.isListMode()) {
slideNumber = e.shiftKey ? slide.number : 0;
shower.go(slideNumber);
shower.enterSlideMode();
shower.showPresenterNotes(slideNumber);
slide.timing && slide.initTimer(shower);
} else {
shower.enterListMode();
}
break;
case 13: // Enter
if (shower.isListMode() && -1 !== currentSlideNumber) {
e.preventDefault();
shower.enterSlideMode();
shower.showPresenterNotes(currentSlideNumber);
slide.timing && slide.initTimer(shower);
}
break;
case 27: // Esc
if (shower.isSlideMode()) {
e.preventDefault();
shower.enterListMode();
}
break;
case 33: // PgUp
case 38: // Up
case 37: // Left
case 72: // H
case 75: // K
if (e.altKey || e.ctrlKey || e.metaKey) { return; }
e.preventDefault();
shower._turnPreviousSlide();
break;
case 34: // PgDown
case 40: // Down
case 39: // Right
case 76: // L
case 74: // J
if (e.altKey || e.ctrlKey || e.metaKey) { return; }
e.preventDefault();
shower._turnNextSlide();
break;
case 36: // Home
e.preventDefault();
shower.first();
break;
case 35: // End
e.preventDefault();
shower.last();
break;
case 9: // Tab (Shift)
case 32: // Space (Shift)
e.preventDefault();
shower[e.shiftKey ? '_turnPreviousSlide' : '_turnNextSlide']();
break;
default:
// Behave as usual
}
}, false);
shower.init();
document.addEventListener('click', function(e) {
var slideId = shower._getSlideIdByEl(e.target),
slideNumber,
slide;
// Click on slide in List mode
if (slideId && shower.isListMode()) {
slideNumber = shower.getSlideNumber(slideId);
// Warning: go must be before enterSlideMode.
// Otherwise there is a bug in Chrome
shower.go(slideNumber);
shower.enterSlideMode();
shower.showPresenterNotes(slideNumber);
slide = shower.slideList[slideNumber];
if (slide.timing) {
slide.initTimer(shower);
}
}
}, false);
document.addEventListener('touchstart', function(e) {
var slideId = shower._getSlideIdByEl(e.target),
slideNumber,
slide,
x;
if (slideId) {
if (shower.isSlideMode() && ! shower._checkInteractiveElement(e)) {
x = e.touches[0].pageX;
if (x > window.innerWidth / 2) {
shower._turnNextSlide();
} else {
shower._turnPreviousSlide();
}
}
if (shower.isListMode()) {
slideNumber = shower.getSlideNumber(slideId);
// Warning: go must be before enterSlideMode.
// Otherwise there is a bug in Chrome
shower.go(slideNumber);
shower.enterSlideMode();
shower.showPresenterNotes(slideNumber);
slide = shower.slideList[slideNumber];
if (slide.timing) {
slide.initTimer(shower);
}
}
}
}, false);
document.addEventListener('touchmove', function(e) {
if (shower.isSlideMode()) {
e.preventDefault();
}
}, false);
document.addEventListener('wheel', shower.wheel, false);
document.addEventListener('mousewheel', shower.wheel, false);
return shower;
})(this, this.document);
/**
* Shower HTML presentation engine: github.com/shower/shower
* @copyright 2010–2014 Vadim Makeev, pepelsbey.net
* @license MIT license: github.com/shower/shower/wiki/MIT-License
*/
window.shower=window.shower||function(a,b,c){function d(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b])}var e,f={},g=a.location,h=b.body,i=[],j=[],k=!(!a.history||!a.history.pushState);return d.prototype={getSlideNumber:function(){return this.number},isLast:function(){return f.slideList.length===this.number+1},isFinished:function(){return this.innerComplete>=this.innerLength},process:function(a){return this.timing?(this.initTimer(a),this):(this.next(a),this)},initTimer:function(a){var b=this;return b.timing?(b.stopTimer(),e=b.isFinished()?setInterval(function(){b.stopTimer(),a.next()},b.timing*(b.innerLength||1)):setInterval(function(){b.isFinished()?(b.stopTimer(),a.next()):b.next(a)},b.timing),this):!1},stopTimer:function(){return e&&(clearInterval(e),e=!1),this},prev:function(a){var c,d=this;return!d.hasInnerNavigation||d.isFinished()||0===d.innerComplete?(a.prev(),!1):(c=b.getElementById(d.id).querySelectorAll(".next.active"),!c||c.length<1?!1:(d.innerComplete>0?(d.innerComplete--,c[c.length-1].classList.remove("active")):a.prev(),this))},next:function(a){var c,d=this;return!d.hasInnerNavigation||d.isFinished()?(a.next(),!1):(d.isFinished()||(c=b.getElementById(d.id).querySelectorAll(".next:not(.active)"),c[0].classList.add("active"),d.innerComplete++),this)}},f._getData=function(a,b){return a.dataset?a.dataset[b]:a.getAttribute("data-"+b)},f.slideList=[],f.init=function(a,c){var e;a=a||".slide",c=c||"div.progress div",i=b.querySelectorAll(a),j=b.querySelector(c);for(var g=0;g<i.length;g++)i[g].id||(i[g].id=g+1),e=f._getData(i[g],"timing"),e&&/^(\d{1,2}:)?\d{1,3}$/.test(e)?(-1!==e.indexOf(":")?(e=e.split(":"),e=1e3*(60*parseInt(e[0],10)+parseInt(e[1],10))):e=1e3*parseInt(e,10),0===e&&(e=!1)):e=!1,f.slideList.push(new d({id:i[g].id,number:g,hasInnerNavigation:null!==i[g].querySelector(".next"),timing:e,innerLength:i[g].querySelectorAll(".next").length,innerComplete:0}));return f},f._getTransform=function(){var b=Math.max(h.clientWidth/a.innerWidth,h.clientHeight/a.innerHeight);return"scale("+1/b+")"},f._applyTransform=function(a){return["WebkitTransform","MozTransform","msTransform","OTransform","transform"].forEach(function(b){h.style[b]=a}),!0},f._isNumber=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},f._normalizeSlideNumber=function(a){if(!f._isNumber(a))throw new Error("Gimme slide number as Number, baby!");return 0>a&&(a=0),a>=f.slideList.length&&(a=f.slideList.length-1),a},f._getSlideIdByEl=function(a){for(;"BODY"!==a.nodeName&&"HTML"!==a.nodeName;){if(a.classList.contains("slide"))return a.id;a=a.parentNode}return""},f._checkInteractiveElement=function(a){return"A"===a.target.nodeName},f.getSlideNumber=function(a){var b,c=f.slideList.length-1;for(""===a&&(b=0);c>=0;--c)if(a===f.slideList[c].id){b=c;break}return b},f.go=function(a,b){var c;if(!f._isNumber(a))throw new Error("Gimme slide number as Number, baby!");return f.slideList[a]?(g.hash=f.getSlideHash(a),f.updateProgress(a),f.updateActiveAndVisitedSlides(a),f.isSlideMode()&&(f.showPresenterNotes(a),c=f.slideList[a],c.timing&&c.initTimer(f)),"function"==typeof b&&b(),a):!1},f.next=function(a){var b=f.getCurrentSlideNumber(),c=f.slideList[b+1];return c?(f.go(b+1),"function"==typeof a&&a(),this):!1},f._turnNextSlide=function(a){var b=f.getCurrentSlideNumber(),c=f.slideList[b];f.isSlideMode()?(c.stopTimer(),c.next(f)):f.go(b+1),"function"==typeof a&&a()},f.prev=f.previous=function(a){var b=f.getCurrentSlideNumber();return 1>b?!1:(f.go(b-1),"function"==typeof a&&a(),!0)},f._turnPreviousSlide=function(a){var b=f.getCurrentSlideNumber(),c=f.slideList[b];return c.stopTimer(),f.isSlideMode()?c.prev(f):f.go(b-1),"function"==typeof a&&a(),!0},f.first=function(a){var b=f.slideList[f.getCurrentSlideNumber()];b&&b.timing&&b.stopTimer(),f.go(0),"function"==typeof a&&a()},f.last=function(a){var b=f.slideList[f.getCurrentSlideNumber()];b&&b.timing&&b.stopTimer(),f.go(f.slideList.length-1),"function"==typeof a&&a()},f.enterSlideMode=function(a){var b=f.getCurrentSlideNumber();return h.classList.remove("list"),h.classList.add("full"),f.isListMode()&&k&&history.pushState(null,null,g.pathname+"?full"+f.getSlideHash(b)),f._applyTransform(f._getTransform()),"function"==typeof a&&a(),!0},f.enterListMode=function(a){var b;return h.classList.remove("full"),h.classList.add("list"),f.clearPresenterNotes(),f._applyTransform("none"),f.isListMode()?!1:(b=f.getCurrentSlideNumber(),f.slideList[b].stopTimer(),f.isSlideMode()&&k&&history.pushState(null,null,g.pathname+f.getSlideHash(b)),f.scrollToSlide(b),"function"==typeof a&&a(),!0)},f.toggleMode=function(a){return f.isListMode()?f.enterSlideMode():f.enterListMode(),"function"==typeof a&&a(),!0},f.getCurrentSlideNumber=function(){var a=f.slideList.length-1,b=g.hash.substr(1);if(""===b)return-1;for(;a>=0;--a)if(b===f.slideList[a].id)return a;return 0},f.scrollToSlide=function(c){var d,e=!1;if(!f._isNumber(c))throw new Error("Gimme slide number as Number, baby!");if(f.isSlideMode())throw new Error("You can't scroll to because you in slide mode. Please, switch to list mode.");if(-1===c)return e;if(!f.slideList[c])throw new Error("There is no slide with number "+c);return d=b.getElementById(f.slideList[c].id),a.scrollTo(0,d.offsetTop),e=!0,e},f.isListMode=function(){return k?!/^full.*/.test(g.search.substr(1)):h.classList.contains("list")},f.isSlideMode=function(){return k?/^full.*/.test(g.search.substr(1)):h.classList.contains("full")},f.updateProgress=function(a){if(null===j)return!1;if(!f._isNumber(a))throw new Error("Gimme slide number as Number, baby!");return j.style.width=(100/(f.slideList.length-1)*f._normalizeSlideNumber(a)).toFixed(2)+"%",!0},f.updateActiveAndVisitedSlides=function(a){var c,d,e=f.slideList.length;if(a=f._normalizeSlideNumber(a),!f._isNumber(a))throw new Error("Gimme slide number as Number, baby!");for(c=0;e>c;++c)d=b.getElementById(f.slideList[c].id),a>c?(d.classList.remove("active"),d.classList.add("visited")):c>a?(d.classList.remove("visited"),d.classList.remove("active")):(d.classList.remove("visited"),d.classList.add("active"));return!0},f.clearPresenterNotes=function(){f.isSlideMode()&&a.console&&a.console.clear&&console.clear()},f.showPresenterNotes=function(c){if(f.clearPresenterNotes(),a.console){c=f._normalizeSlideNumber(c);var d=f.slideList[c].id,e=f.slideList[c+1]?f.slideList[c+1].id:null,g=b.getElementById(d).querySelector("footer");if(g&&g.innerHTML&&console.info(g.innerHTML.replace(/\n\s+/g,"\n")),e){var h=b.getElementById(e).querySelector("h2");h&&(h=h.innerHTML.replace(/^\s+|<[^>]+>/g,""),console.info("NEXT: "+h))}}},f.getSlideHash=function(a){if(!f._isNumber(a))throw new Error("Gimme slide number as Number, baby!");return a=f._normalizeSlideNumber(a),"#"+f.slideList[a].id},f.wheel=function(a){var d,e=b.querySelector("body"),g="locked"===e.getAttribute("data-scroll");g||f.isListMode()||(e.setAttribute("data-scroll","locked"),d=a.deltaY===c?a.wheelDeltaY<0:a.deltaY>0,d?f._turnNextSlide():f._turnPreviousSlide(),setTimeout(function(){e.setAttribute("data-scroll","unlocked")},200))},a.addEventListener("DOMContentLoaded",function(){var a=f.getCurrentSlideNumber(),b=h.classList.contains("full")||f.isSlideMode();-1===a&&b?f.go(0):(0===a||b)&&f.go(a),b&&f.enterSlideMode()},!1),a.addEventListener("popstate",function(){var a=f.getCurrentSlideNumber();-1!==a&&f.go(a),f.isListMode()?f.enterListMode():f.enterSlideMode()},!1),a.addEventListener("resize",function(){f.isSlideMode()&&f._applyTransform(f._getTransform())},!1),b.addEventListener("keydown",function(a){var b,c=f.getCurrentSlideNumber(),d=f.slideList[-1!==c?c:0];switch(a.which){case 80:f.isListMode()&&a.altKey&&a.metaKey&&(a.preventDefault(),b=d.number,f.go(b),f.enterSlideMode(),f.showPresenterNotes(b),d.timing&&d.initTimer(f));break;case 116:a.preventDefault(),f.isListMode()?(b=a.shiftKey?d.number:0,f.go(b),f.enterSlideMode(),f.showPresenterNotes(b),d.timing&&d.initTimer(f)):f.enterListMode();break;case 13:f.isListMode()&&-1!==c&&(a.preventDefault(),f.enterSlideMode(),f.showPresenterNotes(c),d.timing&&d.initTimer(f));break;case 27:f.isSlideMode()&&(a.preventDefault(),f.enterListMode());break;case 33:case 38:case 37:case 72:case 75:if(a.altKey||a.ctrlKey||a.metaKey)return;a.preventDefault(),f._turnPreviousSlide();break;case 34:case 40:case 39:case 76:case 74:if(a.altKey||a.ctrlKey||a.metaKey)return;a.preventDefault(),f._turnNextSlide();break;case 36:a.preventDefault(),f.first();break;case 35:a.preventDefault(),f.last();break;case 9:case 32:a.preventDefault(),f[a.shiftKey?"_turnPreviousSlide":"_turnNextSlide"]()}},!1),f.init(),b.addEventListener("click",function(a){var b,c,d=f._getSlideIdByEl(a.target);d&&f.isListMode()&&(b=f.getSlideNumber(d),f.go(b),f.enterSlideMode(),f.showPresenterNotes(b),c=f.slideList[b],c.timing&&c.initTimer(f))},!1),b.addEventListener("touchstart",function(b){var c,d,e,g=f._getSlideIdByEl(b.target);g&&(f.isSlideMode()&&!f._checkInteractiveElement(b)&&(e=b.touches[0].pageX,e>a.innerWidth/2?f._turnNextSlide():f._turnPreviousSlide()),f.isListMode()&&(c=f.getSlideNumber(g),f.go(c),f.enterSlideMode(),f.showPresenterNotes(c),d=f.slideList[c],d.timing&&d.initTimer(f)))},!1),b.addEventListener("touchmove",function(a){f.isSlideMode()&&a.preventDefault()},!1),b.addEventListener("wheel",f.wheel,!1),b.addEventListener("mousewheel",f.wheel,!1),f}(this,this.document);
\ No newline at end of file
module.exports = {
// --------------------------------
// Click
// --------------------------------
'Click on slide is switching from List to Full': function (test) {
test
.open('http://localhost:7497/tests/')
.click('[id="1"]')
.assert.attr('body', 'class').to.contain('full', 'Mode is Full')
.done();
},
// --------------------------------
// Walking
// --------------------------------
'All slides could be switched from first to last in List mode': function (test) {
test
.open('http://localhost:7497/tests/#1')
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="6"]', 'class').to.contain('active', 'Last slide is Active')
.done();
},
'All slides could be switched from last to first in List mode': function (test) {
test
.open('http://localhost:7497/tests/#6')
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is Active')
.done();
},
'All slides could be switched from first to last in Full mode': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="6"]', 'class').to.contain('active', 'Last slide is Active')
.done();
},
'All slides could be switched from last to first in Full mode': function (test) {
test
.open('http://localhost:7497/tests/?full#6')
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is Active')
.done();
},
// --------------------------------
// Back
// --------------------------------
'Back is switching from Full to List': function (test) {
test
.open('http://localhost:7497/tests/')
.click('[id="1"]')
.back()
.assert.attr('body', 'class').to.contain('list', 'Mode is List')
.done();
},
// --------------------------------
// Zoom
// --------------------------------
'Back from Full to List is restoring scale': function (test) {
test
.open('http://localhost:7497/tests/')
.click('[id="1"]')
.back()
.assert.attr('body', 'style').to.contain('none', 'Scale is restored')
.done();
}
};
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test page for Shower</title>
<meta charset="utf-8">
<meta name="viewport" content="width=792, user-scalable=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="stylesheet" href="../themes/ribbon/styles/screen.css">
</head>
<body class="list">
<section class="slide"><div>
<h2>1</h2>
</div></section>
<section class="slide"><div>
<h2>2</h2>
</div></section>
<section class="slide" data-timing="00:03"><div>
<h2>3</h2>
</div></section>
<section class="slide"><div>
<h2>4</h2>
</div></section>
<section class="slide"><div>
<h2>5</h2>
<div class="next">1</div>
<div class="next">2</div>
<div class="next">3</div>
</div></section>
<section class="slide"><div>
<h2>6</h2>
</div></section>
<script src="../shower.js"></script>
</body>
</html>
\ No newline at end of file
module.exports = {
'Navigation doesn’t work in List mode': function (test) {
test
.open('http://localhost:7497/tests/#5')
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="6"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Right Arrow key is switching first Next item to Active': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="5"] .next:first-of-type', 'class').to.contain('active', 'First Next item is Active')
.done();
},
'Left Arrow key is switching Active items back to Next': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE012') // Left
.sendKeys('body', '\uE012') // Left
.assert.numberOfElements('[id="5"] .next.active', 0, 'There are no Active items')
.done();
},
'Right Arrow key is switching to next slide once all Next items becomes Active': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="6"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Left Arrow key is switching to previous slide when all Next items becomes Active': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE014') // Right
.sendKeys('body', '\uE012') // Left
.assert.attr('[id="4"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
'Reload reset navigation': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE014') // Right
.reload()
.assert.numberOfElements('[id="5"] .next.active', 0, 'There are no Active items')
.done();
}
};
\ No newline at end of file
module.exports = {
// --------------------------------
// F5
// --------------------------------
'F5 is switching from List to Full': function (test) {
test
.open('http://localhost:7497/tests/')
.sendKeys('body', '\uE035') // F5
.assert.attr('body', 'class').to.contain('full', 'Mode is Full')
.done();
},
'F5 is switching from Full to List': function (test) {
test
.open('http://localhost:7497/tests/?full')
.sendKeys('body', '\uE035') // F5
.assert.attr('body', 'class').to.contain('list', 'Mode is List')
.done();
},
// --------------------------------
// Cmd Alt P
// --------------------------------
'Cmd Alt P keys are switching from List to Full': function (test) {
test
.open('http://localhost:7497/tests/')
.sendKeys('body', '\uE03D\uE00A\u0070') // Cmd Alt P
.assert.attr('body', 'class').to.contain('full', 'Mode is Full')
.done();
},
'Cmd Alt P keys are not switching from Full to List': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE03D\uE00A\u0070') // Cmd Alt P
.assert.attr('body', 'class').to.contain('full', 'Mode is Full')
.done();
},
// --------------------------------
// Esc
// --------------------------------
'Esc is switching from Full to List': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE00C') // Esc
.assert.attr('body', 'class').to.contain('list', 'Mode is List')
.done();
},
// --------------------------------
// Left
// --------------------------------
'Left Arrow key is switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\uE012') // Left
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// Right
// --------------------------------
'Right Arrow key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
// --------------------------------
// Up
// --------------------------------
'Up Arrow key is switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\uE013') // Up
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// Down
// --------------------------------
'Down Arrow key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE015') // Down
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
// --------------------------------
// H
// --------------------------------
'H key is switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\u0068') // H
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// K
// --------------------------------
'K key is switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\u006B') // K
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// J
// --------------------------------
'J key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\u006A') // J
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
// --------------------------------
// L
// --------------------------------
'L key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\u006C') // L
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
// --------------------------------
// Space
// --------------------------------
'Space key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE00D') // Space
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Shift Space keys are switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\uE008\uE00D') // Shift Space
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// Tab
// --------------------------------
'Tab key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE004') // Tab
// Not sure why it’s failing. It works fine manually
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Shift Tab keys are switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\uE008\uE004') // Shift Tab
// Not sure why it’s failing. It works fine manually
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// PageUp
// --------------------------------
'PageUp key is switching to the previous slide': function (test) {
test
.open('http://localhost:7497/tests/?full#2')
.sendKeys('body', '\uE00E') // PageUp
.assert.attr('[id="1"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
// --------------------------------
// PageDown
// --------------------------------
'PageDown key is switching to the next slide': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE00F') // PageDown
.assert.attr('[id="2"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
// --------------------------------
// Home
// --------------------------------
'Home key select the first slide in List mode': function (test) {
test
.open('http://localhost:7497/tests/')
.sendKeys('body', '\uE011') // Home
// Failing unlike next one with current slide
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is active')
.done();
},
'Home key select the first slide in List mode (with current)': function (test) {
test
.open('http://localhost:7497/tests/#5')
.sendKeys('body', '\uE011') // Home
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is active')
.done();
},
'Home key select the first slide in Full mode': function (test) {
test
.open('http://localhost:7497/tests/?full#5')
.sendKeys('body', '\uE011') // Home
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is active')
.done();
},
// --------------------------------
// End
// --------------------------------
'End key select the last slide in List mode': function (test) {
test
.open('http://localhost:7497/tests/')
.sendKeys('body', '\uE010') // End
// Failing unlike next one with current slide
.assert.attr('[id="6"]', 'class').to.contain('active', 'Last slide is active')
.done();
},
'End key select the last slide in List mode (with current)': function (test) {
test
.open('http://localhost:7497/tests/#1')
.sendKeys('body', '\uE010') // End
.assert.attr('[id="6"]', 'class').to.contain('active', 'Last slide is active')
.done();
},
'End key select the last slide in Full mode': function (test) {
test
.open('http://localhost:7497/tests/?full#1')
.sendKeys('body', '\uE010') // End
.assert.attr('[id="6"]', 'class').to.contain('active', 'Last slide is active')
.done();
},
// --------------------------------
// Enter
// --------------------------------
'Enter is opening current slide': function (test) {
test
.open('http://localhost:7497/tests/#1')
.sendKeys('body', '\uE007') // Enter
.assert.attr('body', 'class', 'full', 'Full mode')
.assert.attr('[id="1"]', 'class').to.contain('active', 'First slide is active')
.done();
},
'Enter is not opening any slide if there’s no current': function (test) {
test
.open('http://localhost:7497/tests/')
.sendKeys('body', '\uE007') // Enter
.assert.attr('body', 'class', 'list', 'Mode is List')
.done();
}
};
\ No newline at end of file
module.exports = {
'Timer is switching to the next slide when finished': function (test) {
test
.open('http://localhost:7497/tests/?full#3')
.wait(5000)
.assert.attr('[id="4"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Timer becomes Active and switching to the next slide when finished': function (test) {
test
.open('http://localhost:7497/tests/?full#4')
.sendKeys('body', '\uE012') // Left
.wait(5000)
.assert.attr('[id="4"]', 'class').to.contain('active', 'Next slide is Active')
.done();
},
'Left Arrow key is skipping timer while it’s not finished': function (test) {
test
.open('http://localhost:7497/tests/?full#3')
.sendKeys('body', '\uE012') // Left
.assert.attr('[id="2"]', 'class').to.contain('active', 'Previous slide is Active')
.done();
},
'Right Arrow key is skipping timer while it’s not finished': function (test) {
test
.open('http://localhost:7497/tests/?full#3')
.sendKeys('body', '\uE014') // Right
.assert.attr('[id="4"]', 'class').to.contain('active', 'Next slide is Active')
.done();
}
};
\ No newline at end of file
Subproject commit 2ba8abeb2b23b3c202472e02eff4f68adc13bf25
Subproject commit b79263a6bf1b81cf8793077718fcbe0e0f2833db
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment