Completed
Push — master ( 4f51a7...df0dcd )
by Jacob
02:12
created

tinymce/js/commonmodule.js   B

Complexity

Total Complexity 40
Complexity/F 2.11

Size

Lines of Code 244
Function Count 19

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 0
c 3
b 0
f 0
nc 704
dl 0
loc 244
rs 8.2608
wmc 40
mnd 4
bc 40
fnc 19
bpm 2.1052
cpm 2.1052
noi 1

13 Functions

Rating   Name   Duplication   Size   Complexity  
B 0 21 5
A commonmodule.js ➔ d 0 3 1
A M.tinymce_recordrtc.pad 0 9 2
B M.tinymce_recordrtc.makeXMLHttpRequest 0 30 1
B M.tinymce_recordrtc.uploadToServer 0 41 1
A M.tinymce_recordrtc.insert_annotation 0 6 1
B M.tinymce_recordrtc.startRecording 0 59 8
A M.tinymce_recordrtc.check_browser 0 8 2
A M.tinymce_recordrtc.setTime 0 10 2
A M.tinymce_recordrtc.handleDataAvailable 0 3 1
A M.tinymce_recordrtc.create_annotation 0 6 1
A M.tinymce_recordrtc.check_secure 0 9 2
A M.tinymce_recordrtc.captureUserMedia 0 3 1

How to fix   Complexity   

Complexity

Complex classes like tinymce/js/commonmodule.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: tinyMCEPopup */
9
/** global: bowser */
10
/** global: params */
11
/** global: recordrtc */
12
13
M.tinymce_recordrtc = M.tinymce_recordrtc || {};
14
15
// Extract plugin settings to params hash.
16
(function() {
17
    var params = {};
18
    var r = /([^&=]+)=?([^&]*)/g;
19
20
    var d = function(s) {
21
        return decodeURIComponent(s.replace(/\+/g, ' '));
22
    };
23
24
    var search = window.location.search;
25
    var match = r.exec(search.substring(1));
26
    while (match) {
27
        params[d(match[1])] = d(match[2]);
28
29
        if (d(match[2]) === 'true' || d(match[2]) === 'false') {
30
            params[d(match[1])] = d(match[2]) === 'true' ? true : false;
31
        }
32
        match = r.exec(search.substring(1));
33
    }
34
35
    window.params = params;
36
})();
37
38
// Initialize some variables.
39
var player = null;
40
var startStopBtn = null;
41
var uploadBtn = null;
42
var countdownSeconds = null;
43
var countdownTicker = null;
44
var recType = null;
45
var mediaRecorder = null;
46
var chunks = null;
47
48
// Notify and redirect user if plugin is used from insecure location.
49
M.tinymce_recordrtc.check_secure = function() {
50
    var isSecureOrigin = (window.location.protocol === 'https:') ||
51
                         (window.location.host.indexOf('localhost') !== -1);
52
53
    if (!isSecureOrigin) {
54
        window.alert(M.util.get_string('insecurealert', 'tinymce_recordrtc'));
55
        tinyMCEPopup.close();
56
    }
57
}
58
59
// Display "consider switching browsers" message if not using:
60
// - Firefox 29+;
61
// - Chrome 49+;
62
// - Opera 36+.
63
M.tinymce_recordrtc.check_browser = function() {
64
    if (!((bowser.firefox && bowser.version >= 29) ||
65
          (bowser.chrome && bowser.version >= 49) ||
66
          (bowser.opera && bowser.version >= 36))) {
67
        var alert = document.querySelector('div[id=alert-warning]');
68
        alert.parentElement.parentElement.classList.remove('hide');
69
    }
70
};
71
72
// Capture webcam/microphone stream.
73
M.tinymce_recordrtc.captureUserMedia = function(mediaConstraints, successCallback, errorCallback) {
74
    navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
75
};
76
77
// Add chunks of audio/video to array when made available.
78
M.tinymce_recordrtc.handleDataAvailable = function(event) {
79
    chunks.push(event.data);
80
};
81
82
// Get everything set up to start recording.
83
M.tinymce_recordrtc.startRecording = function(type, stream) {
84
    // The options for the recording codecs and bitrates.
85
    var options = null;
86
    if (type === 'audio') {
87
        if (MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) {
1 ignored issue
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...
88
            options = {
89
                audioBitsPerSecond: params['audiobitrate'],
90
                mimeType: 'audio/webm;codecs=opus'
91
            };
92
        } else if (MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')) {
93
            options = {
94
                audioBitsPerSecond: params['audiobitrate'],
95
                mimeType: 'audio/ogg;codecs=opus'
96
            };
97
        }
98
    } else {
99
        if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9,opus')) {
100
            options = {
101
                audioBitsPerSecond: params['audiobitrate'],
102
                videoBitsPerSecond: params['videobitrate'],
103
                mimeType: 'video/webm;codecs=vp9,opus'
104
            };
105
        } else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264,opus')) {
106
            options = {
107
                audioBitsPerSecond: params['audiobitrate'],
108
                videoBitsPerSecond: params['videobitrate'],
109
                mimeType: 'video/webm;codecs=h264,opus'
110
            };
111
        } else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8,opus')) {
112
            options = {
113
                audioBitsPerSecond: params['audiobitrate'],
114
                videoBitsPerSecond: params['videobitrate'],
115
                mimeType: 'video/webm;codecs=vp8,opus'
116
            };
117
        }
118
    }
119
120
    // If none of the options above are supported, fall back on browser defaults.
121
    mediaRecorder = options ? new MediaRecorder(stream)
122
                            : new MediaRecorder(stream, options);
