GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#11)
by
unknown
01:40
created

tinymce/js/videomodule.js   C

Complexity

Total Complexity 58
Complexity/F 1.71

Size

Lines of Code 409
Function Count 34

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 0
nc 4224
dl 0
loc 409
rs 5.4871
c 4
b 0
f 0
wmc 58
mnd 3
bc 54
fnc 34
bpm 1.5882
cpm 1.7058
noi 27

15 Functions

Rating   Name   Duplication   Size   Complexity  
A videomodule.js ➔ d 0 3 1
B 0 21 5
A M.tinymce_recordrtc.create_annotation 0 12 1
A M.tinymce_recordrtc.stopRecording 0 54 1
A M.tinymce_recordrtc.setTime 0 10 2
A M.tinymce_recordrtc.insert_annotation 0 6 1
A M.tinymce_recordrtc.pad 0 9 2
B M.tinymce_recordrtc.makeXMLHttpRequest 0 31 1
B M.tinymce_recordrtc.uploadToServer 0 40 1
B M.tinymce_recordrtc.view_init 0 123 2
A M.tinymce_recordrtc.captureUserMedia 0 3 1
A M.tinymce_recordrtc.handleStop 0 3 1
A M.tinymce_recordrtc.startRecording 0 19 1
B M.tinymce_recordrtc.captureAudioVideo 0 29 1
A M.tinymce_recordrtc.handleDataAvailable 0 3 1

How to fix   Complexity   

Complexity

Complex classes like tinymce/js/videomodule.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
/** global: M */
8
/** global: URL */
9
/** global: params */
10
/** global: initialized variables */
11
12
M.tinymce_recordrtc = M.tinymce_recordrtc || {};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
13
14
// Extract plugin settings to params hash.
15
(function() {
16
    var params = {};
17
    var r = /([^&=]+)=?([^&]*)/g;
18
19
    var d = function(s) {
20
        return decodeURIComponent(s.replace(/\+/g, ' '));
21
    };
22
23
    var search = window.location.search;
24
    var match = r.exec(search.substring(1));
25
    while (match) {
26
        params[d(match[1])] = d(match[2]);
27
28
        if (d(match[2]) === 'true' || d(match[2]) === 'false') {
29
            params[d(match[1])] = d(match[2]) === 'true' ? true : false;
30
        }
31
        match = r.exec(search.substring(1));
32
    }
33
34
    window.params = params;
35
})();
36
37
// Initialize some variables.
38
var player = null;
39
var startStopBtn = null;
40
var uploadBtn = null;
41
var countdownSeconds = null;
42
var countdownTicker = null;
43
var mediaRecorder = null;
44
var chunks = null;
45
46
/**
47
 * This function is initialized from PHP
48
 *
49
 * @param {Object}
0 ignored issues
show
Documentation introduced by
The parameter * does not exist. Did you maybe forget to remove this comment?
Loading history...
50
 *            Y YUI instance
51
 */
