diff --git a/decktape.js b/decktape.js index fb0d832..1b60e5e 100644 --- a/decktape.js +++ b/decktape.js @@ -257,9 +257,9 @@ function exportSlides(plugin) { .then(delay(options.pause)) .then(exportSlide) .then(hasNextSlide) - .then(function (hasNext) { + .then(async function (hasNext) { if (hasNext && (!options.slides || plugin.currentSlide < Math.max.apply(null, Object.keys(options.slides)))) { - nextSlide(plugin); + await nextSlide(plugin); return exportSlides(plugin); } else { return plugin; diff --git a/plugins/bespoke.js b/plugins/bespoke.js index 9a98eed..d66429a 100644 --- a/plugins/bespoke.js +++ b/plugins/bespoke.js @@ -1,72 +1,78 @@ exports.help = - 'Requires the bespoke-extern module to expose the Bespoke.js API to a global variable named\n' + - '\'bespoke\' and provides access to the collection of deck instances via \'bespoke.decks\'\n' + - 'and the most recent deck via \'bespoke.deck\'.'; +`Requires the bespoke-extern module to expose the Bespoke.js API to a global variable named +'bespoke' and provides access to the collection of deck instances via 'bespoke.decks +and the most recent deck via 'bespoke.deck'.`; -exports.create = function (page) { - return new Bespoke(page); -}; +exports.create = page => new Bespoke(page); -function Bespoke(page) { +class Bespoke { + + constructor(page) { this.page = page; + } + + getName() { + return 'Bespoke.js'; + } + + isActive() { + return this.page.evaluate(_ => (window.bespoke || {}).deck ? (deck = bespoke.deck) : false); + } + + configure() { + return this.page.evaluate(_ => { + document.body.classList.add('export'); + if (deck.parent.classList.contains('bespoke-overview')) + deck.fire('overview'); + deck.slide(0); + // Advance to last build on first slide (internal state in bespoke-bullets makes this tricky) + const builds = 0; + const one = deck.slides.length === 1; + if (one) + deck.slides.push(document.createElement('section')); + do + ++builds && deck.next(); + while (deck.slide() === 0); + for (let i = 0; i < builds; i++) + i === 0 ? deck.slide(0) : deck.next(); + if (one) + deck.slides.splice(-1, 1); + }); + } + + size() { + return this.page.evaluate(_ => { + const style = getComputedStyle(deck.slides[0]); + return { + width : parseInt(style.width, 10), + height : parseInt(style.height, 10), + }; + }); + } + + slideCount() { + return this.page.evaluate(_ => deck.slides.length); + } + + currentSlideIndex() { + return this.page.evaluate(_ => deck.slide() + 1); + } + + nextSlide() { + return this.page.evaluate(_ => { + // Advance to last build on next slide (internal state in bespoke-bullets makes this tricky) + const next = deck.slide() + 1; + const beforeLast = next === deck.slides.length - 1; + const builds = 0; + if (beforeLast) + deck.slides.push(document.createElement('section')); + do + ++builds && deck.next(); + while (deck.slide() <= next); + for (let i = 1; i < builds; i++) + i === 1 ? deck.slide(next) : deck.next(); + if (beforeLast) + deck.slides.splice(-1, 1); + }); + } } - -Bespoke.prototype = { - - getName: function () { - return 'Bespoke.js'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return (window.bespoke || {}).deck ? (deck = bespoke.deck) : false; - }); - }, - - configure: function () { - this.page.evaluate(function () { - document.body.classList.add('export'); - if (deck.parent.classList.contains('bespoke-overview')) deck.fire('overview'); - deck.slide(0); - // Advance to last build on first slide (internal state in bespoke-bullets makes this tricky) - var builds = 0, one = (deck.slides.length === 1); - if (one) deck.slides.push(document.createElement('section')); - do ++builds && deck.next(); while (deck.slide() === 0); - for (var i = 0; i < builds; i++) i === 0 ? deck.slide(0) : deck.next(); - if (one) deck.slides.splice(-1, 1); - }); - }, - - size: function () { - return this.page.evaluate(function () { - var style = getComputedStyle(deck.slides[0]); - return { - width: parseInt(style.width, 10), - height: parseInt(style.height, 10) - }; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return deck.slides.length; - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return deck.slide() + 1; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - // Advance to last build on next slide (internal state in bespoke-bullets makes this tricky) - var next = deck.slide() + 1, beforeLast = (next === deck.slides.length - 1), builds = 0; - if (beforeLast) deck.slides.push(document.createElement('section')); - do ++builds && deck.next(); while (deck.slide() <= next); - for (var i = 1; i < builds; i++) i === 1 ? deck.slide(next) : deck.next(); - if (beforeLast) deck.slides.splice(-1, 1); - }); - } -}; \ No newline at end of file diff --git a/plugins/csss.js b/plugins/csss.js index 1022a19..0b3e472 100644 --- a/plugins/csss.js +++ b/plugins/csss.js @@ -1,51 +1,39 @@ -function CSSS(page) { +exports.create = page => new CSSS(page); + +class CSSS { + + constructor(page) { this.page = page; + } + + getName() { + return 'CSSS'; + } + + isActive() { + // Avoid global variable name collision with remark.js + return this.page.evaluate(_ => + typeof remark === 'undefined' && typeof slideshow === 'object'); + } + + configure() { + return this.page.evaluate(_ => document.getElementById('timer').style.display = 'none'); + } + + slideCount() { + return this.page.evaluate(_ => + document.querySelectorAll('.slide, .delayed, .delayed-children > *').length); + } + + hasNextSlide() { + return this.page.evaluate(_ => slideshow.index + 1 in slideshow.slides); + } + + nextSlide() { + return this.page.evaluate(_ => slideshow.next(false)); + } + + currentSlideIndex() { + return this.page.evaluate(_ => slideshow.slides[slideshow.slide].id); + } } - -CSSS.prototype = { - - getName: function () { - return 'CSSS'; - }, - - isActive: function () { - return this.page.evaluate(function () { - // Avoid global variable name collision with remark.js - return typeof remark === 'undefined' && typeof slideshow === 'object'; - }); - }, - - configure: function () { - this.page.evaluate(function () { - document.getElementById('timer').style.display = 'none'; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return document.querySelectorAll('.slide, .delayed, .delayed-children > *').length; - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return (slideshow.index + 1) in slideshow.slides; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - slideshow.next(false); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return slideshow.slides[slideshow.slide].id; - }); - } -}; - -exports.create = function (page) { - return new CSSS(page); -}; \ No newline at end of file diff --git a/plugins/deck.js b/plugins/deck.js index 116fa30..7803df3 100644 --- a/plugins/deck.js +++ b/plugins/deck.js @@ -1,38 +1,28 @@ -function Deck(page) { +exports.create = page => new Deck(page); + +class Deck { + + constructor(page) { this.page = page; + } + + getName() { + return 'Deck JS'; + } + + isActive() { + return this.page.evaluate(_ => typeof $ === 'function' && typeof $.deck === 'function'); + } + + slideCount() { + return this.page.evaluate(_ => $.deck('getSlides').length); + } + + nextSlide() { + return this.page.evaluate(_ => $.deck('next')); + } + + currentSlideIndex() { + return this.page.evaluate(_ => $.deck('getSlide').attr('id')); + } } - -Deck.prototype = { - - getName: function () { - return 'Deck JS'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof $ === 'function' && typeof $.deck === 'function'; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return $.deck('getSlides').length; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - $.deck('next'); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return $.deck('getSlide').attr('id'); - }); - } -}; - -exports.create = function (page) { - return new Deck(page); -}; \ No newline at end of file diff --git a/plugins/dzslides.js b/plugins/dzslides.js index 47c7124..5704508 100644 --- a/plugins/dzslides.js +++ b/plugins/dzslides.js @@ -1,47 +1,34 @@ -function DZSlides(page) { +exports.create = page => new DZSlides(page); + +class DZSlides { + + constructor(page) { this.page = page; + } + + getName() { + return 'DZ Slides'; + } + + isActive() { + return this.page.evaluate(_ => typeof Dz !== 'undefined'); + } + + slideCount() { + return this.page.evaluate(_ => + Dz.slides.reduce((count, slide) => count + slide.$$('.incremental > *').length + 1, 0)); + } + + hasNextSlide() { + return this.page.evaluate(_ => !(Dz.idx == Dz.slides.length + && Dz.step == Dz.slides[Dz.idx - 1].$$('.incremental > *').length)); + } + + nextSlide() { + return this.page.evaluate(_ => Dz.forward()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => Dz.idx + '.' + Dz.step); + } } - -DZSlides.prototype = { - - getName: function () { - return 'DZ Slides'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof Dz !== 'undefined'; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return Dz.slides.reduce(function (count, slide) { - var fragments = slide.$$('.incremental > *').length; - return count + (fragments ? fragments + 1 : 1); - }, 0); - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return !(Dz.idx == Dz.slides.length && Dz.step == Dz.slides[Dz.idx - 1].$$('.incremental > *').length); - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - Dz.forward(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return Dz.idx + '.' + Dz.step; - }); - } -}; - -exports.create = function (page) { - return new DZSlides(page); -}; \ No newline at end of file diff --git a/plugins/flowtime.js b/plugins/flowtime.js index 73bd3d8..199206f 100644 --- a/plugins/flowtime.js +++ b/plugins/flowtime.js @@ -1,49 +1,40 @@ -function Flowtime(page) { +exports.create = page => new Flowtime(page); + +class Flowtime { + + constructor(page) { this.page = page; + } + + getName() { + return 'Flowtime JS'; + } + + isActive() { + return this.page.evaluate(_ => typeof Flowtime === 'object'); + } + + configure() { + return this.page.evaluate(_ => { + Flowtime.showProgress(false); + Flowtime.loop(false); + }); + } + + slideCount() { + return undefined; + } + + hasNextSlide() { + return this.page.evaluate(_ => Flowtime.getNextPage() || Flowtime.getNextSection()); + } + + nextSlide() { + return this.page.evaluate(_ => Flowtime.next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => + `/section-${Flowtime.getSectionIndex() + 1}/page-${Flowtime.getPageIndex() + 1}`); + } } - -Flowtime.prototype = { - - getName: function () { - return 'Flowtime JS'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof Flowtime === 'object'; - }); - }, - - configure: function () { - this.page.evaluate(function () { - Flowtime.showProgress(false); - Flowtime.loop(false); - }); - }, - - slideCount: function () { - return undefined; - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return Flowtime.getNextPage() || Flowtime.getNextSection(); - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - Flowtime.next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return '/section-' + (Flowtime.getSectionIndex() + 1) + '/page-' + (Flowtime.getPageIndex() + 1); - }); - } -}; - -exports.create = function (page) { - return new Flowtime(page); -}; \ No newline at end of file diff --git a/plugins/generic.js b/plugins/generic.js index 3e0a0cb..3b69979 100644 --- a/plugins/generic.js +++ b/plugins/generic.js @@ -3,16 +3,16 @@ // is detected afterward. exports.options = { - key : { - default : 'ArrowRight', - metavar : '', - help : 'Key pressed to navigate to next slide' - }, - maxSlides : { - full : 'max-slides', - metavar : '', - help : 'Maximum number of slides to export' - } + key : { + default : 'ArrowRight', + metavar : '', + help : 'Key pressed to navigate to next slide', + }, + maxSlides : { + full : 'max-slides', + metavar : '', + help : 'Maximum number of slides to export', + } }; exports.help = @@ -23,57 +23,58 @@ and iterates over the presentation as long as: - Nor the number of slides exported has reached the specified --max-slides option. The --key option must be one of the 'KeyboardEvent' keys and defaults to [${exports.options.key.default}].`; -exports.create = function (page, options) { - return new Generic(page, options); -}; +exports.create = (page, options) => new Generic(page, options); -function Generic(page, options) { +class Generic { + constructor(page, options) { this.page = page; this.options = options; this.isNextSlideDetected = false; this.key = this.options.key || exports.options.key.default; + } + + getName() { + return 'Generic'; + } + + isActive() { + return true; + } + + async configure() { + await this.page.exposeFunction('onMutation', _ => (this.isNextSlideDetected = true)); + return this.page.evaluate(_ => + new MutationObserver(_ => window.onMutation()).observe(document, { + attributes : true, + childList : true, + subtree : true, + }) + ); + } + + slideCount() { + return undefined; + } + + // A priori knowledge is impossible to achieve in a generic way. Thus the only way is to + // actually emulate end-user interaction by pressing the configured key and check whether + // the DOM has changed a posteriori. + async hasNextSlide() { + if (this.options.maxSlides && this.currentSlide >= this.options.maxSlides) + return false; + await this.page.press(this.key); + // TODO: use mutation event directly instead of relying on a timeout + // TODO: detect cycle to avoid infinite navigation for frameworks + // that support loopable presentations like impress.js and flowtime.js + return new Promise(fulfill => setTimeout(_ => fulfill(this.isNextSlideDetected), 1000)); + } + + nextSlide() { + this.isNextSlideDetected = false; + } + + async currentSlideIndex() { + const fragment = await this.page.evaluate(_ => window.location.hash.replace(/^#\/?/, '')); + return fragment.length ? fragment : this.currentSlide; + } } - -Generic.prototype = { - - getName: function () { - return 'Generic'; - }, - - isActive: function () { - return true; - }, - - configure: async function () { - await this.page.exposeFunction('onMutation', _ => this.isNextSlideDetected = true); - return this.page.evaluate(_ => new MutationObserver(_ => window.onMutation()) - .observe(document, { attributes: true, childList: true, subtree: true }) - ); - }, - - slideCount: function () { - return undefined; - }, - - // A priori knowledge is impossible to achieve in a generic way. Thus the only way is to - // actually emulate end-user interaction by pressing the configured key and check whether - // the DOM has changed a posteriori. - hasNextSlide: async function () { - if (this.options.maxSlides && this.currentSlide >= this.options.maxSlides) - return false; - await this.page.press(this.key); - // TODO: use mutation event directly instead of relying on a timeout - // TODO: detect cycle to avoid infinite navigation for frameworks - // that support loopable presentations like impress.js and flowtime.js - return new Promise(fulfill => setTimeout(_ => fulfill(this.isNextSlideDetected), 1000)); - }, - - nextSlide: function () { - this.isNextSlideDetected = false; - }, - - currentSlideIndex: async function () { - const fragment = await this.page.evaluate(_ => window.location.hash.replace(/^#\/?/, '')); - return fragment.length ? fragment : this.currentSlide; - } -}; \ No newline at end of file diff --git a/plugins/impress.js b/plugins/impress.js index 80be0b1..9fac704 100644 --- a/plugins/impress.js +++ b/plugins/impress.js @@ -1,44 +1,37 @@ -function Impress(page) { +exports.create = page => new Impress(page); + +class Impress { + + constructor(page) { this.page = page; + } + + getName() { + return 'Impress JS'; + } + + isActive() { + return this.page.evaluate(_ => { + if (typeof impress === 'function') { + return true; + } + if (document.getElementById('impress')) { + console.log('Impress JS plugin isn\'t compatible with impress.js version < 0.3.0'); + } + return false; + }); + } + + slideCount() { + return this.page.evaluate(_ => + document.querySelectorAll('#impress .step, #impress .substep').length); + } + + nextSlide() { + return this.page.evaluate(_ => impress().next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => window.location.hash.replace(/^#\/?/, '')); + } } - -Impress.prototype = { - - getName: function () { - return 'Impress JS'; - }, - - isActive: function () { - return this.page.evaluate(function () { - if (typeof impress === 'function') - return true; - - if (document.getElementById('impress')) - console.log('Impress JS plugin isn\'t compatible with impress.js version < 0.3.0'); - - return false; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return document.querySelectorAll('#impress .step, #impress .substep').length; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - impress().next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return window.location.hash.replace(/^#\/?/, ''); - }); - } -}; - -exports.create = function (page) { - return new Impress(page); -}; \ No newline at end of file diff --git a/plugins/remark.js b/plugins/remark.js index f286c35..37202b4 100644 --- a/plugins/remark.js +++ b/plugins/remark.js @@ -1,45 +1,35 @@ -function Remark(page) { - this.page = page; -} - // TODO: improve backward compatibility (e.g. getCurrentSlideIndex was getCurrentSlideNo in earlier versions) -Remark.prototype = { +exports.create = page => new Remark(page); - getName: function () { - return 'Remark JS'; - }, +class Remark { - isActive: function () { - return this.page.evaluate(function () { - return typeof remark === 'object' && typeof slideshow === 'object'; - }); - }, + constructor(page) { + this.page = page; + } - slideCount: function () { - return this.page.evaluate(function () { - return slideshow.getSlideCount(); - }); - }, + getName() { + return 'Remark JS'; + } - hasNextSlide: function () { - return this.page.evaluate(function () { - return slideshow.getCurrentSlideIndex() + 1 < slideshow.getSlideCount(); - }); - }, + isActive() { + return this.page.evaluate(_ => + typeof remark === 'object' && typeof slideshow === 'object'); + } - nextSlide: function () { - this.page.evaluate(function () { - slideshow.gotoNextSlide(); - }); - }, + slideCount() { + return this.page.evaluate(_ => slideshow.getSlideCount()); + } - currentSlideIndex: function () { - return this.page.evaluate(function () { - return slideshow.getCurrentSlideIndex() + 1; - }); - } -}; + hasNextSlide() { + return this.page.evaluate(_ => + slideshow.getCurrentSlideIndex() + 1 < slideshow.getSlideCount()); + } -exports.create = function (page) { - return new Remark(page); -}; \ No newline at end of file + nextSlide() { + return this.page.evaluate(_ => slideshow.gotoNextSlide()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => slideshow.getCurrentSlideIndex() + 1); + } +} diff --git a/plugins/reveal.js b/plugins/reveal.js index 53ae9d5..e744ac2 100644 --- a/plugins/reveal.js +++ b/plugins/reveal.js @@ -1,75 +1,66 @@ -function Reveal(page) { +exports.create = page => new Reveal(page); + +class Reveal { + + constructor(page) { this.page = page; + } + + getName() { + return 'Reveal JS'; + } + + isActive() { + return this.page.evaluate(_ => { + if (typeof Reveal === 'undefined') { + return false; + } + if (!(typeof Reveal.isLastSlide === 'function')) { + console.log('Reveal JS plugin isn\'t compatible with reveal.js version < 2.3.0'); + return false; + } + return true; + }); + } + + configure() { + const URI = require('URI'); + return this.page.evaluate(fragments => Reveal.configure({ + controls : false, + progress : false, + fragments : fragments, + }), + // It seems passing 'fragments=true' in the URL query string does not take precedence + // over globally configured 'fragments' and prevents from being able to toggle fragments + // with ...?fragments= so we work around that by parsing the page query string + (URI(this.page.url()).query(true)['fragments'] || 'false').toLowerCase() === 'true'); + } + + slideCount() { + // TODO: the getTotalSlides API does not report the number of slides accurately + // as it does not take stacks and some index-less fragments into account + // getTotalSlides API is only available starting reveal.js version 3.0.0 + return this.page.evaluate(_ => typeof Reveal.getTotalSlides === 'function' + ? Reveal.getTotalSlides() + : undefined); + } + + hasNextSlide() { + // check with fragments option? + return this.page.evaluate(_ => !Reveal.isLastSlide() || Reveal.availableFragments().next); + } + + nextSlide() { + return this.page.evaluate(_ => Reveal.next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => { + const indices = Reveal.getIndices(); + const id = Reveal.getCurrentSlide().getAttribute('id'); + return typeof id === 'string' && id.length + ? '/' + id + : '/' + indices.h + (indices.v > 0 ? '/' + indices.v : ''); + }); + } } - -Reveal.prototype = { - - getName: function () { - return 'Reveal JS'; - }, - - isActive: function () { - return this.page.evaluate(function () { - if (typeof Reveal === 'undefined') - return false; - - if (!(typeof Reveal.isLastSlide === 'function')) { - console.log('Reveal JS plugin isn\'t compatible with reveal.js version < 2.3.0'); - return false; - } - - return true; - }); - }, - - configure: function () { - var URI = require('URI'); - this.page.evaluate(function (fragments) { - Reveal.configure({ - controls: false, - progress: false, - fragments: fragments - }); - // It seems passing 'fragments=true' in the URL query string does not take precedence - // over globally configured 'fragments' and prevents from being able to toggle fragments - // with ...?fragments= so we work around that by parsing the page query string - }, (URI(this.page.url()).query(true)['fragments'] || 'false').toLowerCase() === 'true'); - }, - - slideCount: function () { - return this.page.evaluate(function () { - // TODO: the getTotalSlides API does not report the number of slides accurately - // as it does not take stacks and some index-less fragments into account - // getTotalSlides API is only available starting reveal.js version 3.0.0 - return typeof Reveal.getTotalSlides === 'function' ? Reveal.getTotalSlides() : undefined; - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - // check with fragments option? - return !Reveal.isLastSlide() || Reveal.availableFragments().next; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - Reveal.next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - var indices = Reveal.getIndices(); - var id = Reveal.getCurrentSlide().getAttribute('id'); - if (typeof id === 'string' && id.length) - return '/' + id; - else - return '/' + indices.h + (indices.v > 0 ? '/' + indices.v : ''); - }); - } -}; - -exports.create = function (page) { - return new Reveal(page); -}; \ No newline at end of file diff --git a/plugins/rise.js b/plugins/rise.js index 6db88e1..ae826a7 100644 --- a/plugins/rise.js +++ b/plugins/rise.js @@ -1,85 +1,73 @@ -function RISE(page) { +exports.create = page => new RISE(page); + +class RISE { + + constructor(page) { this.page = page; + } + + getName() { + return 'RISE'; + } + + isActive() { + return this.page.evaluate(_ => + typeof $ !== 'undefined' && typeof $('#start_livereveal') === 'object'); + } + + configure() { + return Promise.resolve() + // Wait a while until the RISE extension has loaded + // It'd be better to rely on a deterministic condition though the Jupyter JS API + // isn't stable and documented yet... + .then(delay(2000)) + // Click on the 'Enter/Exit Live Reveal Slideshow' button in the notebook toolbar + .then(_ => this.page.evaluate(_ => { + $('#start_livereveal').click(); + $('#help_b, #exit_b').fadeToggle(); + })) + // Then wait a bit until Reveal.js gets configured by the RISE extension + .then(delay(2000)) + // Finally override Reveal.js configuration + .then(_ => this.page.evaluate(_ => Reveal.configure({ + controls : false, + progress : false, + // FIXME: 0 is still displayed when slideNumber is set to false! + // slideNumber : false, + fragments : false, + } + ))); + } + + slideCount() { + // TODO: the getTotalSlides API does not report the number of slides accurately + // as it does not take stacks and some index-less fragments into account + // getTotalSlides API is only available starting reveal.js version 3.0.0 + return this.page.evaluate(_ => typeof Reveal.getTotalSlides === 'function' + ? Reveal.getTotalSlides() + : undefined); + } + + hasNextSlide() { + // The way RISE re-arranges cell DOM elements to fit into + // the expected Reveal.js structure is not compatible with the + // isLastSlide API exposed by Reveal.js + // return !Reveal.isLastSlide(); + return this.page.evaluate(_ => + Reveal.getCurrentSlide().parentNode.nextElementSibling.nodeName.match(/section/i)); + } + + nextSlide() { + return this.page.evaluate(_ => Reveal.next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => { + const indices = Reveal.getIndices(); + const id = Reveal.getCurrentSlide().getAttribute('id'); + return typeof id === 'string' && id.length + ? '/' + id + : '/' + indices.h + (indices.v > 0 ? '/' + indices.v : ''); + }); + } } - -RISE.prototype = { - - getName: function () { - return 'RISE'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof $ !== 'undefined' && typeof $('#start_livereveal') === 'object'; - }); - }, - - configure: function () { - return Promise.resolve() - // Wait a while until the RISE extension has loaded - // It'd be better to rely on a deterministic condition though the Jupyter JS API - // isn't stable and documented yet... - .then(delay(2000)) - // Click on the 'Enter/Exit Live Reveal Slideshow' button in the notebook toolbar - .then(function () { - this.page.evaluate(function () { - $('#start_livereveal').click(); - $('#help_b, #exit_b').fadeToggle(); - }); - }) - // Then wait a bit until Reveal.js gets configured by the RISE extension - .then(delay(2000)) - // Finally override Reveal.js configuration - .then(function() { - page.evaluate(function () { - Reveal.configure({ - controls: false, - progress: false, - // FIXME: 0 is still displayed when slideNumber is set to false! - // slideNumber: false, - fragments: false - }); - }); - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - // TODO: the getTotalSlides API does not report the number of slides accurately - // as it does not take stacks and some index-less fragments into account - // getTotalSlides API is only available starting reveal.js version 3.0.0 - return typeof Reveal.getTotalSlides === 'function' ? Reveal.getTotalSlides() : undefined; - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - // The way RISE re-arranges cell DOM elements to fit into - // the expected Reveal.js structure is not compatible with the - // isLastSlide API exposed by Reveal.js - // return !Reveal.isLastSlide(); - return Reveal.getCurrentSlide().parentNode.nextElementSibling.nodeName.match(/section/i); - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - Reveal.next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - var indices = Reveal.getIndices(); - var id = Reveal.getCurrentSlide().getAttribute('id'); - if (typeof id === 'string' && id.length) - return '/' + id; - else - return '/' + indices.h + (indices.v > 0 ? '/' + indices.v : ''); - }); - } -}; - -exports.create = function (page) { - return new RISE(page); -}; \ No newline at end of file diff --git a/plugins/shower-1.x.js b/plugins/shower-1.x.js index 26662ea..9832191 100644 --- a/plugins/shower-1.x.js +++ b/plugins/shower-1.x.js @@ -1,52 +1,41 @@ -function Shower(page) { +exports.create = page => new Shower(page); + +class Shower { + + constructor(page) { this.page = page; + } + + getName() { + return 'Shower 1.x'; + } + + isActive() { + return this.page.evaluate(_ => + typeof shower === 'object' && typeof shower.modules === 'undefined'); + } + + configure() { + return this.page.evaluate(_ => { + shower.showPresenterNotes = _ => {}; + shower.first(); + shower.enterSlideMode(); + }); + } + + slideCount() { + return this.page.evaluate(_ => shower.slideList.length); + } + + hasNextSlide() { + return this.page.evaluate(_ => shower.getCurrentSlideNumber() + 1 in shower.slideList); + } + + nextSlide() { + return this.page.evaluate(_ => shower.next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => shower.getSlideHash(shower.getCurrentSlideNumber()).substring(1)); + } } - -Shower.prototype = { - - getName: function () { - return 'Shower 1.x'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof shower === 'object' && typeof shower.modules === 'undefined'; - }); - }, - - configure: function () { - this.page.evaluate(function () { - shower.showPresenterNotes = function () {}; - shower.first(); - shower.enterSlideMode(); - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return shower.slideList.length; - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return (shower.getCurrentSlideNumber() + 1) in shower.slideList; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - shower.next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return shower.getSlideHash(shower.getCurrentSlideNumber()).substring(1); - }); - } -}; - -exports.create = function (page) { - return new Shower(page); -}; \ No newline at end of file diff --git a/plugins/shower-2.x.js b/plugins/shower-2.x.js index 9149127..b4452b0 100644 --- a/plugins/shower-2.x.js +++ b/plugins/shower-2.x.js @@ -1,59 +1,49 @@ -function Shower(page) { +exports.create = page => new Shower(page); + +class Shower { + + constructor(page) { this.page = page; + } + + getName() { + return 'Shower 2.x'; + } + + isActive() { + return this.page.evaluate(_ => + typeof shower === 'object' && typeof shower.modules === 'object'); + } + + configure() { + return new Promise(async resolve => { + await this.page.exposeFunction('onShowerInit', _ => resolve()); + await this.page.evaluate(_ => { + shower.modules.require(['shower.global'], sh => { + window.decktape = {}; + decktape.shower = sh.getInited()[0]; + decktape.shower.container.enterSlideMode(); + window.onShowerInit(); + }); + }); + }); + } + + slideCount() { + // FIXME: this does not take fragments into account which ideally should be deactivated + return this.page.evaluate(_ => decktape.shower.getSlidesCount()); + } + + hasNextSlide() { + return this.page.evaluate(_ => + decktape.shower.player.getCurrentSlideIndex() + 1 < decktape.shower.getSlidesCount()); + } + + nextSlide() { + return this.page.evaluate(_ => decktape.shower.player.next()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => decktape.shower.player.getCurrentSlideIndex() + 1); + } } - -Shower.prototype = { - - getName: function () { - return 'Shower 2.x'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof shower === 'object' && typeof shower.modules === 'object'; - }); - }, - - configure: function () { - return new Promise(async resolve => { - await this.page.exposeFunction('onShowerInit', _ => resolve()); - await this.page.evaluate(_ => { - shower.modules.require(['shower.global'], function (sh) { - window.decktape = {}; - decktape.shower = sh.getInited()[0]; - decktape.shower.container.enterSlideMode(); - window.onShowerInit(); - }); - }); - }); - }, - - slideCount: function () { - // FIXME: this does not take fragments into account which ideally should be deactivated - return this.page.evaluate(function () { - return decktape.shower.getSlidesCount(); - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return decktape.shower.player.getCurrentSlideIndex() + 1 < decktape.shower.getSlidesCount(); - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - decktape.shower.player.next(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return decktape.shower.player.getCurrentSlideIndex() + 1; - }); - } -}; - -exports.create = function (page) { - return new Shower(page); -}; \ No newline at end of file diff --git a/plugins/slidy.js b/plugins/slidy.js index 0180c89..b35577f 100644 --- a/plugins/slidy.js +++ b/plugins/slidy.js @@ -1,54 +1,41 @@ -function Slidy(page) { +exports.create = page => new Slidy(page); + +class Slidy { + + constructor(page) { this.page = page; + } + + getName() { + return 'Slidy'; + } + + isActive() { + return this.page.evaluate(_ => typeof w3c_slidy === 'object'); + } + + configure() { + return this.page.evaluate(_ => { + w3c_slidy.hide_toolbar(); + w3c_slidy.initial_prompt.style.visibility = 'hidden'; + }); + } + + slideCount() { + return this.page.evaluate(_ => w3c_slidy.slides.length + Array.prototype.slice + .call(document.querySelectorAll('.incremental')) + .reduce((incrementals, parent) => incrementals + parent.querySelectorAll('*').length || 1, 0)); + } + + hasNextSlide() { + return this.page.evaluate(_ => w3c_slidy.slide_number + 1 < w3c_slidy.slides.length); + } + + nextSlide() { + return this.page.evaluate(_ => w3c_slidy.next_slide(true)); + } + + currentSlideIndex() { + return this.page.evaluate(_ => '(' + (w3c_slidy.slide_number + 1) + ')'); + } } - -Slidy.prototype = { - - getName: function () { - return 'Slidy'; - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof w3c_slidy === 'object'; - }); - }, - - configure: function () { - this.page.evaluate(function () { - w3c_slidy.hide_toolbar(); - w3c_slidy.initial_prompt.style.visibility = 'hidden'; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return w3c_slidy.slides.length + Array.prototype.slice.call(document.querySelectorAll('.incremental')).reduce(function (incrementals, parent) { - var children = parent.querySelectorAll('*'); - return incrementals + (children.length == 0 ? 1 : children.length); - }, 0); - }); - }, - - hasNextSlide: function () { - return this.page.evaluate(function () { - return w3c_slidy.slide_number + 1 < w3c_slidy.slides.length; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - w3c_slidy.next_slide(true); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return '(' + (w3c_slidy.slide_number + 1) + ')'; - }); - } -}; - -exports.create = function (page) { - return new Slidy(page); -}; \ No newline at end of file diff --git a/plugins/webslides.js b/plugins/webslides.js index cc6205c..9766073 100644 --- a/plugins/webslides.js +++ b/plugins/webslides.js @@ -1,51 +1,37 @@ -function WebSlides(page) { +exports.create = page => new WebSlides(page); + +class WebSlides { + + constructor(page) { this.page = page; + } + + getName() { + return 'WebSlides'; + } + + configure() { + return this.page.evaluate(_ => { + const style = document.createElement('style'); + const css = document.createTextNode('#counter, #navigation {display: none !important}'); + style.appendChild(css); + document.body.appendChild(style); + }); + } + + isActive() { + return this.page.evaluate(_ => typeof ws === 'object'); + } + + slideCount() { + return this.page.evaluate(_ => ws.maxSlide_); + } + + nextSlide() { + return this.page.evaluate(_ => ws.goNext()); + } + + currentSlideIndex() { + return this.page.evaluate(_ => ws.currentSlideI_); + } } - -WebSlides.prototype = { - - getName: function () { - return 'WebSlides'; - }, - - configure: function () { - this.page.evaluate(function () { - var styleNode = document.createElement('style'); - var css = document.createTextNode(''+ - '#counter, #navigation {'+ - ' display: none !important;'+ - '}' - ); - styleNode.appendChild(css); - document.body.appendChild(styleNode); - }); - }, - - isActive: function () { - return this.page.evaluate(function () { - return typeof ws === 'object'; - }); - }, - - slideCount: function () { - return this.page.evaluate(function () { - return ws.maxSlide_; - }); - }, - - nextSlide: function () { - this.page.evaluate(function () { - ws.goNext(); - }); - }, - - currentSlideIndex: function () { - return this.page.evaluate(function () { - return ws.currentSlideI_; - }); - } -}; - -exports.create = function (page) { - return new WebSlides(page); -};