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