52
M.tinymce_recordrtc.view_init = function() {
53
    // Assignment of global variables.
54
    player = document.querySelector('video#player');
55
    startStopBtn = document.querySelector('button#start-stop');
56
    uploadBtn = document.querySelector('button#upload');
57
58
    // Display "consider switching browsers" message if not using:
59
    // - Firefox 29+;
60
    // - Chrome 49+;
61
    // - Opera 36+.
62
    if (!((bowser.firefox && bowser.version >= 29) ||
0 ignored issues
show
Bug introduced by
The variable bowser seems to be never declared. If this is a global, consider adding a /** global: bowser */ 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...
63
          (bowser.chrome && bowser.version >= 49) ||
64
          (bowser.opera && bowser.version >= 36))) {
65
        var alert = document.querySelector('div[id=alert-warning]');
66
        alert.parentElement.parentElement.classList.remove('hide');
67
    }
68
69
    // Run when user clicks on "record" button.
70
    startStopBtn.onclick = function() {
71
        var btn = this;
72
        btn.disabled = true;
73
74
        // If button is displaying "Start Recording" or "Record Again".
75
        if ((btn.textContent === M.util.get_string('startrecording', 'tinymce_recordrtc')) ||
76
            (btn.textContent === M.util.get_string('recordagain', 'tinymce_recordrtc')) ||
77
            (btn.textContent === M.util.get_string('recordingfailed', 'tinymce_recordrtc'))) {
78
            // Hide alert-danger if it is shown.
79
            var alert = document.querySelector('div[id=alert-danger]');
80
            alert.parentElement.parentElement.classList.add('hide');
81
82
            // Make sure the upload button is not shown.
83
            uploadBtn.parentElement.parentElement.classList.add('hide');
84
85
            // Change look of recording button.
86
            startStopBtn.classList.remove('btn-outline-danger');
87
            startStopBtn.classList.add('btn-danger');
88
89
            // Empty the array containing the previously recorded chunks.
90
            chunks = [];
91
92
            // Initialize common configurations.
93
            var commonConfig = {
94
                // When the stream is captured from the microphone/webcam.
95
                onMediaCaptured: function(stream) {
96
                    // Make video stream available at a higher level by making it a property of btn.
97
                    btn.stream = stream;
98
99
                    if (btn.mediaCapturedCallback) {
100
                        btn.mediaCapturedCallback();
101
                    }
102
                },
103
104
                // Revert button to "Record Again" when recording is stopped.
105
                onMediaStopped: function(btnLabel) {
106
                    btn.textContent = btnLabel;
107
                },
108
109
                // Handle recording errors.
110
                onMediaCapturingFailed: function(error) {
111
                    var btnLabel = null;
112
113
                    // If Firefox and Permission Denied error.
114
                    if ((error.name === 'PermissionDeniedError') && bowser.firefox) {
0 ignored issues
show
Bug introduced by
The variable bowser seems to be never declared. If this is a global, consider adding a /** global: bowser */ 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...
115
                        InstallTrigger.install({
0 ignored issues
show
Bug introduced by
The variable InstallTrigger seems to be never declared. If this is a global, consider adding a /** global: InstallTrigger */ 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...
116
                            'Foo': {
117
                                // Link: https://addons.mozilla.org/firefox/downloads/latest/655146/addon-655146...
118
                                // ...-latest.xpi?src=dp-btn-primary.
119
                                URL: 'https://addons.mozilla.org/en-US/firefox/addon/enable-screen-capturing/',
120
                                toString: function() {
121
                                    return this.URL;
122
                                }
123
                            }
124
                        });
125
126
                        btnLabel = M.util.get_string('startrecording', 'tinymce_recordrtc');
127
                    } else if ((error.name === 'DevicesNotFoundError') ||
128
                               (error.name === 'NotFoundError')) { // If Device Not Found error.
129
                        var alert = document.querySelector('div[id=alert-danger]');
130
                        alert.parentElement.parentElement.classList.remove('hide');
131
                        alert.textContent = M.util.get_string('inputdevicealert', 'tinymce_recordrtc') + ' ' + M.util.get_string('inputdevicealert', 'tinymce_recordrtc');
132
133
                        btnLabel = M.util.get_string('recordingfailed', 'tinymce_recordrtc');
134
                    }
135
136
                    // Proceed to treat as a stopped recording.
137
                    commonConfig.onMediaStopped(btnLabel);
138
                }
139
            };
140
141
            // Show video tag without controls to view webcam stream.
142
            player.parentElement.parentElement.classList.remove('hide');
143
            player.controls = false;
144
145
            // Capture audio+video stream from webcam/microphone.
146
            M.tinymce_recordrtc.captureAudioVideo(commonConfig);
147
148
            // When audio+video stream is successfully captured, start recording.
149
            btn.mediaCapturedCallback = function() {
150
                M.tinymce_recordrtc.startRecording(btn.stream);
151
            };
152
153
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
154
        } else { // If button is displaying "Stop Recording".
155
            // First of all clears the countdownTicker.
156
            clearInterval(countdownTicker);
157
158
            // Disable "Record Again" button for 1s to allow background processing (closing streams).
159
            setTimeout(function() {
160
                btn.disabled = false;
161
            }, 1000);
162
163
            // Stop recording.
164
            M.tinymce_recordrtc.stopRecording(btn.stream);
165
166
            // Change button to offer to record again.
167
            btn.textContent = M.util.get_string('recordagain', 'tinymce_recordrtc');
168
            startStopBtn.classList.remove('btn-danger');
169
            startStopBtn.classList.add('btn-outline-danger');
170
171
            return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
172
        }
173
    };
174
};
175
176
/////////////////////////
177
// Functions for capturing, recording, and uploading stream.
178
/////////////////////////
179
180
// Capture webcam/microphone stream.
181
M.tinymce_recordrtc.captureUserMedia = function(mediaConstraints, successCallback, errorCallback) {
182
    navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ 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...
183
};
184
185
// Setup to get audio+video stream from microphone/webcam.
186
M.tinymce_recordrtc.captureAudioVideo = function(config) {
187
    M.tinymce_recordrtc.captureUserMedia(
188
        // Media constraints.
189
        {
190
            audio: true,
191
            video: {
192
              width: {ideal: 640},
193
              height: {ideal: 480}
194
            }
195
        },
196
197
        // Success callback.
198
        function(audioVideoStream) {
199
            console.log('getUserMedia() got stream:', audioVideoStream);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
200
201
            // Set video player to play microphone+webcam stream.
202
            player.srcObject = audioVideoStream;
203
            player.play();
204
205
            config.onMediaCaptured(audioVideoStream);
206
        },
207
208
        // Error callback.
209
        function(error) {
210
            console.log('getUserMedia() error:', error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
211
            config.onMediaCapturingFailed(error);
212
        }
213
    );
214
};
215
216
// Add chunks of video to array when made available.
217
M.tinymce_recordrtc.handleDataAvailable = function(event) {
218
    chunks.push(event.data);
219
};
220
221
// Output information to console when recording stopped.
222
M.tinymce_recordrtc.handleStop = function(event) {
223
    console.log('MediaRecorder stopped:', event);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
224
};
225
226
M.tinymce_recordrtc.startRecording = function(stream) {
227
    // Initialize recording of stream.
228
    mediaRecorder = new MediaRecorder(stream);
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...
229
    console.log('Created MediaRecorder:', mediaRecorder);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
230
231
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handleDataAvailable;
232
    mediaRecorder.onstop = M.tinymce_recordrtc.handleStop;
233
    mediaRecorder.start(10); // Capture in 10ms chunks. Must be set to work with Firefox.
234
    console.log('MediaRecorder started:', mediaRecorder);
235
236
    // Set recording timer to the time specified in the settings.
237
    countdownSeconds = params['timelimit'];
238
    countdownSeconds++;
239
    startStopBtn.innerHTML = M.util.get_string('stoprecording', 'tinymce_recordrtc') +
240
                             ' (<span id="minutes"></span>:<span id="seconds"></span>)';
241
    M.tinymce_recordrtc.setTime();
242
    startStopBtn.disabled = false;
243
    countdownTicker = setInterval(M.tinymce_recordrtc.setTime, 1000);
244
};
245
246
M.tinymce_recordrtc.stopRecording = function(stream) {
247
    mediaRecorder.stop();
248
249
    stream.getTracks().forEach(function(track) {
250
        track.stop();
251
        console.log('MediaTrack stopped:', track);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
252
    });
253
254
    // Set source of video player, then show it with controls enabled.
255
    var blob = new Blob(chunks, {
0 ignored issues
show
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ 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...
256
        type: 'video/webm;codecs=vp8'
257
    });
258
    player.src = URL.createObjectURL(blob);
259
260
    player.muted = false;
261
    player.controls = true;
262
    player.play();
263
264
    player.onended = function() {
265
        player.pause();
266
    };
267
268
    // Show upload button.
269
    uploadBtn.parentElement.parentElement.classList.remove('hide');
270
    uploadBtn.textContent = M.util.get_string('attachrecording', 'tinymce_recordrtc');
271
    uploadBtn.disabled = false;
272
273
    // Handle when upload button is clicked.
274
    uploadBtn.onclick = function() {
275
        // Trigger error if no recording has been made.
276
        if (!player.src || chunks === []) {
277
            return alert(M.util.get_string('norecordingfound', 'tinymce_recordrtc'));
278
        }
279
280
        var btn = uploadBtn;
281
        btn.disabled = true;
282
283
        // Upload recording to server.
284
        M.tinymce_recordrtc.uploadToServer(function(progress, fileURL) {
285
            if (progress === 'ended') {
286
                btn.disabled = false;
287
                M.tinymce_recordrtc.insert_annotation(fileURL);
288
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
289
            } else if (progress === 'upload-failed') {
290
                btn.disabled = false;
291
                btn.textContent = M.util.get_string('uploadfailed', 'tinymce_recordrtc');
292
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
293
            } else {
294
                btn.textContent = progress;
295
                return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
296
            }
297
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
298
    };
299
};
300
301
// Upload recorded video to server.
302
M.tinymce_recordrtc.uploadToServer = function(callback) {
303
    var xhr = new XMLHttpRequest();
0 ignored issues
show
Bug introduced by
The variable XMLHttpRequest seems to be never declared. If this is a global, consider adding a /** global: XMLHttpRequest */ 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...
304
305
    // Get src URL of video tag.
306
    xhr.open('GET', player.src, true);
307
    xhr.responseType = 'blob';
308
309
    xhr.onload = function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
310
        if (xhr.status === 200) {
311
            var date = new Date();
0 ignored issues
show
Unused Code introduced by
The variable date seems to be never used. Consider removing it.
Loading history...
312
313
            // Variable blob is now the blob that the video tag's src pointed to.
314
            var blob = this.response;
315
            // Generate filename with random ID and file extension.
316
            var fileName = (Math.random() * 1000).toString().replace('.', '');
317
            fileName += '-video.webm';
318
319
            // Create FormData to send to PHP upload/save script.
320
            var formData = new FormData();
321
            formData.append('contextid', recordrtc.contextid);
0 ignored issues
show
Bug introduced by
The variable recordrtc seems to be never declared. If this is a global, consider adding a /** global: recordrtc */ 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...
322
            formData.append('sesskey', parent.M.cfg.sesskey);
0 ignored issues
show
Bug introduced by
The variable parent seems to be never declared. If this is a global, consider adding a /** global: parent */ 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...
323
            formData.append('video-filename', fileName);
324
            formData.append('video-blob', blob);
325
326
            // Pass FormData to PHP script using XHR.
327
            M.tinymce_recordrtc.makeXMLHttpRequest('save.php', formData, function(progress, responseText) {
328
                if (progress === 'upload-ended') {
329
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
330
                    callback('ended', initialURL + responseText);
331
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
332
                } else {
333
                    callback(progress);
334
                    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
335
                }
336
            });
337
        }
338
    };
339
340
    xhr.send();
341
}
342
343
// Handle XHR sending/receiving/status.
344
M.tinymce_recordrtc.makeXMLHttpRequest = function(url, data, callback) {
345
    var xhr = new XMLHttpRequest();
0 ignored issues
show
Bug introduced by
The variable XMLHttpRequest seems to be never declared. If this is a global, consider adding a /** global: XMLHttpRequest */ 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...
346
347
    xhr.onreadystatechange = function() {
348
        // When request is finished and successful.
349
        if ((xhr.readyState === 4) && (xhr.status === 200)) {
350
            callback('upload-ended', xhr.responseText);
351
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
352
            callback('upload-failed');
353
        }
354
    };
355
356
    xhr.upload.onprogress = function(event) {
357
        callback(Math.round(event.loaded / event.total * 100) + "% " +
358
                 M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
359
    };
360
361
    xhr.upload.onerror = function(error) {
362
        callback('upload-failed');
363
        console.error('XMLHttpRequest failed: ', error);
364
    };
365
366
    xhr.upload.onabort = function(error) {
367
        callback(M.util.get_string('uploadaborted', 'tinymce_recordrtc'));
368
        console.error('XMLHttpRequest aborted: ', error);
369
    };
370
371
    // POST FormData to PHP script that handles uploading/saving.
372
    xhr.open('POST', url);
373
    xhr.send(data);
374
}
375
376
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
377
M.tinymce_recordrtc.pad = function(val) {
378
    var valString = val + "";
379
380
    if (valString.length < 2) {
381
        return "0" + valString;
382
    } else {
383
        return valString;
384
    }
385
};
386
387
// Functionality to make recording timer count down.
388
// Also makes recording stop when time limit is hit.
389
M.tinymce_recordrtc.setTime = function() {
390
    countdownSeconds--;
391
392
    startStopBtn.querySelector('span#seconds').textContent = M.tinymce_recordrtc.pad(countdownSeconds % 60);
393
    startStopBtn.querySelector('span#minutes').textContent = M.tinymce_recordrtc.pad(parseInt(countdownSeconds / 60));
394
395
    if (countdownSeconds === 0) {
396
        startStopBtn.click();
397
    }
398
};
399
400
// Generates link to recorded annotation.
401
M.tinymce_recordrtc.create_annotation = function(recording_url) {
402
    // Create an icon linked to file in both editor text area and submission page:
403
    // var annotation = '<div id="recordrtc_annotation" class="text-center">...
404
    // ...<a target="_blank" href="' + recording_url + '"><img alt="RecordRTC Annotation"...
405
    // ...title="RecordRTC Annotation" src="' + recordrtc.icon32 + '" /></a></div>';.
406
407
    // Create link to file in editor text area and video player in submission page.
408
    var annotation = '<div id="recordrtc_annotation" class="text-center"><a target="_blank" href="' + recording_url + '">' +
409
                     M.util.get_string('annotation', 'tinymce_recordrtc') + '</a></div>';
410
411
    return annotation;
412
};
413
414
// Inserts link to annotation in editor text area.
415
M.tinymce_recordrtc.insert_annotation = function(recording_url) {
416
    var annotation = M.tinymce_recordrtc.create_annotation(recording_url);
417
418
    tinyMCEPopup.editor.execCommand('mceInsertContent', false, annotation);
0 ignored issues
show
Bug introduced by
The variable tinyMCEPopup seems to be never declared. If this is a global, consider adding a /** global: tinyMCEPopup */ 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...
419
    tinyMCEPopup.close();
420
};
421