123
124
    // Initialize MediaRecorder events and start recording.
125
    mediaRecorder.ondataavailable = M.tinymce_recordrtc.handleDataAvailable;
126
    mediaRecorder.start(10); // Capture in 10ms chunks. Must be set to work with Firefox.
127
128
    // Mute audio, distracting while recording.
129
    player.muted = true;
130
131
    // Set recording timer to the time specified in the settings.
132
    countdownSeconds = params['timelimit'];
133
    countdownSeconds++;
134
    startStopBtn.innerHTML = M.util.get_string('stoprecording', 'tinymce_recordrtc') +
135
                             ' (<span id="minutes"></span>:<span id="seconds"></span>)';
136
    M.tinymce_recordrtc.setTime();
137
    countdownTicker = setInterval(M.tinymce_recordrtc.setTime, 1000);
138
139
    // Make button clickable again, to allow stopping recording.
140
    startStopBtn.disabled = false;
141
};
142
143
// Upload recorded audio/video to server.
144
M.tinymce_recordrtc.uploadToServer = function(type, callback) {
145
    var xhr = new XMLHttpRequest();
146
147
    // Get src media of audio/video tag.
148
    xhr.open('GET', player.src, true);
149
    xhr.responseType = 'blob';
150
151
    xhr.onload = function() {
152
        if (xhr.status === 200) { // If src media was successfully retrieved.
153
            // blob is now the media that the audio/video tag's src pointed to.
154
            var blob = this.response;
155
156
            // Generate filename with random ID and file extension.
157
            var fileName = (Math.random() * 1000).toString().replace('.', '');
158
            if (type === 'audio') {
159
              fileName += '-audio.ogg';
160
            } else {
161
              fileName += '-video.webm';
162
            }
163
164
            // Create FormData to send to PHP upload/save script.
165
            var formData = new FormData();
166
            formData.append('contextid', recordrtc.contextid);
167
            formData.append('sesskey', parent.M.cfg.sesskey);
168
            formData.append(type + '-filename', fileName);
169
            formData.append(type + '-blob', blob);
170
171
            // Pass FormData to PHP script using XHR.
172
            M.tinymce_recordrtc.makeXMLHttpRequest('save.php', formData, function(progress, responseText) {
173
                if (progress === 'upload-ended') {
174
                    var initialURL = location.href.replace(location.href.split('/').pop(), '') + 'uploads.php/';
175
                    callback('ended', initialURL + responseText);
176
                } else {
177
                    callback(progress);
178
                }
179
            });
180
        }
181
    };
182
183
    xhr.send();
184
};
185
186
// Handle XHR sending/receiving/status.
187
M.tinymce_recordrtc.makeXMLHttpRequest = function(url, data, callback) {
188
    var xhr = new XMLHttpRequest();
189
190
    xhr.onreadystatechange = function() {
191
        if ((xhr.readyState === 4) && (xhr.status === 200)) { // When request is finished and successful.
192
            callback('upload-ended', xhr.responseText);
193
        } else if (xhr.status === 404) { // When request returns 404 Not Found.
194
            callback('upload-failed');
195
        }
196
    };
197
198
    xhr.upload.onprogress = function(event) {
199
        callback(Math.round(event.loaded / event.total * 100) + "% " +
200
                 M.util.get_string('uploadprogress', 'tinymce_recordrtc'));
201
    };
202
203
    xhr.upload.onerror = function(error) {
204
        callback('upload-failed');
205
        console.error('XMLHttpRequest failed:', error);
206
    };
207
208
    xhr.upload.onabort = function(error) {
209
        callback(M.util.get_string('uploadaborted', 'tinymce_recordrtc'));
210
        console.error('XMLHttpRequest aborted:', error);
211
    };
212
213
    // POST FormData to PHP script that handles uploading/saving.
214
    xhr.open('POST', url);
215
    xhr.send(data);
216
};
217
218
// Makes 1min and 2s display as 1:02 on timer instead of 1:2, for example.
219
M.tinymce_recordrtc.pad = function(val) {
220
    var valString = val + "";
221
222
    if (valString.length < 2) {
223
        return "0" + valString;
224
    } else {
225
        return valString;
226
    }
227
};
228
229
// Functionality to make recording timer count down.
230
// Also makes recording stop when time limit is hit.
231
M.tinymce_recordrtc.setTime = function() {
232
    countdownSeconds--;
233
234
    startStopBtn.querySelector('span#seconds').textContent = M.tinymce_recordrtc.pad(countdownSeconds % 60);
235
    startStopBtn.querySelector('span#minutes').textContent = M.tinymce_recordrtc.pad(parseInt(countdownSeconds / 60));
236
237
    if (countdownSeconds === 0) {
238
        startStopBtn.click();
239
    }
240
};
241
242
// Generates link to recorded annotation to be inserted.
243
M.tinymce_recordrtc.create_annotation = function(type, recording_url) {
244
    var linkText = window.prompt('What should the annotation appear as?', M.util.get_string('annotation:' + type, 'tinymce_recordrtc'));
245
    var annotation = '<div id="recordrtc_annotation" class="text-center"><a target="_blank" href="' + recording_url + '">' + linkText + '</a></div>';
246
247
    return annotation;
248
};
249
250
// Inserts link to annotation in editor text area.
251
M.tinymce_recordrtc.insert_annotation = function(type, recording_url) {
252
    var annotation = M.tinymce_recordrtc.create_annotation(type, recording_url);
253
254
    tinyMCEPopup.editor.execCommand('mceInsertContent', false, annotation);
255
    tinyMCEPopup.close();
256
};
257