Sebastian Berlin (WMSE) has uploaded a new change for review.
https://gerrit.wikimedia.org/r/314664
Change subject: Skip back sentence
......................................................................
Skip back sentence
Enabled skipping back to the previous sentence while playing. If the current
sentence has played for less than a certain number of seconds, it's played
from the beginning. This threshold can be set in the configuration and is
3 seconds by default. Button and keyborad shortcut (ctrl + down arrow by
deafult) was added.
Bug: T133687
Change-Id: Ib63a013f525ce41f4e03bccf115ebe2a15fe4992
---
M Hooks.php
M extension.json
M modules/ext.wikispeech.css
M modules/ext.wikispeech.js
M tests/qunit/ext.wikispeech.test.js
5 files changed, 222 insertions(+), 61 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikispeech
refs/changes/64/314664/1
diff --git a/Hooks.php b/Hooks.php
index a7c3211..0d15c02 100644
--- a/Hooks.php
+++ b/Hooks.php
@@ -120,6 +120,9 @@
global $wgWikispeechKeyboardShortcuts;
$vars['wgWikispeechKeyboardShortcuts'] =
$wgWikispeechKeyboardShortcuts;
+ global $wgWikispeechSkipBackRewindsThreshold;
+ $vars['wgWikispeechSkipBackRewindsThreshold'] =
+ $wgWikispeechSkipBackRewindsThreshold;
return true;
}
}
diff --git a/extension.json b/extension.json
index efba7c8..6f0c748 100644
--- a/extension.json
+++ b/extension.json
@@ -80,10 +80,15 @@
"key": 39,
"modifiers": [ "ctrl" ]
},
+ "skipBackSentence": {
+ "key": 37,
+ "modifiers": [ "ctrl" ]
+ },
"skipAheadWord": {
"key": 40,
"modifiers": [ "ctrl" ]
}
- }
+ },
+ "WikispeechSkipBackRewindsThreshold": 3.0
}
}
diff --git a/modules/ext.wikispeech.css b/modules/ext.wikispeech.css
index 0ea613c..d362b04 100644
--- a/modules/ext.wikispeech.css
+++ b/modules/ext.wikispeech.css
@@ -14,6 +14,10 @@
content: "Skip ahead sentence";
}
+.ext-wikispeech-skip-back-sentence:after {
+ content: "Skip back sentence";
+}
+
.ext-wikispeech-skip-ahead-word:after {
content: "Skip ahead word";
}
diff --git a/modules/ext.wikispeech.js b/modules/ext.wikispeech.js
index 1a3b0b4..7bce8a6 100644
--- a/modules/ext.wikispeech.js
+++ b/modules/ext.wikispeech.js
@@ -5,28 +5,39 @@
self = this;
$currentUtterance = $();
- this.addButtons = function () {
- self.addPlayStopButton();
- self.addSkipAheadSentenceButton();
- self.addSkipAheadWordButton();
- };
-
/**
- * Add a button for starting and stopping recitation to the
page.
- *
- * When no utterance is playing, clicking starts the first
utterance.
- * When an utterance is being played, clicking stops the
playback.
- * The button changes appearance to reflect its current
function.
+ * Add buttons for controlling playback to the top of the
article.
*/
- this.addPlayStopButton = function () {
- var $playStopButton = $( '<button></button>' )
- .attr( 'id', 'ext-wikispeech-play-stop-button' )
- .addClass( 'ext-wikispeech-play' );
- $( '#firstHeading' ).append( $playStopButton );
- // For some reason, testing doesn't work with
- // .click( self.playOrStop ).
- $playStopButton.click( function () { self.playOrStop();
} );
+ this.addButtons = function () {
+ self.addButton(
+ 'ext-wikispeech-play-stop-button',
+ 'ext-wikispeech-play',
+ self.playOrStop
+ );
+ self.addButton(
+ 'ext-wikispeech-skip-ahead-sentence-button',
+ 'ext-wikispeech-skip-ahead-sentence',
+ self.skipAheadUtterance
+ );
+ self.addButton(
+ 'ext-wikispeech-skip-back-sentence-button',
+ 'ext-wikispeech-skip-back-sentence',
+ self.skipBackUtterance
+ );
+ self.addButton(
+ 'ext-wikispeech-skip-ahead-word-button',
+ 'ext-wikispeech-skip-ahead-word',
+ self.skipAheadToken
+ );
+ };
+
+ this.addButton = function ( id, cssClass, onClickFunction ) {
+ var $button = $( '<button></button>' )
+ .attr( 'id', id )
+ .addClass( cssClass );
+ $( '#firstHeading' ).append( $button );
+ $button.click( onClickFunction );
};
/**
@@ -101,23 +112,6 @@
};
/**
- * Add a button for skipping to the next sentence.
- *
- * This actually skips to the next utterance; it's assumed that
the
- * utterances are sentences, where titles count as sentences.
- */
-
- this.addSkipAheadSentenceButton = function () {
- var $skipAheadSentenceButton = $( '<button></button>' )
- .attr( 'id',
'ext-wikispeech-skip-ahead-sentence-button' )
- .addClass( 'ext-wikispeech-skip-ahead-sentence'
);
- $( '#firstHeading' ).append( $skipAheadSentenceButton );
- $skipAheadSentenceButton.click( function () {
- self.skipAheadUtterance();
- } );
- };
-
- /**
* Skip to the next utterance.
*
* Stop the current utterance and start playing the next one.
@@ -156,17 +150,30 @@
};
/**
- * Add a button for skipping to the next word.
+ * Skip to the previous utterance.
+ *
+ * Stop the current utterance and start playing the previous
one. If
+ * the first utterance is playing, restart it.
*/
- this.addSkipAheadWordButton = function () {
- var $button = $( '<button></button>' )
- .attr( 'id',
'ext-wikispeech-skip-ahead-word-button' )
- .addClass( 'ext-wikispeech-skip-ahead-word' );
- $( '#firstHeading' ).append( $button );
- $button.click( function () {
- self.skipAheadToken();
- } );
+ this.skipBackUtterance = function () {
+ var previousUtterance, rewindThreshold, $audio, time;
+
+ previousUtterance =
+ self.getPreviousUtterance( $currentUtterance );
+ if ( previousUtterance.length ) {
+ rewindThreshold = mw.config.get(
+ 'wgWikispeechSkipBackRewindsThreshold'
);
+ $audio = $currentUtterance.children( 'audio' );
+ time = $audio.prop( 'currentTime' );
+ if ( time > rewindThreshold ) {
+ $audio.prop( 'currentTime', 0.0 );
+ } else {
+ self.playUtterance( previousUtterance );
+ }
+ } else if ( self.isPlaying() ) {
+ self.play();
+ }
};
/**
@@ -240,6 +247,11 @@
) {
self.skipAheadUtterance();
return false;
+ } else if ( self.eventMatchShortcut(
+ event,
+ shortcuts.skipBackSentence )
+ ) {
+ self.skipBackUtterance();
} else if (
self.eventMatchShortcut( event,
shortcuts.skipAheadWord )
) {
@@ -310,6 +322,55 @@
};
/**
+ * Get the utterance after the given utterance.
+ *
+ * @param $utterance The original utterance.
+ * @return The utterance after the original utterance.
+ */
+
+ this.getNextUtterance = function ( $utterance ) {
+ return self.getUtteranceByOffset( $utterance, 1 );
+ };
+
+ /**
+ * Get the utterance by offset from another utterance.
+ *
+ * @param $utterance The original utterance.
+ * @param {number} offest The difference, in index, to the
wanted
+ * utterance. Can be negative for preceeding utterances.
+ * @return The utterance after the original utterance. Empty
object if
+ * $utterance isn't a valid utterance or if an uttrance
couldn't be
+ * found.
+ */
+
+ this.getUtteranceByOffset = function ( $utterance, offset ) {
+ var utteranceIdParts, nextUtteranceIndex,
nextUtteranceId;
+
+ if ( !$utterance.length ) {
+ return $();
+ }
+ // Utterance id's follow the pattern "utterance-x",
where x is
+ // the index.
+ utteranceIdParts = $utterance.attr( 'id' ).split( '-' );
+ nextUtteranceIndex =
+ parseInt( utteranceIdParts[ 1 ], 10 ) + offset;
+ utteranceIdParts[ 1 ] = nextUtteranceIndex;
+ nextUtteranceId = utteranceIdParts.join( '-' );
+ return $( '#' + nextUtteranceId );
+ };
+
+ /**
+ * Get the utterance before the given utterance.
+ *
+ * @param $utterance The original utterance.
+ * @return The utterance before the original utterance.
+ */
+
+ this.getPreviousUtterance = function ( $utterance ) {
+ return self.getUtteranceByOffset( $utterance, -1 );
+ };
+
+ /**
* Request audio for an utterance.
*
* Adds audio and token elements when the response is received.
diff --git a/tests/qunit/ext.wikispeech.test.js
b/tests/qunit/ext.wikispeech.test.js
index cfbbd6e..b5bb6bf 100644
--- a/tests/qunit/ext.wikispeech.test.js
+++ b/tests/qunit/ext.wikispeech.test.js
@@ -33,11 +33,19 @@
key: 39,
modifiers: [ 'ctrl' ]
},
+ skipBackSentence: {
+ key: 37,
+ modifiers: [ 'ctrl' ]
+ },
skipAheadWord: {
key: 40,
modifiers: [ 'ctrl' ]
}
}
+ );
+ mw.config.set(
+ 'wgWikispeechSkipBackRewindsThreshold',
+ 3.0
);
},
teardown: function () {
@@ -170,7 +178,7 @@
} );
QUnit.test( 'addButtons()', function ( assert ) {
- assert.expect( 3 );
+ assert.expect( 4 );
wikispeech.addButtons();
assert.strictEqual(
@@ -179,6 +187,10 @@
);
assert.strictEqual(
$( '#firstHeading
#ext-wikispeech-skip-ahead-sentence-button' ).length,
+ 1
+ );
+ assert.strictEqual(
+ $( '#firstHeading
#ext-wikispeech-skip-back-sentence-button' ).length,
1
);
assert.strictEqual(
@@ -204,13 +216,24 @@
*/
function testClickButton( assert, functionName, buttonId ) {
+ var baseFunction, called;
+
assert.expect( 1 );
+ // Sinon has problems spying on the on click functions. It
seems to
+ // be caused by function names getting lost at some point. This
+ // replaces the tested function and just sets a boolean if it's
called.
+ baseFunction = wikispeech[ functionName ];
+ called = false;
+ wikispeech[ functionName ] = function () {
+ called = true;
+ };
wikispeech.addButtons();
- sinon.spy( wikispeech, functionName );
$( buttonId ).click();
- assert.strictEqual( wikispeech[ functionName ].called, true );
+ assert.strictEqual( called, true );
+ // Restore the tested function.
+ wikispeech[ functionName ] = baseFunction;
}
QUnit.test( 'Clicking skip ahead sentence button', function ( assert ) {
@@ -218,6 +241,14 @@
assert,
'skipAheadUtterance',
'#ext-wikispeech-skip-ahead-sentence-button'
+ );
+ } );
+
+ QUnit.test( 'Clicking skip back sentence button', function ( assert ) {
+ testClickButton(
+ assert,
+ 'skipBackUtterance',
+ '#ext-wikispeech-skip-back-sentence-button'
);
} );
@@ -302,7 +333,7 @@
QUnit.test( 'stop()', function ( assert ) {
assert.expect( 4 );
- wikispeech.addPlayStopButton();
+ wikispeech.addButtons();
wikispeech.play();
wikispeech.prepareUtterance( $( '#utterance-0' ) );
$( '#utterance-0 audio' ).prop( 'currentTime', 1 );
@@ -329,7 +360,7 @@
QUnit.test( 'play()', function ( assert ) {
var $firstUtterance = $( '#utterance-0' );
assert.expect( 3 );
- wikispeech.addPlayStopButton();
+ wikispeech.addButtons();
wikispeech.prepareUtterance( $firstUtterance );
wikispeech.play();
@@ -353,6 +384,7 @@
QUnit.test( 'skipAheadUtterance()', function ( assert ) {
assert.expect( 2 );
wikispeech.prepareUtterance( $( '#utterance-0' ) );
+ wikispeech.prepareUtterance( $( '#utterance-1' ) );
wikispeech.play();
wikispeech.skipAheadUtterance();
@@ -372,6 +404,62 @@
wikispeech.skipAheadUtterance();
assert.strictEqual( wikispeech.stop.called, true );
+ } );
+
+ QUnit.test( 'skipBackUtterance()', function ( assert ) {
+ assert.expect( 2 );
+ wikispeech.prepareUtterance( $( '#utterance-0' ) );
+ wikispeech.prepareUtterance( $( '#utterance-1' ) );
+ wikispeech.playUtterance( $( '#utterance-1' ) );
+
+ wikispeech.skipBackUtterance();
+
+ assert.strictEqual( $( '#utterance-1 audio' ).prop( 'paused' ),
true );
+ assert.strictEqual(
+ $( '#utterance-0 audio' ).prop( 'paused' ),
+ false
+ );
+ } );
+
+ QUnit.test( 'skipBackUtterance(): restart if first utterance', function
( assert ) {
+ assert.expect( 2 );
+ wikispeech.prepareUtterance( $( '#utterance-0' ) );
+ wikispeech.playUtterance( $( '#utterance-0' ) );
+ $( '#utterance-0 audio' ).prop( 'currentTime', 1.0 );
+
+ wikispeech.skipBackUtterance();
+
+ assert.strictEqual(
+ $( '#utterance-0 audio' ).prop( 'paused' ),
+ false
+ );
+ assert.strictEqual(
+ $( '#utterance-0 audio' ).prop( 'currentTime' ),
+ 0.0
+ );
+ } );
+
+ QUnit.test( 'skipBackUtterance(): restart if played long enough',
function ( assert ) {
+ assert.expect( 3 );
+ wikispeech.prepareUtterance( $( '#utterance-0' ) );
+ wikispeech.prepareUtterance( $( '#utterance-1' ) );
+ wikispeech.playUtterance( $( '#utterance-1' ) );
+ $( '#utterance-1 audio' ).prop( 'currentTime', 3.1 );
+
+ wikispeech.skipBackUtterance();
+
+ assert.strictEqual(
+ $( '#utterance-1 audio' ).prop( 'paused' ),
+ false
+ );
+ assert.strictEqual(
+ $( '#utterance-1 audio' ).prop( 'currentTime' ),
+ 0.0
+ );
+ assert.strictEqual(
+ $( '#utterance-0 audio' ).prop( 'paused' ),
+ true
+ );
} );
QUnit.test( 'getNextUtterance()', function ( assert ) {
@@ -643,14 +731,14 @@
$( '#utterance-0' ).append(
$( '<tokens></tokens>' )
.append(
- $( '<token></token>' )
+ $( '<token></token>' )
.attr( 'time', 0.0 )
- )
+ )
.append(
- $( '<token></token>' )
+ $( '<token></token>' )
.attr( 'time', 1.0 )
- )
- );
+ )
+ );
wikispeech.play();
wikispeech.skipAheadToken();
@@ -667,14 +755,14 @@
$( '#utterance-0' ).append(
$( '<tokens></tokens>' )
.append(
- $( '<token></token>' )
+ $( '<token></token>' )
.attr( 'time', 0.0 )
- )
+ )
.append(
- $( '<token></token>' )
+ $( '<token></token>' )
.attr( 'time', 1.0 )
- )
- );
+ )
+ );
wikispeech.play();
$( '#utterance-0 audio' ).prop( 'currentTime', 1.1 );
sinon.spy( wikispeech, 'skipAheadUtterance' );
--
To view, visit https://gerrit.wikimedia.org/r/314664
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib63a013f525ce41f4e03bccf115ebe2a15fe4992
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikispeech
Gerrit-Branch: master
Gerrit-Owner: Sebastian Berlin (WMSE) <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits