Completed
Pull Request — master (#1993)
by Marc
02:11
created

TextToSpeechWidget   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 6
dl 0
loc 158
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 11 5
A run() 0 43 3
A createButton() 0 8 1
1
<?php
2
3
namespace luya\texttospeech;
4
5
use luya\base\Widget;
6
use luya\helpers\Json;
7
use Yii;
8
use yii\base\InvalidConfigException;
9
10
/**
11
 * Text to Speech.
12
 *
13
 * Using the browsers text to speech option to return a websites text. Either be providing the text as string or read the text from a given css class selector.
14
 *
15
 * Example using a Selector:
16
 *
17
 * ```
18
 * <?php
19
 * TextToSpeechWidget::widget(['targetSelector' => '#content']);
20
 * ?>
21
 * <div id="content">
22
 *    Hello World, this is LUYA Text to Speech!
23
 * </div>
24
 * ```
25
 *
26
 * By default the Widget will generate a play, pause and stop icon at the location the widget is integrated.
27
 *
28
 * @author Martin Petrasch <[email protected]>
29
 * @author Basil Suter <[email protected]>
30
 * @since 1.1.0
31
 */
32
class TextToSpeechWidget extends Widget
33
{
34
    /**
35
     * @var string The jQuery selector which should be used to read the content from, examples:
36
     * - `.container`: All elements which class `.container` will be spoken.
37
     * - `#content`: The element with id `id="content"` only will be spoken.
38
     *
39
     * The input data will be wrapped with `$()`.
40
     */
41
    public $targetSelector;
42
43
    /**
44
     * @var string If enabled, the {{$targetSelector}} attribute has no effect and only the text from the property will be read - on start.
45
     */
46
    public $text;
47
48
    /**
49
     * @var string The selector for the play button.
50
     */
51
    public $playButtonSelector = '#play-button';
52
53
    /**
54
     * @var string The selector for the pause button.
55
     */
56
    public $pauseButtonSelector = '#pause-button';
57
58
    /**
59
     * @var string The selector for the stop button.
60
     */
61
    public $stopButtonSelector = '#stop-button';
62
63
    /**
64
     * @var string The class which will be assigned to the play button when playing sound.
65
     */
66
    public $playButtonActiveClass = 'playing';
67
68
    /**
69
     * @var string The class which will be assigned to the pause button when pause button is clicked.
70
     */
71
    public $pauseButtonActiveClass = 'paused';
72
73
    /**
74
     * @var string The class of the div in which the buttons are located, the button wrapper div class.
75
     */
76
    public $containerClass = 'text-to-speech-container';
77
78
    /**
79
     * @var string The class which each of the text-to-speech buttons recieves.
80
     */
81
    public $buttonClass = 'btn text-to-speech-button';
82
83
    /**
84
     * @var integer The size of the default buttons, in pixel.
85
     */
86
    public $buttonSize = 30;
87
88
    /**
89
     * @var array|boolean You can either disable the default buttons by setting buttons to false, or you can provide an array with button configurations, each element requires the following keys:
90
     * - label:
91
     * - id:
92
     * - content:
93
     */
94
    public $buttons;
95
96
    /**
97
     * @var string The SVG code for the play icon when using default buttons.
98
     */
99
    public $playSVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"/></svg>';
100
101
    /**
102
     * @var string The SVG code for the stop icon when using default buttons.
103
     */
104
    public $stopSVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z"/></svg>';
105
106
    /**
107
     * @var string The SVG code for the pause icon when using default buttons.
108
     */
109
    public $pauseSVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"/></svg>';
110
    
111
    /**
112
     * {@inheritDoc}
113
     */
114
    public function init()
115
    {
116
        if (!$this->text && !$this->targetSelector) {
117
            throw new InvalidConfigException("Either text or targetSelector property must be configured.");
118
        }
119
        if ($this->buttons === null || $this->buttons === true) {
120
            $this->buttons[] = $this->createButton('Play', 'play-button', $this->playSVG);
121
            $this->buttons[] = $this->createButton('Pause', 'pause-button', $this->pauseSVG);
122
            $this->buttons[] = $this->createButton('Stop', 'stop-button', $this->stopSVG);
123
        }
124
    }
125
126
    /**
127
     * {@inheritDoc}
128
     */
129
    public function run()
130
    {
131
        TextToSpeechAsset::register($this->view);
132
133
        $config = Json::htmlEncode([
134
            'text' => $this->text ? $this->text : '', // must be an empty string as it will checked with .length
135
            'language' => Yii::$app->composition->langShortCode,
136
        ]);
137
138
        $this->view->registerJs("
139
            var tts = $.textToSpeech({$config});
140
            var playButton = $('{$this->playButtonSelector}');
141
            var pauseButton = $('{$this->pauseButtonSelector}');
142
            " . ($this->targetSelector ? "tts.setText($('{$this->targetSelector}').text());" : "") . "
143
            var applyPlayClasses = function () {
144
                playButton.addClass('{$this->playButtonActiveClass}');
145
                pauseButton.removeClass('{$this->pauseButtonActiveClass}');
146
            };
147
            var applyPauseClasses = function () {
148
                playButton.removeClass('{$this->playButtonActiveClass}');
149
                pauseButton.addClass('{$this->pauseButtonActiveClass}');
150
            };
151
            var cleanupClasses = function () {
152
                playButton.removeClass('{$this->playButtonActiveClass}');
153
                pauseButton.removeClass('{$this->pauseButtonActiveClass}');
154
            };
155
            $('document').on('textToSpeech:play', applyPlayClasses);
156
            $('document').on('textToSpeech:pause', applyPauseClasses);
157
            $('document').on('textToSpeech:resume', applyPlayClasses);
158
            $('document').on('textToSpeech:stop', cleanupClasses);
159
            playButton.on('click', function() { tts.play() });
160
            pauseButton.on('click', function() { tts.pause() });
161
            $('{$this->stopButtonSelector}').on('click', function() { tts.stop() });
162
        ");
163
164
        return $this->render('texttospeech', [
165
            'buttons' => $this->buttons,
166
            'id' => $this->getId(),
167
            'containerClass' => $this->containerClass,
168
            'buttonClass' => $this->buttonClass,
169
            'buttonSize' => $this->buttonSize,
170
        ]);
171
    }
172
173
    /**
174
     * Create a default button element.
175
     *
176
     * @param string $label
177
     * @param string $id
178
     * @param string $svg
179
     * @return array
180
     */
181
    protected function createButton($label, $id, $svg)
182
    {
183
        return [
184
            'label' => $label,
185
            'id' => $id,
186
            'content' => $svg,
187
        ];
188
    }
189
}
190