{"version":3,"file":"poodll_mediarecorder.min.js","sources":["../src/poodll_mediarecorder.js"],"sourcesContent":["/* jshint ignore:start */\ndefine(['jquery', 'core/log', 'filter_poodll/utils_amd',\n    'filter_poodll/adapter', 'filter_poodll/uploader', 'filter_poodll/hermes', 'filter_poodll/timer',\n    'filter_poodll/audioanalyser',\n    'filter_poodll/msr_poodll',\n    'filter_poodll/dlg_errordisplay',\n    'filter_poodll/dlg_download',\n    'filter_poodll/speech_poodll',\n    'filter_poodll/poodll_mediaskins'], function ($, log, utils, adapter, uploader, hermes, timer, audioanalyser,\n                                                  poodll_msr, errordialog, downloaddialog, speechrecognition, mediaskins) {\n\n    \"use strict\"; // jshint ;_;\n\n    log.debug('PoodLL Media Recorder: initialising');\n\n    return {\n\n        instanceprops: [],\n        skins: [],\n        laststream: [],\n\n        fetch_instanceprops: function (controlbarid) {\n            return this.instanceprops[controlbarid];\n        },\n\n        fetch_skin: function (controlbarid) {\n            return this.skins[controlbarid];\n        },\n\n        is_ios: function () {\n            return utils.is_ios();\n        },\n\n        // This recorder supports the current browser\n        supports_current_browser: function (config) {\n\n            var protocol_ok = M.cfg.wwwroot.indexOf('https:') == 0 ||\n                M.cfg.wwwroot.indexOf('http://localhost') == 0;\n            if(!protocol_ok){return false;}\n\n            if (config.mediatype != 'audio' && config.mediatype != 'video') {\n                return false;\n            }\n\n            var ret = utils.can_html5_record(config.mediatype);\n            if (ret) {\n                log.debug('PoodLL Media Recorder: supports this browser');\n            }\n            return ret;\n        },\n\n        // Perform the embed of this recorder on the page\n        // into the element passed in. with config\n        embed: function (element, config) {\n            var that = this;\n\n            var controlbarid = \"filter_poodll_controlbar_\" + config.widgetid;\n            this.init_instance_props(controlbarid);\n            var ip = this.fetch_instanceprops(controlbarid);\n            ip.config = config;\n            ip.controlbarid = controlbarid;\n            if (config.hideupload) {\n                ip.showupload = false;\n            } else {\n                ip.showupload = true;\n            }\n            ip.timeinterval = config.media_timeinterval;\n            ip.audiomimetype = config.media_audiomimetype;\n            ip.videorecordertype = config.media_videorecordertype;\n            ip.videocaptureheight = config.media_videocaptureheight;\n            ip.errordialog = errordialog.clone();\n            ip.errordialog.init(ip);\n            ip.downloaddialog = downloaddialog.clone();\n            ip.downloaddialog.init(this, ip);\n\n            //init the hermes\n            //putting it in config allows us to post messages from uploader and skin as required\n            ip.config.hermes = hermes.clone();\n            ip.config.hermes.init(config.id, config.allowedURL, config.iframeembed);\n\n            // init our skin\n            var theskin = this.init_skin(controlbarid, ip.config.media_skin, ip);\n\n            //Speech recognition\n            if (ip.config.speechevents && ip.speechrec.will_work_ok(ip.config)) {\n                if (!ip.config.language) {\n                    ip.config.language = 'en-US';\n                }\n                ip.speechrec.init(ip.config);\n                ip.speechrec.onfinalspeechcapture = function (speechtext,speechresults) {\n                    var messageObject = {};\n                    messageObject.type = \"speech\";\n                    messageObject.capturedspeech = speechtext;\n                    messageObject.speechresults = speechresults;\n                    ip.config.hermes.postMessage(messageObject);\n                    //send message to our skin\n                    if (theskin.hasOwnProperty('onfinalspeechcapture')) {\n                        theskin.onfinalspeechcapture(speechtext,speechresults);\n                    }\n                };\n            }else{\n                //just turn off speech events to make it easier to check later\n                log.debug('turning off speech events. not req. or not supported.');\n                ip.config.speechevents = false;\n            }\n\n            // add callbacks for uploadsuccess and upload failure\n            ip.config.onuploadsuccess = function (widgetid) {\n                that.onUploadSuccess(widgetid, theskin);\n            };\n            ip.config.onuploadfailure = function (widgetid) {\n                that.onUploadFailure(widgetid, theskin);\n            };\n\n            switch (config.mediatype) {\n                case 'audio':\n                    var preview = theskin.fetch_preview_audio(config.media_skin);\n                    var resource = theskin.fetch_resource_audio(config.media_skin);\n\n\n                    ip.controlbar = this.fetch_controlbar_audio(element, controlbarid, preview, resource);\n                    ip.uploader = uploader.clone();\n\n                    //init uploader skin and uploader\n                    //uploader skin(upskin) if set to false here will default to naff green bar\n                    //should be called after controlbar is created, because thats when canvas is created\n                    var upskin = theskin.fetch_uploader_skin(ip.controlbarid, element);\n                    ip.uploader.init(element, config, upskin);\n\n                    this.register_events_audio(controlbarid);\n\n                    //if this is the uploader skin, then we do not bother to get mediaDevices\n                    if (ip.config.media_skin == 'upload' || ip.config.media_skin == 'warning') {\n                        break;\n                    }\n\n                    // force permissions;\n                    navigator.mediaDevices.getUserMedia({\"audio\": true}).then(function (stream) {\n                        //do nothing\n                        log.debug('successfully forced permissions and got user media');\n\n                    }).catch(function (err) {\n                        log.debug('location 9998');\n                        log.debug(err);\n                        ip.errordialog.open(err);\n                    });\n\n\n                    break;\n                case 'video':\n                    var preview = theskin.fetch_preview_video(config.media_skin);\n                    var resource = theskin.fetch_resource_video(config.media_skin);\n                    ip.controlbar = this.fetch_controlbar_video(element, controlbarid, preview, resource);\n                    ip.uploader = uploader.clone();\n                    //init uploader skin and uploader\n                    //uploader skin(upskin) if set to false here will default to naff green bar\n                    //should be called after controlbar is created, because thats when canvas is created\n                    var upskin = theskin.fetch_uploader_skin(ip.controlbarid, element);\n                    ip.uploader.init(element, config, upskin);\n\n                    this.register_events_video(controlbarid);\n\n                    //if this is any of the uploader/warning/screen skins, then we do not bother to get mediaDevices\n                    if (ip.config.media_skin == 'upload' || ip.config.media_skin == 'warning' || ip.config.media_skin == 'screen') {\n                        break;\n                    }\n\n                    //force permissions and show in preview\n                    navigator.mediaDevices.getUserMedia({\"audio\": true, \"video\": true}).then(function (stream) {\n                        //stop any playing tracks of the current stream\n                        that.restream_preview_video_player(controlbarid, stream)\n\n                    }).catch(function (err) {\n                        log.debug('location 9999');\n                        log.debug(err);\n                    });\n                    break;\n\n            }\n\n\n            // init timer\n            ip.timer = timer.clone();\n            ip.timer.init(ip.config.timelimit, function () {\n                    theskin.handle_timer_update(controlbarid);\n                    // ip.controlbar.status.html(ip.timer.fetch_display_time());\n                }\n            );\n            theskin.handle_timer_update(controlbarid);\n\n            //in the case of an API embed, the caller might want a handle on the skin\n            return theskin;\n        },\n\n\n        init_instance_props: function (controlbarid) {\n            this.instanceprops[controlbarid] = {};\n            this.instanceprops[controlbarid].recorded_index = 0;\n            this.instanceprops[controlbarid].mediaRecorder = null;\n            this.instanceprops[controlbarid].blobs = [];\n            this.instanceprops[controlbarid].timeinterval = 1000;\n            this.instanceprops[controlbarid].audiomimetype = 'audio/webm';\n            this.instanceprops[controlbarid].videorecordertype = 'auto';// mediarec or webp\n            this.instanceprops[controlbarid].videocapturewidth = 320;\n            this.instanceprops[controlbarid].videocaptureheight = 240;\n            this.instanceprops[controlbarid].controlbar = '';\n            this.instanceprops[controlbarid].previewvolume = 1;\n            this.instanceprops[controlbarid].timer = {};\n            this.instanceprops[controlbarid].timer = {};\n            this.instanceprops[controlbarid].showupload = true;\n            this.instanceprops[controlbarid].uploader = {};\n            this.instanceprops[controlbarid].uploaded = false;\n\n            // we create the audio context object here because so its created in the init and passed around\n            // video context is associated with a player so it seems to be ok.\n            this.instanceprops[controlbarid].useraudiodeviceid = false;\n            this.instanceprops[controlbarid].uservideodeviceid = false;\n            this.instanceprops[controlbarid].devices = [];\n\n            //we only want one context per recorder, but beyond 6 we hit Chromes limit, so we reuse the first we stashed in\n            //window\n            var AudioContext = window.AudioContext // Default\n                || window.webkitAudioContext // Safari and old versions of Chrome\n                || false;\n            if (typeof window.poodllmediarecorder_actx === 'undefined') {\n                var ac = new AudioContext();\n                window.poodllmediarecorder_actx = ac;\n                window.poodllmediarecorder_actx_cnt = 1;\n            } else if (window.poodllmediarecorder_actx_cnt == 6) {\n                var ac = window.poodllmediarecorder_actx;\n                log.debug('More than 6 contexts, reusing first one. visualizations might go weird');\n            } else {\n                var ac = new AudioContext();\n                window.poodllmediarecorder_actx_cnt += 1;\n            }\n\n            this.instanceprops[controlbarid].audioctx = ac;\n\n            var aa = audioanalyser.clone();\n            aa.init(ac);\n            this.instanceprops[controlbarid].audioanalyser = aa;\n            this.instanceprops[controlbarid].previewstillcold = true;\n\n            //speech recognition\n            this.instanceprops[controlbarid].speechrec = speechrecognition.clone();\n\n        },\n\n        init_skin: function (controlbarid, skinname, instanceprops) {\n            this.skins[controlbarid] = mediaskins.fetch_skin_clone(skinname);\n            this.skins[controlbarid].init(instanceprops, this);\n            return this.skins[controlbarid];\n        },\n\n        onUploadSuccess: function (widgetid, theskin) {\n            log.debug('from poodllmediarecorder: uploadsuccess');\n            var controlbarid = 'filter_poodll_controlbar_' + widgetid;\n            theskin.onUploadSuccess(controlbarid);\n        },\n\n        onUploadFailure: function (widgetid, theskin) {\n            log.debug('from poodllmediarecorder: uploadfailure');\n            var controlbarid = 'filter_poodll_controlbar_' + widgetid;\n            theskin.onUploadFailure(controlbarid);\n            //if it failed we want to push the user to download this file\n            theskin.fetch_instanceprops().downloaddialog.open(theskin.pmr, theskin.instanceprops);\n        },\n\n\n        onMediaError: function (e, ip) {\n            if(ip.hasOwnProperty('errordialog')) {\n                ip.errordialog.open(e);\n            }\n            log.error('media error', e);\n        },\n\n        captureUserMedia: function (mediaConstraints, successCallback, errorCallback) {\n            navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);\n\n        },\n\n\n        warmup_context: function (ip) {\n            var ctx = ip.audioctx;\n            //for chrome oct 2018\n            if (ctx.state == 'suspended') {\n                ctx.resume();\n            }\n\n            var buffer = ctx.createBuffer(1, 1, 22050);\n            var source = ctx.createBufferSource();\n            source.buffer = buffer;\n            source.connect(ctx.destination);\n            source.start(0);\n        },\n        warmup_preview: function (ip) {\n            var preview = ip.controlbar.preview;\n            if (ip.previewstillcold && preview && preview.get(0)) {\n                var pPromise = ip.controlbar.preview[0].play();\n                // the promise thing here is just to suppress console warnings\n                if (pPromise !== undefined) {\n                    pPromise.then(function () {\n                        // playback started we do not need to do anything\n                    }).catch(function (error) {\n                        log.debug(error);\n                    });\n                }\n                ip.previewstillcold = false;\n            }\n\n        },\n        do_start_audio: function (ip, onMediaSuccess) {\n\n            var that = this;\n            // we warm up the context object\n            this.warmup_context(ip);\n\n            // warmup. the preview object\n            this.warmup_preview(ip);\n\n            //mute the preview\n            ip.controlbar.preview[0].muted=true;\n\n            ip.blobs = [];\n            switch (ip.config.mediatype) {\n                case 'audio':\n                    var mediaConstraints = this.fetch_audio_constraints(ip);\n                    break;\n                case 'video':\n                    var mediaConstraints = this.fetch_video_constraints(ip);\n            }\n\n            //We always tidy up old streams before calling getUserMedia\n            //this.tidy_old_stream(ip.controlbarid);\n            this.captureUserMedia(mediaConstraints, onMediaSuccess, function (e) {\n                that.onMediaError(e, ip);\n            });\n\n        },\n\n\n        do_start_screen: function (ip, onMediaSuccess) {\n\n            var that = this;\n            // we warm up the context object\n            this.warmup_context(ip);\n\n            // warmup. the preview object\n            this.warmup_preview(ip);\n\n            //mute the preview\n            ip.controlbar.preview[0].muted=true;\n\n            ip.blobs = [];\n            //get media constraints\n            var mediaConstraints = {\n                audio: {'echoCancellation': true},\n                video: {cursor: \"motion\"}\n            };\n\n            //set aspect ratio and I think the \"exact\" below should be \"ideal\"\n            //  mediaConstraints.video = {aspectRatio: 1920/1080};\n\n\n            //do all our stream stuff\n            navigator.mediaDevices.getDisplayMedia(mediaConstraints)\n                .then(function(displayStream){\n                    // check for a user audio selected device\n                    if (ip.useraudiodeviceid) {\n                        var audiodeviceid = ip.useraudiodeviceid.valueOf();\n                        mediaConstraints.audio.deviceId = audiodeviceid ? {exact: audiodeviceid} : undefined;\n                    }\n\n                    navigator.mediaDevices.getUserMedia({ audio: mediaConstraints.audio, video: false }).then(\n                        function(voiceStream) {\n                            var tracks = displayStream.getTracks().concat(voiceStream.getAudioTracks());\n                            var stream = new MediaStream(tracks);\n                            onMediaSuccess(stream);\n                        }\n                    )\n                })\n                .catch(function (e) {\n                    that.onMediaError(e, ip);\n            });\n        },\n\n        do_start_video: function (ip, onMediaSuccess) {\n\n        },\n\n        do_stopplay_audio: function (ip, preview) {\n            preview.pause();\n            preview.muted=false;\n\n            switch (ip.config.mediatype) {\n                case 'audio':\n                    //we already did everything we need to\n                    break;\n                case 'video':\n                    //Safari can not reuse the preview player, so we created a 'review' and now dispose of it\n                    if(ip.controlbar.hasOwnProperty('livepreview')){\n                        ip.controlbar.preview.hide();\n                        ip.controlbar.preview = ip.controlbar.livepreview;\n                        ip.controlbar.preview.show();\n                    }\n            }\n        },\n\n        do_play_audio: function (ip, preview_unused) {\n\n            if (ip.blobs && ip.blobs.length > 0) {\n                log.debug('playing type:' + ip.blobs[0].type);\n                log.debug(ip.blobs);\n                utils.doConcatenateBlobs(ip.blobs, function (concatenatedBlob) {\n\n                    log.debug(concatenatedBlob);\n                    var mediaurl = URL.createObjectURL(concatenatedBlob);\n\n                    //Safari can not reuse the preview player [sigh]\n                    //audio is only used for review, so for video only we create a second video element\n                    switch (ip.config.mediatype) {\n                        case 'audio':\n                            var preview = ip.controlbar.preview[0];\n                            break;\n                        case 'video':\n                            ip.controlbar.livepreview = ip.controlbar.preview;\n                            ip.controlbar.preview = ip.controlbar.preview.clone().insertAfter(ip.controlbar.preview);\n                            var preview = ip.controlbar.preview[0];\n                            ip.controlbar.livepreview.hide();\n\n                    }\n                    preview.src = mediaurl;\n                    preview.controls = false;\n                    preview.volume = ip.previewvolume;\n                    preview.muted=false;\n\n                    // Click the stop button if playback ends;\n                    $(preview).bind('ended', function () {\n                        ip.controlbar.stopbutton.click();\n                    });\n\n                    var ppromise = preview.play();\n                    if (ppromise !== undefined) {\n                        ppromise.then(function () {\n                            // playback started we do not need to do anything\n                        }).catch(function (error) {\n                            log.debug('location: do_play_audio');\n                            log.debug(error);\n                        });\n                    }\n\n                });\n\n\n            }// end of if blobs\n        },\n        do_play_video: function (ip) {\n\n        },\n        do_save_audio: function (ip) {\n            // We do want to allow multiple submissions off one page load BUT\n            // this will require a new filename. The filename is the basis of the\n            // s3filename, s3uploadurl and filename for moodle. The problem with\n            // allowing multiple uploads is that once the placeholder is overwritten\n            // the subsequent submissions ad_hoc move task can no longer find the file to\n            // replace. So we need a whole new filename or to cancel the previous ad hoc move.\n            // This should probably be\n            // an ajax request from the uploader, or even a set of 10 filenames/s3uploadurls\n            // pulled down at PHP time ..\n            // this is one of those cases where a simple thing is hard ...J 20160919\n            if (ip.blobs && ip.blobs.length > 0) {\n                utils.doConcatenateBlobs(ip.blobs, function (concatenatedBlob) {\n                    ip.uploader.uploadBlob(concatenatedBlob, ip.blobs[0].type);\n                });\n                ip.uploaded = true;\n                ip.controlbar.startbutton.attr('disabled', true);\n            }// end of if self.blobs\n        },\n        do_save_video: function (ip) {\n\n        },\n        do_stop_audio: function (ip) {\n            //if its paused we need to resume it before stopping.\n            ip.mediaRecorder.resume();\n            ip.mediaRecorder.stop();\n\n            //stop Speech to text if doing that\n            if (ip.config.speechevents) {\n                ip.speechrec.stop();\n            }\n\n            //publish recording stopped event\n            var messageObject = {};\n            messageObject.type = \"recording\";\n            messageObject.action = 'stopped';\n            ip.config.hermes.postMessage(messageObject);\n        },\n        do_stop_video: function (ip) {\n            //just use do_stop_audio\n        },\n        do_stop_screen: function (ip) {\n         //just use do_stop_audio\n        },\n        do_pause_audio: function (ip) {\n            //if its paused we need to resume it before pausing again.\n            //should never happen ...right?\n            ip.mediaRecorder.resume();\n            ip.mediaRecorder.pause();\n        },\n        do_pause_video: function (ip) {\n\n        },\n        do_resume_audio: function (ip) {\n            ip.mediaRecorder.resume();\n        },\n        do_resume_video: function (ip) {\n\n        },\n\n        /* fetch the video constraints for passing to mediastream */\n        fetch_video_constraints: function (ip) {\n            var mediaConstraints = {\n                audio: !utils.is_opera() && !utils.is_edge(),\n                video: {\n                    width: {ideal: 640},\n                    height: {ideal: 480}\n                }\n            };\n\n            //set aspect ratio and I think the \"exact\" below should be \"ideal\"\n            //  mediaConstraints.video = {aspectRatio: 1920/1080};\n            //alert('set');\n\n            // check for a user video selected device\n            if (ip.uservideodeviceid) {\n                var videodeviceid = ip.uservideodeviceid.valueOf();\n                var videoconstraints = {deviceId: videodeviceid ? {exact: videodeviceid} : undefined};\n                videoconstraints.width={ideal: 640};\n                videoconstraints.height={ideal: 480};\n                videoconstraints.frameRate = { ideal: 30, max: 60 };\n                mediaConstraints.video = videoconstraints;\n            }\n            // check for a user audio selected device\n            if (ip.useraudiodeviceid) {\n                var audiodeviceid = ip.useraudiodeviceid.valueOf();\n                var audioconstraints = {deviceId: audiodeviceid ? {exact: audiodeviceid} : undefined};\n                mediaConstraints.audio = audioconstraints;\n            }\n            return mediaConstraints;\n        },\n\n        /* fetch the screen constraints for passing to mediastream */\n        fetch_screen_constraints: function (ip) {\n            var mediaConstraints = {\n                audio: !utils.is_opera() && !utils.is_edge(),\n                video: true\n            };\n\n            //set aspect ratio\n            // we try to get it right here, so we do not have to rely on re-orienting the video later\n            mediaConstraints.video = {aspectRatio: window.innerWidth > window.innerHeight ? 16/9 : 9/16 };\n\n            // check for a user video selected device\n            if (ip.uservideodeviceid) {\n                var videodeviceid = ip.uservideodeviceid.valueOf();\n                var constraints = {deviceId: videodeviceid ? {exact: videodeviceid} : undefined};\n\n                mediaConstraints.video = constraints;\n            }\n            // check for a user audio selected device\n            if (ip.useraudiodeviceid) {\n                var audiodeviceid = ip.useraudiodeviceid.valueOf();\n                var constraints = {deviceId: audiodeviceid ? {exact: audiodeviceid} : undefined};\n                mediaConstraints.audio = constraints;\n            }\n            return mediaConstraints;\n        },\n\n        /* fetch the audio constraints for passing to mediastream */\n        fetch_audio_constraints: function (ip) {\n\n            // really we need to deal with preferences properly\n            //  var sc = navigator.mediaDevices.getSupportedConstraints();\n            //  log.debug(sc);\n            var audioconstraints = {};\n            var customised = false;\n\n\n\n            // tried hard on safari to get the chosen devce, this but just gave up.\n            if (utils.is_safari() && !ip.useraudiodeviceid) {\n                ip.audiomimetype = 'audio/wav';\n            }// end of if Safari\n\n            // check for a user selected device\n            if (ip.useraudiodeviceid) {\n                audioconstraints.deviceID = ip.useraudiodeviceid;\n                customised=true;\n            }\n\n            //if shadowing we want to turn off echo cancellation and noise suppression\n            if(ip.config.shadowing){\n                audioconstraints.echoCancellation = false;\n                audioconstraints.noiseSuppression = false;\n                customised=true;\n            }\n\n            //if we have not customised the audio constraints, then just set to true\n            if(!customised){\n                audioconstraints = true;\n            }\n\n            // init return object\n            var mediaConstraints = {\n                audio: audioconstraints\n            };\n            log.debug('mediaConstraints');\n            log.debug(mediaConstraints);\n            return mediaConstraints;\n        },\n\n        /* register audio events, including those of skin*/\n        register_events_audio: function (controlbarid) {\n\n            var self = this;\n            var ip = this.fetch_instanceprops(controlbarid);\n            var skin = this.skins[controlbarid];\n\n            var onMediaSuccess = function (stream) {\n\n                //There is an occasion where the start recording event fires twice\n                //We hope to stop it here.. 2023-02-26\n                if(ip.mediaRecorder !== null && ip.mediaRecorder.state==='started'){\n                    return;\n                }else{\n                    log.debug(\"register_events_audio - onmediasuccess\");\n                }\n\n\n                //stop any playing tracks of the current stream\n                //DONT call this. caused problems\n                //self.tidy_old_stream(controlbarid);\n\n                //save a reference to the stream\n                self.laststream[controlbarid] = stream;\n\n                //set encoder\n                var encoder = 'auto';\n                if (ip.config.hasOwnProperty('encoder')) {\n                    encoder = ip.config.encoder;\n                }\n\n                // get blob after specific time interval\n                ip.mediaRecorder = poodll_msr;\n                ip.mediaRecorder.init(stream, ip.audioctx, ip.audioanalyser, ip.config.mediatype, encoder);\n                //the following are really for stereoaudio  msr\n                ip.mediaRecorder.mimeType = ip.audiomimetype;\n                ip.mediaRecorder.audioChannels = 1;\n\n                // we pass in the context object because it needs to be activated right on the event.\n                // so its created in the init and passed around\n                ip.mediaRecorder.start(ip.timeinterval, ip.audioctx);\n                ip.mediaRecorder.ondataavailable = function (blob) {\n                    //log.debug(blob);\n                    ip.blobs.push(blob);\n                };\n\n\n\n                //We want to publish a \"started\" recording event.\n                //----------------------------------\n                //but non-iframe (Classic) and in-iframe (Cloud) events work a bit different\n                //this is for non-iframe (Classic)\n                if (!ip.config.iframeembed) {\n                    if (ip.config.callbackjs && ip.config.callbackjs != '') {\n\n                        //publish recording started event to non-iframe\n                        //For callbackjs and for postmessage we need an array of stuff\n                        var callbackObject = new Array();\n                        callbackObject[0] = ip.config.widgetid;\n                        callbackObject[1] = \"started\";\n                        callbackObject[2] = ip.config.filename;\n                        callbackObject[3] = ip.config.updatecontrol;\n                        callbackObject[4] = ip.config.s3filename;\n                        callbackObject[4] = ip.config.s3filename;\n\n                        if (typeof(ip.config.callbackjs) === 'function') {\n                            ip.config.callbackjs(callbackObject);\n                        } else {\n                            uploader.executeFunctionByName(ip.config.callbackjs, window, callbackObject);\n                        }\n                    }\n                }\n\n                //this is for in-iframe (Cloud)\n                var messageObject = {};\n                messageObject.type = \"recording\";\n                messageObject.action = 'started';\n                ip.config.hermes.postMessage(messageObject);\n                //----------------------------------\n\n                //start  speech to text\n                if (ip.config.speechevents) {\n                    ip.speechrec.start(stream);\n                }\n\n                //defer to the skins code\n                skin.onMediaSuccess_audio(controlbarid);\n\n            };\n\n            skin.register_controlbar_events_audio(onMediaSuccess, controlbarid);\n\n        }, // end of register audio events\n\n        /* fetch the video events */\n        register_events_video: function (controlbarid) {\n\n            var self = this;\n            var ip = this.fetch_instanceprops(controlbarid);\n            var skin = this.skins[controlbarid];\n\n            var onMediaSuccess = function (stream) {\n\n                //restream preview video_player\n                self.restream_preview_video_player(controlbarid, stream);\n\n                //set encoder\n                var encoder = 'auto';\n                if (ip.config.hasOwnProperty('encoder')) {\n                    encoder = ip.config.encoder;\n                }\n\n                //choose and turn on the recorder\n                ip.mediaRecorder = poodll_msr;\n                ip.mediaRecorder.init(stream, ip.audioctx, ip.audioanalyser, ip.config.mediatype, encoder);\n\n\n                // set recorder type\n                if (ip.videorecordertype === 'mediarec') {\n                    ip.mediaRecorder.recorderType = MediaRecorderWrapper;\n                }\n\n                // set capture size\n                ip.mediaRecorder.videoWidth = ip.videocapturewidth;\n                ip.mediaRecorder.videoHeight = ip.videocaptureheight;\n\n                // start recording\n                ip.mediaRecorder.start(ip.timeinterval);\n                ip.mediaRecorder.ondataavailable = function (blob) {\n                    ip.blobs.push(blob);\n                    // log.debug('We got a blobby');\n                    // log.debug(URL.createObjectURL(blob));\n                };\n\n                //We want to publish a \"started\" recording event.\n                //----------------------------------\n                //but non-iframe (Classic) and in-iframe (Cloud) events work a bit different\n                //this is for non-iframe (Classic)\n                if (!ip.config.iframeembed) {\n                    if (ip.config.callbackjs && ip.config.callbackjs != '') {\n\n                        //publish recording started event to non-iframe\n                        //For callbackjs and for postmessage we need an array of stuff\n                        var callbackObject = new Array();\n                        callbackObject[0] = ip.config.widgetid;\n                        callbackObject[1] = \"started\";\n                        callbackObject[2] = ip.config.filename;\n                        callbackObject[3] = ip.config.updatecontrol;\n                        callbackObject[4] = ip.config.s3filename;\n\n                        if (typeof(ip.config.callbackjs) === 'function') {\n                            ip.config.callbackjs(callbackObject);\n                        } else {\n                            uploader.executeFunctionByName(ip.config.callbackjs, window, callbackObject);\n                        }\n                    }\n                }\n\n                //this is for in-iframe (Cloud)\n                var messageObject = {};\n                messageObject.type = \"recording\";\n                messageObject.action = 'started';\n                ip.config.hermes.postMessage(messageObject);\n\n                //start speech to text\n                if (ip.config.speechevents) {\n                    ip.speechrec.start(stream);\n                }\n\n                //defer to the skins code\n                skin.onMediaSuccess_video(controlbarid);\n\n            };\n\n            skin.register_controlbar_events_video(onMediaSuccess, controlbarid);\n        }, // end of register video events\n\n        //clear up the old stream\n        tidy_old_stream: function (controlbarid) {\n\n            //stop any playing tracks of the current stream\n            if (this.laststream[controlbarid]) {\n                this.laststream[controlbarid].getTracks().forEach(\n                    function (track) {\n                        track.stop();\n                    });\n            }\n        },\n\n        restream_preview_video_player: function (controlbarid, stream) {\n\n            //store new stream\n            this.laststream[controlbarid] = stream;\n            //play in preview\n            this.init_video_preview(controlbarid);\n\n            //do we need to do this? ..\n            //lets just do it for android and see how it works out it causes a flicker and few second delays\n            if (utils.is_android()) {\n                navigator.mediaDevices.enumerateDevices();\n            }\n\n        },\n\n        //play the stream in the preview\n        init_video_preview: function (controlbarid) {\n            var ip = this.fetch_instanceprops(controlbarid);\n            var preview = ip.controlbar.preview[0];\n\n            preview.srcObject = this.laststream[controlbarid];\n            preview.controls = false;\n            preview.volume = 0;\n            var ppromise = preview.play();\n            if (ppromise !== undefined) {\n                ppromise.then(function () {\n                    // playback started we do not need to do anything\n                }).catch(function (error) {\n                    log.debug('location: init_video_preview');\n                    log.debug(error);\n                });\n            }\n        },\n\n\n        update_status: function (controlbarid) {\n            var ip = this.fetch_instanceprops(controlbarid);\n            ip.controlbar.status.html(ip.timer.fetch_display_time());\n        },\n\n\n        fetch_controlbar_audio: function (element, controlbarid, preview, resource) {\n            var ip = this.fetch_instanceprops(controlbarid);\n            var skin = this.fetch_skin(controlbarid);\n            var controlbar = skin.insert_controlbar_audio(element, controlbarid, preview, resource);\n            return controlbar;\n        },\n\n        fetch_controlbar_video: function (element, controlbarid, preview, resource) {\n            var ip = this.fetch_instanceprops(controlbarid);\n            var skin = this.fetch_skin(controlbarid);\n            var controlbar = skin.insert_controlbar_video(element, controlbarid, preview, resource);\n            return controlbar;\n        },\n\n        fetch_strings: function () {\n            var ss = [];\n            var keys = ['record', 'play', 'pause', 'continue', 'stop', 'save', 'restart', 'testmic',\n                 'upload', 'recordagain', 'readytorecord', 'downloadfile','takesnapshot','cancelsnapshot','pushtospeak',\n                 'settings','audiodevice','videodevice','soundtest','soundtesting','resume'];\n            $.each(keys, function (index, key) {\n                ss['recui_' + key] = M.util.get_string('recui_' + key, 'filter_poodll');\n                //log.debug(key + ':' + ss['recui_' + key]);\n                if (ss['recui_' + key].indexOf(',filter_poodll]]') > 1 || ss['recui_' + key] == '') {\n                    ss['recui_' + key] = key;\n                }\n            });\n            return ss;\n        }\n    };// end of returned object\n});// total end\n"],"names":["define","$","log","utils","adapter","uploader","hermes","timer","audioanalyser","poodll_msr","errordialog","downloaddialog","speechrecognition","mediaskins","debug","instanceprops","skins","laststream","fetch_instanceprops","controlbarid","this","fetch_skin","is_ios","supports_current_browser","config","M","cfg","wwwroot","indexOf","mediatype","ret","can_html5_record","embed","element","that","widgetid","init_instance_props","ip","hideupload","showupload","timeinterval","media_timeinterval","audiomimetype","media_audiomimetype","videorecordertype","media_videorecordertype","videocaptureheight","media_videocaptureheight","clone","init","id","allowedURL","iframeembed","theskin","init_skin","media_skin","speechevents","speechrec","will_work_ok","language","onfinalspeechcapture","speechtext","speechresults","messageObject","capturedspeech","postMessage","hasOwnProperty","onuploadsuccess","onUploadSuccess","onuploadfailure","onUploadFailure","preview","fetch_preview_audio","resource","fetch_resource_audio","controlbar","fetch_controlbar_audio","upskin","fetch_uploader_skin","register_events_audio","navigator","mediaDevices","getUserMedia","then","stream","catch","err","open","fetch_preview_video","fetch_resource_video","fetch_controlbar_video","register_events_video","restream_preview_video_player","timelimit","handle_timer_update","recorded_index","mediaRecorder","blobs","videocapturewidth","previewvolume","uploaded","useraudiodeviceid","uservideodeviceid","devices","AudioContext","window","webkitAudioContext","poodllmediarecorder_actx","ac","poodllmediarecorder_actx_cnt","audioctx","aa","previewstillcold","skinname","fetch_skin_clone","pmr","onMediaError","e","error","captureUserMedia","mediaConstraints","successCallback","errorCallback","warmup_context","ctx","state","resume","buffer","createBuffer","source","createBufferSource","connect","destination","start","warmup_preview","get","pPromise","play","undefined","do_start_audio","onMediaSuccess","muted","fetch_audio_constraints","fetch_video_constraints","do_start_screen","audio","video","cursor","getDisplayMedia","displayStream","audiodeviceid","valueOf","deviceId","exact","voiceStream","tracks","getTracks","concat","getAudioTracks","MediaStream","do_start_video","do_stopplay_audio","pause","hide","livepreview","show","do_play_audio","preview_unused","length","type","doConcatenateBlobs","concatenatedBlob","mediaurl","URL","createObjectURL","insertAfter","src","controls","volume","bind","stopbutton","click","ppromise","do_play_video","do_save_audio","uploadBlob","startbutton","attr","do_save_video","do_stop_audio","stop","do_stop_video","do_stop_screen","do_pause_audio","do_pause_video","do_resume_audio","do_resume_video","is_opera","is_edge","width","ideal","height","videodeviceid","videoconstraints","max","audioconstraints","fetch_screen_constraints","aspectRatio","innerWidth","innerHeight","constraints","customised","is_safari","deviceID","shadowing","echoCancellation","noiseSuppression","self","skin","register_controlbar_events_audio","encoder","mimeType","audioChannels","ondataavailable","blob","push","callbackjs","callbackObject","Array","filename","updatecontrol","s3filename","executeFunctionByName","onMediaSuccess_audio","register_controlbar_events_video","recorderType","MediaRecorderWrapper","videoWidth","videoHeight","onMediaSuccess_video","tidy_old_stream","forEach","track","init_video_preview","is_android","enumerateDevices","srcObject","update_status","status","html","fetch_display_time","insert_controlbar_audio","insert_controlbar_video","fetch_strings","ss","each","index","key","util","get_string"],"mappings":"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"}