2015-07-08 18:22:39 +03:00
// The generic plugin emulates end-user interaction by pressing keyboard and detects changes to the DOM.
// The deck is considered over when no change is detected afterward.
exports . options = {
keycode : {
default : "Right" ,
help : "Key code pressed to navigate to next slide"
2015-09-07 22:40:41 +03:00
} ,
maxSlides : {
help : "Maximum number of slides to export"
2015-07-08 18:22:39 +03:00
}
} ;
2015-07-09 16:07:55 +03:00
exports . help =
2015-09-07 22:40:41 +03:00
"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 [maxSlides] option.\n" +
"The [keycode] option must be one of the PhantomJS page event keys and defaults to [Right]." ;
2015-07-08 18:22:39 +03:00
2015-08-09 19:02:49 +03:00
exports . create = function ( page , options ) {
2015-07-08 18:22:39 +03:00
return new Generic ( page , options ) ;
} ;
function Generic ( page , options ) {
this . page = page ;
this . options = options ;
this . isNextSlideDetected = false ;
2015-09-07 19:11:31 +03:00
this . keycode = this . page . event . key [ this . options . keycode || exports . options . keycode . default ] ;
2015-07-08 18:22:39 +03:00
}
Generic . prototype = {
2015-08-09 19:02:49 +03:00
getName : function ( ) {
2015-07-08 18:22:39 +03:00
return "Generic" ;
} ,
2015-08-09 19:02:49 +03:00
isActive : function ( ) {
2015-07-08 18:22:39 +03:00
return true ;
} ,
2015-08-09 19:02:49 +03:00
configure : function ( ) {
this . page . evaluate ( function ( ) {
var observer = new window . MutationObserver ( function ( ) {
2015-07-08 18:22:39 +03:00
window . callPhantom ( { isNextSlideDetected : true } ) ;
} ) ;
observer . observe ( document . querySelector ( "body" ) , { attributes : true , childList : true , subtree : true } ) ;
} ) ;
var plugin = this ;
2015-08-09 19:02:49 +03:00
this . page . onCallback = function ( mutation ) {
2015-07-08 18:22:39 +03:00
if ( mutation . isNextSlideDetected )
plugin . isNextSlideDetected = true ;
} ;
} ,
2015-08-09 19:02:49 +03:00
slideCount : function ( ) {
2015-07-08 18:22:39 +03:00
return undefined ;
} ,
2015-09-07 19:11:31 +03:00
// 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.
2015-08-09 19:02:49 +03:00
hasNextSlide : function ( ) {
2015-09-07 22:40:41 +03:00
if ( this . options . maxSlides && this . currentSlide >= this . options . maxSlides )
return false ;
2015-09-07 19:11:31 +03:00
// 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 ) ;
2015-07-08 18:22:39 +03:00
var plugin = this ;
return new Promise ( function ( fulfill ) {
// TODO: use mutation event directly instead of relying on a timeout
2015-09-07 19:11:31 +03:00
// TODO: detect cycle to avoid infinite navigation for frameworks that support loopable presentations like impress.js and flowtime.js
2015-08-09 19:02:49 +03:00
setTimeout ( function ( ) {
2015-07-08 18:22:39 +03:00
fulfill ( plugin . isNextSlideDetected ) ;
} , 1000 ) ;
} ) ;
} ,
2015-08-09 19:02:49 +03:00
nextSlide : function ( ) {
2015-07-08 18:22:39 +03:00
this . isNextSlideDetected = false ;
} ,
2015-08-09 19:02:49 +03:00
currentSlideIndex : function ( ) {
var fragment = this . page . evaluate ( function ( ) {
2015-07-08 18:22:39 +03:00
return window . location . hash . replace ( /^#\/?/ , "" ) ;
} ) ;
return fragment . length ? fragment : this . currentSlide ;
}
} ;