Completed
Push — master ( a001d3...c4e388 )
by Jacob
01:45
created

tinymce/js/helpermodule.js   B

Complexity

Total Complexity 42
Complexity/F 2.47

Size

Lines of Code 237
Function Count 17

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
c 1
b 0
f 0
nc 64
dl 0
loc 237
rs 8.295
wmc 42
mnd 5
bc 44
fnc 17
bpm 2.5882
cpm 2.4705
noi 15

7 Functions

Rating   Name   Duplication   Size   Complexity  
B M.tinymce_recordrtc.handle_data_available 0 20 5
B M.tinymce_recordrtc.start_recording 0 61 8
B M.tinymce_recordrtc.make_xmlhttprequest 0 27 1
A M.tinymce_recordrtc.pad 0 9 2
A M.tinymce_recordrtc.set_time 0 12 2
B M.tinymce_recordrtc.upload_to_server 0 41 1
B M.tinymce_recordrtc.handle_stop 0 44 1

How to fix   Complexity   

Complexity

Complex classes like tinymce/js/helpermodule.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// TinyMCE recordrtc library functions.
2
// @package    tinymce_recordrtc.
3
// @author     Jesus Federico  (jesus [at] blindsidenetworks [dt] com).
4
// @copyright  2016 to present, Blindside Networks Inc.
5
// @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
6
7
// Scrutinizer CI directives.
8
/** global: M */
9
/** global: Y */
10
11
M.tinymce_recordrtc = M.tinymce_recordrtc || {};
12
13
// Initialize some variables.
14
var blobSize = null;
15
var chunks = null;
16
var countdownSeconds = null;
17
var countdownTicker = null;
18
var maxUploadSize = null;
19
20
// Add chunks of audio/video to array when made available.
21
M.tinymce_recordrtc.handle_data_available = function(event) {
22
    // Size of all recorded data so far.
23
    blobSize += event.data.size;
24
25
    // Push recording slice to array.
26
    // If total size of recording so far exceeds max upload limit, stop recording.
27
    // An extra condition exists to avoid displaying alert twice.
28
    if ((blobSize >= maxUploadSize) && (!window.localStorage.getItem('alerted'))) {
29
        window.localStorage.setItem('alerted', 'true');
30
31
        Y.use('node-event-simulate', function() {
32
            startStopBtn.simulate('click');
0 ignored issues
show
Bug introduced by
The variable startStopBtn seems to be never declared. If this is a global, consider adding a /** global: startStopBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
33
        });
34
        M.tinymce_recordrtc.show_alert('nearingmaxsize');
35
    } else if ((blobSize >= maxUploadSize) && (window.localStorage.getItem('alerted') === 'true')) {
36
        window.localStorage.removeItem('alerted');
37
    } else {
38
        chunks.push(event.data);
39
    }
40
};
41
42
// Handle recording end.
43
M.tinymce_recordrtc.handle_stop = function() {
44
    // Set source of audio player.
45
    var blob = new window.Blob(chunks, {type: mediaRecorder.mimeType});
0 ignored issues
show
Bug introduced by
The variable mediaRecorder seems to be never declared. If this is a global, consider adding a /** global: mediaRecorder */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
46
    player.set('src', window.URL.createObjectURL(blob));
0 ignored issues
show
Bug introduced by
The variable player seems to be never declared. If this is a global, consider adding a /** global: player */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
47
48
    // Show audio player with controls enabled, and unmute.
49
    player.set('muted', false);
50
    player.set('controls', true);
51
    player.ancestor().ancestor().removeClass('hide'); // AUDIO ONLY
52
53
    // Show upload button.
54
    uploadBtn.set('disabled', false);
0 ignored issues
show
Bug introduced by
The variable uploadBtn seems to be never declared. If this is a global, consider adding a /** global: uploadBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
55
    uploadBtn.set('textContent', M.util.get_string('attachrecording', 'tinymce_recordrtc'));
56
    uploadBtn.ancestor().ancestor().removeClass('hide');
57
58
    // Handle when upload button is clicked.
59
    uploadBtn.on('click', function() {
60
        // Trigger error if no recording has been made.
61
        if (!player.get('src') || chunks === []) {
0 ignored issues
show
Bug introduced by
The variable player seems to be never declared. If this is a global, consider adding a /** global: player */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
62
            M.tinymce_recordrtc.show_alert('norecordingfound');
63
        } else {
64
            uploadBtn.set('disabled', true);
0 ignored issues
show
Bug introduced by
The variable uploadBtn seems to be never declared. If this is a global, consider adding a /** global: uploadBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
65
66
            // Upload recording to server.
67
            M.tinymce_recordrtc.upload_to_server(recType, function(progress, fileURLOrError) {
0 ignored issues
show
Bug introduced by
The variable recType seems to be never declared. If this is a global, consider adding a /** global: recType */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
68
                if (progress === 'ended') { // Insert annotation in text.
69
                    uploadBtn.set('disabled', false);
0 ignored issues
show
Bug introduced by
The variable uploadBtn seems to be never declared. If this is a global, consider adding a /** global: uploadBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
70
                    M.tinymce_recordrtc.insert_annotation(recType, fileURLOrError);
0 ignored issues
show
Bug introduced by
The variable recType seems to be never declared. If this is a global, consider adding a /** global: recType */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
71
                } else if (progress === 'upload-failed') { // Show error message in upload button.
72
                    uploadBtn.set('disabled', false);
73
                    uploadBtn.set('textContent', M.util.get_string('uploadfailed', 'tinymce_recordrtc') + ' ' + fileURLOrError);
74
                } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
75
                    uploadBtn.set('disabled', false);
76
                    uploadBtn.set('textContent', M.util.get_string('uploadfailed404', 'tinymce_recordrtc'));
77
                } else if (progress === 'upload-aborted') {
78
                    uploadBtn.set('disabled', false);
79
                    uploadBtn.set('textContent', M.util.get_string('uploadaborted', 'tinymce_recordrtc') + ' ' + fileURLOrError);
80
                } else {
81
                    uploadBtn.set('textContent', progress);
82
                }
83
            });
84
        }
