Use Reveal.js API for the RISE plugin

This commit is contained in:
Antonin Stefanutti 2016-07-17 17:47:43 +02:00
parent 4db761a70e
commit 7324d65e46

View File

@ -1,36 +1,5 @@
// RISE plugin emulates end-user interaction by pressing keyboard and detects changes to the DOM.
// (clone from generic.js)
exports.options = {
keycode: {
default: 'Right',
metavar: '<code>',
help: 'Key code pressed to navigate to next slide'
},
maxSlides: {
full: 'max-slides',
metavar: '<size>',
help: 'Maximum number of slides to export'
}
};
exports.help =
'Emulates the end-user interaction by pressing the key with the specified --keycode option\n' +
'and iterates over the presentation as long as:\n' +
'- Any change to the DOM is detected by observing mutation events targeting the body element\n' +
' and its subtree,\n' +
'- Nor the number of slides exported has reached the specified --max-slides option.\n' +
'The --keycode option must be one of the PhantomJS page event keys and defaults to [Right].';
exports.create = function (page, options) {
return new RISE(page, options);
};
function RISE(page, options) {
function RISE(page) {
this.page = page;
this.options = options;
this.isNextSlideDetected = false;
this.keycode = this.page.event.key[this.options.keycode || exports.options.keycode.default];
}
RISE.prototype = {
@ -41,59 +10,70 @@ RISE.prototype = {
isActive: function () {
return this.page.evaluate(function () {
if (typeof $('#start_livereveal') === 'object') {
return true;
}
return false;
return typeof $('#start_livereveal') === 'object';
});
},
configure: function () {
this.page.evaluate(function () {
var observer = new window.MutationObserver(function () {
window.callPhantom({ isNextSlideDetected: true });
return new Promise(function (resolve) {
// Click on the Enter/Exit Live Reveal Slideshow button in the notebook toolbar
this.page.evaluate(function () {
$('#start_livereveal').click();
$('#help_b,#exit_b').fadeToggle();
});
observer.observe(document.querySelector('body'), { attributes: true, childList: true, subtree: true });
$('#start_livereveal').click();
$('#help_b,#exit_b').fadeToggle();
});
var plugin = this;
this.page.onCallback = function (mutation) {
if (mutation.isNextSlideDetected)
plugin.isNextSlideDetected = 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: function () {
if (this.options.maxSlides && this.currentSlide >= this.options.maxSlides)
return false;
// PhantomJS actually sends a 'keydown' DOM event when sending a 'keypress' user event. Hence 'keypress' event is skipped to avoid moving forward two steps instead of one. See https://github.com/ariya/phantomjs/issues/11094 for more details.
['keydown'/*, 'keypress'*/, 'keyup'].forEach(function (event) {
this.page.sendEvent(event, this.keycode);
}, this);
var plugin = this;
return new Promise(function (fulfill) {
// 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
// Then wait a while and configure Reveal.js
setTimeout(function () {
fulfill(plugin.isNextSlideDetected);
this.page.evaluate(function () {
Reveal.configure({
controls: false,
progress: false,
// FIXME: 0 is still displayed when slideNumber is set to false!
// slideNumber:false,
fragments:false
});
});
resolve();
}, 1000);
});
},
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.isNextSlideDetected = false;
this.page.evaluate(function () {
Reveal.next();
});
},
currentSlideIndex: function () {
var fragment = this.page.evaluate(function () {
return window.location.hash.replace(/^#\/?/, '');
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 : '');
});
return fragment.length ? fragment : this.currentSlide;
}
};
exports.create = function (page) {
return new RISE(page);
};