85
    });
86
};
87
88
// Get everything set up to start recording.
89
M.tinymce_recordrtc.start_recording = function(type, stream) {
90
    // The options for the recording codecs and bitrates.
91
    var options = null;
92
    if (type === 'audio') {
93
        if (window.MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) {
94
            options = {
95
                audioBitsPerSecond: window.params.audiobitrate,
96
                mimeType: 'audio/webm;codecs=opus'
97
            };
98
        } else if (window.MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')) {
99
            options = {
100
                audioBitsPerSecond: window.params.audiobitrate,
101
                mimeType: 'audio/ogg;codecs=opus'
102
            };
103
        }
104
    } else {
105
        if (window.MediaRecorder.isTypeSupported('video/webm;codecs=vp9,opus')) {
106
            options = {
107
                audioBitsPerSecond: window.params.audiobitrate,
108
                videoBitsPerSecond: window.params.videobitrate,
109
                mimeType: 'video/webm;codecs=vp9,opus'
110
            };
111
        } else if (window.MediaRecorder.isTypeSupported('video/webm;codecs=h264,opus')) {
112
            options = {
113
                audioBitsPerSecond: window.params.audiobitrate,
114
                videoBitsPerSecond: window.params.videobitrate,
115
                mimeType: 'video/webm;codecs=h264,opus'
116
            };
117
        } else if (window.MediaRecorder.isTypeSupported('video/webm;codecs=vp8,opus')) {
118
            options = {
119
                audioBitsPerSecond: window.params.audiobitrate,
120
                videoBitsPerSecond: window.params.videobitrate,
121
                mimeType: 'video/webm;codecs=vp8,opus'
122
            };
123
        }
124
    }
125
126
    // If none of the options above are supported, fall back on browser defaults.
127
    mediaRecorder = options ? new window.MediaRecorder(stream, options)
0 ignored issues
show
Bug introduced by
The variable mediaRecorder seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.mediaRecorder.
Loading history...
128
                            : new window.MediaRecorder(stream);
129
130
    // Initialize MediaRecorder events and start recording.
131
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handle_data_available;
132
    mediaRecorder.onstop = M.tinymce_recordrtc.handle_stop;
133
    mediaRecorder.start(1000); // Capture in 1s chunks. Must be set to work with Firefox.
134
135
    // Mute audio, distracting while recording.
136
    player.set('muted', true);
0 ignored issues
show
Bug introduced by
The variable player seems to be never declared. If this is a global, consider adding a /** global: player */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
137
138
    // Set recording timer to the time specified in the settings.
139
    countdownSeconds = window.params.timelimit;
140
    countdownSeconds++;
141
    var timerText = M.util.get_string('stoprecording', 'tinymce_recordrtc');
142
    timerText += ' (<span id="minutes"></span>:<span id="seconds"></span>)';
143
    startStopBtn.setHTML(timerText);
0 ignored issues
show
Bug introduced by
The variable startStopBtn seems to be never declared. If this is a global, consider adding a /** global: startStopBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
144
    M.tinymce_recordrtc.set_time();
145
    countdownTicker = window.setInterval(M.tinymce_recordrtc.set_time, 1000);
146
147
    // Make button clickable again, to allow stopping recording.
148
    startStopBtn.set('disabled', false);
149
};
150
151
// Upload recorded audio/video to server.
152
M.tinymce_recordrtc.upload_to_server = function(type, callback) {
153
    var xhr = new window.XMLHttpRequest();
154
155
    // Get src media of audio/video tag.
156
    xhr.open('GET', player.get('src'), true);
0 ignored issues
show
Bug introduced by
The variable player seems to be never declared. If this is a global, consider adding a /** global: player */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
157
    xhr.responseType = 'blob';
158
159
    xhr.onload = function() {
160
        if (xhr.status === 200) { // If src media was successfully retrieved.
161
            // blob is now the media that the audio/video tag's src pointed to.
162
            var blob = this.response;
163
164
            // Generate filename with random ID and file extension.
165
            var fileName = (Math.random() * 1000).toString().replace('.', '');
166
            if (type === 'audio') {
167
                fileName += '-audio.ogg';
168
            } else {
169
                fileName += '-video.webm';
170
            }
171
172
            // Create FormData to send to PHP upload/save script.
173
            var formData = new window.FormData();
174
            formData.append('contextid', window.params.contextid);
175
            formData.append('sesskey', window.params.sesskey);
176
            formData.append(type + '-filename', fileName);
177
            formData.append(type + '-blob', blob);
178
179
            // Pass FormData to PHP script using XHR.
180
            M.tinymce_recordrtc.make_xmlhttprequest('save.php', formData, function(progress, responseText) {
181
                if (progress === 'upload-ended') {
182
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
183
                    callback('ended', initialURL + responseText);
184
                } else {
185
                    callback(progress);
186
                }
187
            });
188
        }
189
    };
190
191
    xhr.send();
192
};
193
194
// Handle XHR sending/receiving/status.
195
M.tinymce_recordrtc.make_xmlhttprequest = function(url, data, callback) {
196
    var xhr = new window.XMLHttpRequest();
197
198
    xhr.onreadystatechange = function() {
199
        if ((xhr.readyState === 4) && (xhr.status === 200)) { // When request is finished and successful.
200
            callback('upload-ended', xhr.responseText);
201
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
202
            callback('upload-failed-404');
203
        }
204
    };
205
206
    xhr.upload.onprogress = function(event) {
207
        callback(Math.round(event.loaded / event.total * 100) + "% " + M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
208
    };
209
210
    xhr.upload.onerror = function(error) {
211
        callback('upload-failed', error);
212
    };
213
214
    xhr.upload.onabort = function(error) {
215
        callback('upload-aborted', error);
216
    };
217
218
    // POST FormData to PHP script that handles uploading/saving.
219
    xhr.open('POST', url);
220
    xhr.send(data);
221
};
222
223
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
224
M.tinymce_recordrtc.pad = function(val) {
225
    var valString = val + "";
226
227
    if (valString.length < 2) {
228
        return "0" + valString;
229
    } else {
230
        return valString;
231
    }
232
};
233
234
// Functionality to make recording timer count down.
235
// Also makes recording stop when time limit is hit.
236
M.tinymce_recordrtc.set_time = function() {
237
    countdownSeconds--;
238
239
    startStopBtn.one('span#seconds').set('textContent', M.tinymce_recordrtc.pad(countdownSeconds % 60));
0 ignored issues
show
Bug introduced by
The variable startStopBtn seems to be never declared. If this is a global, consider adding a /** global: startStopBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
240
    startStopBtn.one('span#minutes').set('textContent', M.tinymce_recordrtc.pad(window.parseInt(countdownSeconds / 60, 10)));
241
242
    if (countdownSeconds === 0) {
243
        Y.use('node-event-simulate', function() {
244
            startStopBtn.simulate('click');
0 ignored issues
show
Bug introduced by
The variable startStopBtn seems to be never declared. If this is a global, consider adding a /** global: startStopBtn */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
245
        });
246
    }
247
};
248