videomail-client 8.3.1 → 8.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/prototype/js/videomail-client.js +12 -14
  3. package/prototype/js/videomail-client.min.js +1 -1
  4. package/prototype/js/videomail-client.min.js.map +1 -1
  5. package/src/js/client.js +0 -210
  6. package/src/js/constants.js +0 -11
  7. package/src/js/events.js +0 -46
  8. package/src/js/index.js +0 -15
  9. package/src/js/options.js +0 -180
  10. package/src/js/resource.js +0 -206
  11. package/src/js/util/audioRecorder.js +0 -152
  12. package/src/js/util/browser.js +0 -319
  13. package/src/js/util/collectLogger.js +0 -72
  14. package/src/js/util/eventEmitter.js +0 -72
  15. package/src/js/util/humanize.js +0 -16
  16. package/src/js/util/mediaEvents.js +0 -148
  17. package/src/js/util/pretty.js +0 -70
  18. package/src/js/util/standardize.js +0 -71
  19. package/src/js/util/videomailError.js +0 -431
  20. package/src/js/wrappers/buttons.js +0 -670
  21. package/src/js/wrappers/container.js +0 -797
  22. package/src/js/wrappers/dimension.js +0 -149
  23. package/src/js/wrappers/form.js +0 -319
  24. package/src/js/wrappers/optionsWrapper.js +0 -81
  25. package/src/js/wrappers/visuals/inside/recorder/countdown.js +0 -83
  26. package/src/js/wrappers/visuals/inside/recorder/facingMode.js +0 -53
  27. package/src/js/wrappers/visuals/inside/recorder/pausedNote.js +0 -59
  28. package/src/js/wrappers/visuals/inside/recorder/recordNote.js +0 -42
  29. package/src/js/wrappers/visuals/inside/recorder/recordTimer.js +0 -149
  30. package/src/js/wrappers/visuals/inside/recorderInsides.js +0 -144
  31. package/src/js/wrappers/visuals/notifier.js +0 -341
  32. package/src/js/wrappers/visuals/recorder.js +0 -1492
  33. package/src/js/wrappers/visuals/replay.js +0 -355
  34. package/src/js/wrappers/visuals/userMedia.js +0 -541
  35. package/src/js/wrappers/visuals.js +0 -410
  36. package/src/styles/css/main.min.css.js +0 -1
  37. package/src/styles/styl/keyframes/blink.styl +0 -16
  38. package/src/styles/styl/main.styl +0 -126
@@ -1,797 +0,0 @@
1
- import Visibility from "document-visibility";
2
- // needed for IE 11
3
- import elementClosest from "element-closest";
4
- import hidden from "hidden";
5
- import insertCss from "insert-css";
6
- import inherits from "inherits";
7
-
8
- import css from "../../styles/css/main.min.css.js";
9
- import Events from "../events";
10
- import Resource from "../resource";
11
- import EventEmitter from "../util/eventEmitter";
12
- import VideomailError from "../util/videomailError";
13
- import Buttons from "./buttons";
14
- import Dimension from "./dimension";
15
- import Form from "./form";
16
- import OptionsWrapper from "./optionsWrapper";
17
- import Visuals from "./visuals";
18
-
19
- elementClosest(window);
20
-
21
- const Container = function (options) {
22
- EventEmitter.call(this, options, "Container");
23
-
24
- const self = this;
25
-
26
- const visibility = Visibility();
27
- const visuals = new Visuals(this, options);
28
- const buttons = new Buttons(this, options);
29
- const resource = new Resource(options);
30
- const htmlElement =
31
- document && document.querySelector && document.querySelector("html");
32
- const { debug } = options;
33
-
34
- let hasError = false;
35
- let submitted = false;
36
- let lastValidation = false;
37
-
38
- let containerElement;
39
- let built;
40
- let form;
41
-
42
- function prependDefaultCss() {
43
- insertCss(css, { prepend: true });
44
- }
45
-
46
- // since https://github.com/binarykitchen/videomail-client/issues/87
47
- function findParentFormElement() {
48
- return containerElement.closest("form");
49
- }
50
-
51
- function getFormElement() {
52
- let formElement;
53
-
54
- if (containerElement.tagName === "FORM") {
55
- formElement = containerElement;
56
- } else if (options.selectors.formId) {
57
- formElement = document.getElementById(options.selectors.formId);
58
- } else {
59
- formElement = findParentFormElement();
60
- }
61
-
62
- return formElement;
63
- }
64
-
65
- function buildForm() {
66
- const formElement = getFormElement();
67
-
68
- if (formElement) {
69
- debug("Container: buildForm()");
70
- form = new Form(self, formElement, options);
71
-
72
- const submitButton = form.findSubmitButton();
73
- submitButton && buttons.setSubmitButton(submitButton);
74
-
75
- form.build();
76
- }
77
- }
78
-
79
- function buildChildren() {
80
- debug("Container: buildChildren()");
81
-
82
- if (!containerElement.classList) {
83
- self.emit(
84
- Events.ERROR,
85
- VideomailError.create("Sorry, your browser is too old!", options),
86
- );
87
- } else {
88
- containerElement.classList.add("videomail");
89
-
90
- if (!options.playerOnly) {
91
- buttons.build();
92
- }
93
-
94
- visuals.build();
95
- }
96
- }
97
-
98
- function processError(err) {
99
- hasError = true;
100
-
101
- if (err.stack) {
102
- options.logger.error(err.stack);
103
- } else {
104
- options.logger.error(err);
105
- }
106
-
107
- if (options.displayErrors) {
108
- visuals.error(err);
109
- } else {
110
- visuals.reset();
111
- }
112
- }
113
-
114
- function initEvents() {
115
- debug("Container: initEvents()");
116
-
117
- window.addEventListener("beforeunload", (e) => {
118
- self.unload(e);
119
- });
120
-
121
- if (!options.playerOnly) {
122
- visibility.onChange(function (visible) {
123
- // built? see https://github.com/binarykitchen/videomail.io/issues/326
124
- if (built) {
125
- if (visible) {
126
- if (options.isAutoPauseEnabled() && self.isCountingDown()) {
127
- self.resume();
128
- }
129
-
130
- self.emit(Events.VISIBLE);
131
- } else {
132
- if (
133
- options.isAutoPauseEnabled() &&
134
- (self.isCountingDown() || self.isRecording())
135
- ) {
136
- self.pause("document invisible");
137
- }
138
-
139
- self.emit(Events.INVISIBLE);
140
- }
141
- }
142
- });
143
- }
144
-
145
- if (options.enableSpace) {
146
- if (!options.playerOnly) {
147
- window.addEventListener("keypress", function (e) {
148
- const { tagName } = e.target;
149
- const isEditable =
150
- e.target.isContentEditable ||
151
- e.target.contentEditable === "true" ||
152
- e.target.contentEditable === true;
153
-
154
- // beware of rich text editors, hence the isEditable check (wordpress plugin issue)
155
- if (!isEditable && tagName !== "INPUT" && tagName !== "TEXTAREA") {
156
- const code = e.keyCode ? e.keyCode : e.which;
157
-
158
- if (code === 32) {
159
- e.preventDefault();
160
-
161
- if (options.enablePause) {
162
- visuals.pauseOrResume();
163
- } else {
164
- visuals.recordOrStop();
165
- }
166
- }
167
- }
168
- });
169
- }
170
- }
171
-
172
- /*
173
- * better to keep the one and only error listeners
174
- * at one spot, here, because unload() will do a removeAllListeners()
175
- */
176
- self.on(Events.ERROR, function (err) {
177
- processError(err);
178
- unloadChildren(err);
179
-
180
- if (err.removeDimensions && err.removeDimensions()) {
181
- removeDimensions();
182
- }
183
- });
184
-
185
- if (!options.playerOnly) {
186
- self.on(Events.LOADED_META_DATA, function () {
187
- correctDimensions();
188
- });
189
- }
190
- }
191
-
192
- function validateOptions() {
193
- if (options.hasDefinedWidth() && options.video.width % 2 !== 0) {
194
- throw VideomailError.create("Width must be divisible by two.", options);
195
- }
196
-
197
- if (options.hasDefinedHeight() && options.video.height % 2 !== 0) {
198
- throw VideomailError.create("Height must be divisible by two.", options);
199
- }
200
- }
201
-
202
- /*
203
- * this will just set the width but not the height because
204
- * it can be a form with more inputs elements
205
- */
206
- function correctDimensions() {
207
- if (options.video.stretch) {
208
- removeDimensions();
209
- } else {
210
- const width = visuals.getRecorderWidth(true);
211
-
212
- if (width < 1) {
213
- throw VideomailError.create("Recorder width cannot be less than 1!", options);
214
- } else {
215
- containerElement.style.width = `${width}px`;
216
- }
217
- }
218
- }
219
-
220
- function removeDimensions() {
221
- containerElement.style.width = "auto";
222
- }
223
-
224
- function unloadChildren(e) {
225
- visuals.unload(e);
226
- buttons.unload();
227
- self.endWaiting();
228
- }
229
-
230
- function hideMySelf() {
231
- hidden(containerElement, true);
232
- }
233
-
234
- function submitVideomail(formData, method, cb) {
235
- const videomailFormData = form.transformFormData(formData);
236
-
237
- // when method is undefined, treat it as a post
238
- if (isPost(method) || !method) {
239
- videomailFormData.recordingStats = visuals.getRecordingStats();
240
- videomailFormData.width = visuals.getRecorderWidth(true);
241
- videomailFormData.height = visuals.getRecorderHeight(true);
242
-
243
- if (navigator.connection) {
244
- videomailFormData.connection = {
245
- downlink: `${navigator.connection.downlink} Mbit/s`,
246
- effectiveType: navigator.connection.effectiveType,
247
- rtt: navigator.connection.rtt,
248
- type: navigator.connection.type,
249
- };
250
- }
251
-
252
- resource.post(videomailFormData, cb);
253
- } else if (isPut(method)) {
254
- resource.put(videomailFormData, cb);
255
- }
256
- }
257
-
258
- function submitForm(formData, videomailResponse, url, cb) {
259
- /*
260
- * for now, accept POSTs only which have an URL unlike null and
261
- * treat all other submissions as direct submissions
262
- */
263
-
264
- if (!url || url === "") {
265
- // figure out URL automatically then
266
- url = document.baseURI;
267
- }
268
-
269
- // can be missing when no videomail was recorded and is not required
270
- if (videomailResponse) {
271
- formData[options.selectors.aliasInputName] = videomailResponse.videomail.alias;
272
-
273
- /*
274
- * this in case if user wants all videomail metadata to be posted
275
- * altogether with the remaining form
276
- */
277
- if (options.submitWithVideomail) {
278
- formData.videomail = videomailResponse.videomail;
279
- }
280
- }
281
-
282
- resource.form(formData, url, cb);
283
- }
284
-
285
- function finalizeSubmissions(err, method, videomail, response, formResponse) {
286
- self.endWaiting();
287
-
288
- if (err) {
289
- self.emit(Events.ERROR, err);
290
- } else {
291
- submitted = true;
292
-
293
- // merge two json response bodies to fake as if it were only one request
294
- if (response && formResponse && formResponse.body) {
295
- Object.keys(formResponse.body).forEach(function (key) {
296
- response[key] = formResponse.body[key];
297
- });
298
- }
299
-
300
- self.emit(Events.SUBMITTED, videomail, response || formResponse);
301
-
302
- if (formResponse && formResponse.type === "text/html" && formResponse.text) {
303
- // server replied with HTML contents - display these
304
- document.body.innerHTML = formResponse.text;
305
-
306
- /*
307
- * todo: figure out how to fire dom's onload event again
308
- * todo: or how to run all the scripts over again
309
- */
310
- }
311
- }
312
- }
313
-
314
- this.addPlayerDimensions = function (videomail, element) {
315
- try {
316
- videomail.playerHeight = this.calculateHeight(
317
- {
318
- responsive: true,
319
- videoWidth: videomail.width,
320
- ratio: videomail.height / videomail.width,
321
- },
322
- element,
323
- );
324
-
325
- videomail.playerWidth = this.calculateWidth({
326
- responsive: true,
327
- videoHeight: videomail.playerHeight,
328
- ratio: videomail.height / videomail.width,
329
- });
330
-
331
- return videomail;
332
- } catch (exc) {
333
- self.emit(Events.ERROR, exc);
334
- }
335
- };
336
-
337
- this.limitWidth = function (width) {
338
- return Dimension.limitWidth(containerElement, width, options);
339
- };
340
-
341
- this.limitHeight = function (height) {
342
- return Dimension.limitHeight(height, options);
343
- };
344
-
345
- this.calculateWidth = function (fnOptions) {
346
- return Dimension.calculateWidth(OptionsWrapper.merge(options, fnOptions, true));
347
- };
348
-
349
- this.calculateHeight = function (fnOptions, element) {
350
- if (!element) {
351
- if (containerElement) {
352
- element = containerElement;
353
- } else {
354
- // better than nothing
355
- element = document.body;
356
- }
357
- }
358
-
359
- return Dimension.calculateHeight(
360
- element,
361
- OptionsWrapper.merge(options, fnOptions, true),
362
- );
363
- };
364
-
365
- this.areVisualsHidden = function () {
366
- return visuals.isHidden();
367
- };
368
-
369
- this.hasElement = function () {
370
- return Boolean(containerElement);
371
- };
372
-
373
- this.build = function () {
374
- try {
375
- containerElement = document.getElementById(options.selectors.containerId);
376
-
377
- /*
378
- * only build when a container element hast been found, otherwise
379
- * be silent and do nothing
380
- */
381
- if (containerElement) {
382
- options.insertCss && prependDefaultCss();
383
-
384
- !built && initEvents();
385
- validateOptions();
386
- correctDimensions();
387
-
388
- if (!options.playerOnly) {
389
- buildForm();
390
- }
391
-
392
- buildChildren();
393
-
394
- if (!hasError) {
395
- debug("Container: built.");
396
- built = true;
397
- self.emit(Events.BUILT);
398
- } else {
399
- debug("Container: building failed due to an error.");
400
- }
401
- } else {
402
- /*
403
- * commented out since it does too much noise on videomail's view page which is fine
404
- * debug('Container: no container element with ID ' + options.selectors.containerId + ' found. Do nothing.')
405
- */
406
- }
407
- } catch (exc) {
408
- if (visuals.isNotifierBuilt()) {
409
- self.emit(Events.ERROR, exc);
410
- } else {
411
- throw exc;
412
- }
413
- }
414
- };
415
-
416
- this.getSubmitButton = function () {
417
- return buttons.getSubmitButton();
418
- };
419
-
420
- this.querySelector = function (selector) {
421
- return containerElement.querySelector(selector);
422
- };
423
-
424
- this.beginWaiting = function () {
425
- htmlElement.classList && htmlElement.classList.add("wait");
426
- };
427
-
428
- this.endWaiting = function () {
429
- htmlElement.classList && htmlElement.classList.remove("wait");
430
- };
431
-
432
- this.appendChild = function (child) {
433
- containerElement.appendChild(child);
434
- };
435
-
436
- this.insertBefore = function (child, reference) {
437
- containerElement.insertBefore(child, reference);
438
- };
439
-
440
- this.unload = function (e) {
441
- debug("Container: unload()", e);
442
-
443
- try {
444
- unloadChildren(e);
445
- this.removeAllListeners();
446
-
447
- built = submitted = false;
448
- } catch (exc) {
449
- self.emit(Events.ERROR, exc);
450
- }
451
- };
452
-
453
- this.show = function () {
454
- if (containerElement) {
455
- hidden(containerElement, false);
456
-
457
- visuals.show();
458
-
459
- if (!hasError) {
460
- const paused = self.isPaused();
461
-
462
- if (paused) {
463
- buttons.adjustButtonsForPause();
464
- }
465
-
466
- /*
467
- * since https://github.com/binarykitchen/videomail-client/issues/60
468
- * we hide areas to make it easier for the user
469
- */
470
- buttons.show();
471
-
472
- if (self.isReplayShown()) {
473
- self.emit(Events.PREVIEW);
474
- } else {
475
- self.emit(Events.FORM_READY, "paused", paused);
476
- }
477
- }
478
- }
479
- };
480
-
481
- this.hide = function () {
482
- debug("Container: hide()");
483
-
484
- hasError = false;
485
-
486
- this.isRecording() && this.pause();
487
-
488
- visuals.hide();
489
-
490
- if (submitted) {
491
- buttons.hide();
492
- hideMySelf();
493
- }
494
- };
495
-
496
- this.startOver = function (params) {
497
- try {
498
- self.emit(Events.STARTING_OVER);
499
-
500
- submitted = false;
501
- form.show();
502
- visuals.back(params, function () {
503
- if (params && params.keepHidden) {
504
- /*
505
- * just enable form, do nothing else.
506
- * see example contact_form.html when you submit without videomail
507
- * and go back
508
- */
509
- self.enableForm();
510
- } else {
511
- self.show(params);
512
- }
513
- });
514
- } catch (exc) {
515
- self.emit(Events.ERROR, exc);
516
- }
517
- };
518
-
519
- this.showReplayOnly = function () {
520
- hasError = false;
521
-
522
- this.isRecording() && this.pause();
523
-
524
- visuals.showReplayOnly();
525
-
526
- submitted && buttons.hide();
527
- };
528
-
529
- this.isNotifying = function () {
530
- return visuals.isNotifying();
531
- };
532
-
533
- this.isPaused = function () {
534
- return visuals.isPaused();
535
- };
536
-
537
- this.pause = function (params) {
538
- visuals.pause(params);
539
- };
540
-
541
- // this code needs a good rewrite :(
542
- this.validate = function (force) {
543
- let runValidation = true;
544
- let valid;
545
-
546
- if (!options.enableAutoValidation) {
547
- runValidation = false;
548
- lastValidation = true; // needed so that it can be submitted anyway, see submit()
549
- } else if (force) {
550
- runValidation = force;
551
- } else if (self.isNotifying()) {
552
- runValidation = false;
553
- } else if (visuals.isConnected()) {
554
- runValidation = visuals.isUserMediaLoaded() || visuals.isReplayShown();
555
- } else if (visuals.isConnecting()) {
556
- runValidation = false;
557
- }
558
-
559
- if (runValidation) {
560
- this.emit(Events.VALIDATING);
561
-
562
- const visualsValid = visuals.validate() && buttons.isRecordAgainButtonEnabled();
563
- let whyInvalid;
564
-
565
- if (form) {
566
- valid = form.validate();
567
-
568
- if (valid) {
569
- if (!this.areVisualsHidden() && !visualsValid) {
570
- if (
571
- submitted ||
572
- this.isReady() ||
573
- this.isRecording() ||
574
- this.isPaused() ||
575
- this.isCountingDown()
576
- ) {
577
- valid = false;
578
- }
579
-
580
- if (!valid) {
581
- whyInvalid = "Video is not recorded";
582
- }
583
- }
584
- } else {
585
- const invalidInput = form.getInvalidElement();
586
-
587
- if (invalidInput) {
588
- whyInvalid = `Form input named ${
589
- invalidInput.name
590
- } is invalid. It has the value: "${invalidInput.value}"`;
591
- } else {
592
- whyInvalid = "Form input(s) are invalid";
593
- }
594
- }
595
-
596
- if (valid) {
597
- /*
598
- * If CC and/or BCC exist, validate one more time to ensure at least
599
- * one recipient is given
600
- */
601
- const recipients = form.getRecipients();
602
-
603
- const toIsConfigured = "to" in recipients;
604
- const ccIsConfigured = "cc" in recipients;
605
- const bccIsConfigured = "bcc" in recipients;
606
-
607
- const hasTo = recipients.to?.length > 0;
608
- const hasCc = recipients.cc?.length > 0;
609
- const hasBcc = recipients.bcc?.length > 0;
610
-
611
- if (toIsConfigured) {
612
- if (!hasTo) {
613
- if (ccIsConfigured && bccIsConfigured) {
614
- if (!hasCc && !hasBcc) {
615
- valid = false;
616
- }
617
- } else if (ccIsConfigured) {
618
- if (!hasCc) {
619
- valid = false;
620
- }
621
- } else if (bccIsConfigured) {
622
- if (!hasBcc) {
623
- valid = false;
624
- }
625
- } else {
626
- whyInvalid = "Please configure the form to have at least one recipient.";
627
- }
628
- }
629
- } else if (ccIsConfigured) {
630
- if (!hasCc) {
631
- if (bccIsConfigured) {
632
- if (!hasBcc) {
633
- valid = false;
634
- }
635
- }
636
- }
637
- }
638
-
639
- if (!valid) {
640
- whyInvalid = "Please enter at least one recipient.";
641
- }
642
- }
643
- } else {
644
- valid = visualsValid;
645
- }
646
-
647
- if (valid) {
648
- this.emit(Events.VALID);
649
- } else {
650
- this.emit(Events.INVALID, whyInvalid);
651
- }
652
-
653
- lastValidation = valid;
654
- }
655
-
656
- return valid;
657
- };
658
-
659
- this.disableForm = function (buttonsToo) {
660
- form && form.disable(buttonsToo);
661
- };
662
-
663
- this.enableForm = function (buttonsToo) {
664
- form && form.enable(buttonsToo);
665
- };
666
-
667
- this.hasForm = function () {
668
- return Boolean(form);
669
- };
670
-
671
- this.isReady = function () {
672
- return buttons.isRecordButtonEnabled();
673
- };
674
-
675
- function isPost(method) {
676
- return method && method.toUpperCase() === "POST";
677
- }
678
-
679
- function isPut(method) {
680
- return method && method.toUpperCase() === "PUT";
681
- }
682
-
683
- this.submitAll = function (formData, method, url) {
684
- const post = isPost(method);
685
- const hasVideomailKey = Boolean(formData[options.selectors.keyInputName]);
686
-
687
- function startSubmission() {
688
- self.beginWaiting();
689
- self.disableForm(true);
690
- self.emit(Events.SUBMITTING);
691
- }
692
-
693
- // a closure so that we can access method
694
- const submitVideomailCallback = function (err1, videomail, videomailResponse) {
695
- if (err1) {
696
- finalizeSubmissions(err1, method, videomail, videomailResponse);
697
- } else if (post) {
698
- submitForm(formData, videomailResponse, url, function (err2, formResponse) {
699
- finalizeSubmissions(err2, method, videomail, videomailResponse, formResponse);
700
- });
701
- } else {
702
- // it's a direct submission
703
- finalizeSubmissions(null, method, videomail, videomailResponse);
704
- }
705
- };
706
-
707
- /*
708
- * !hasVideomailKey makes it possible to submit form when videomail itself
709
- * is not optional.
710
- */
711
- if (!hasVideomailKey) {
712
- if (options.enableAutoSubmission) {
713
- startSubmission();
714
- submitForm(formData, null, url, function (err2, formResponse) {
715
- finalizeSubmissions(err2, method, null, null, formResponse);
716
- });
717
- }
718
- /*
719
- * ... and when the enableAutoSubmission option is false,
720
- * then that can mean, leave it to the framework to process with the form
721
- * validation/handling/submission itself. for example the ninja form
722
- * will want to highlight which one input are wrong.
723
- */
724
- } else {
725
- startSubmission();
726
- submitVideomail(formData, method, submitVideomailCallback);
727
- }
728
- };
729
-
730
- this.isBuilt = function () {
731
- return built;
732
- };
733
-
734
- this.isReplayShown = function () {
735
- return visuals.isReplayShown();
736
- };
737
-
738
- this.isDirty = function () {
739
- let isDirty = false;
740
-
741
- if (form) {
742
- if (visuals.isRecorderUnloaded()) {
743
- isDirty = false;
744
- } else if (this.isReplayShown() || this.isPaused()) {
745
- isDirty = true;
746
- }
747
- }
748
-
749
- return isDirty;
750
- };
751
-
752
- this.getReplay = function () {
753
- return visuals.getReplay();
754
- };
755
-
756
- this.isOutsideElementOf = function (element) {
757
- return element.parentNode !== containerElement && element !== containerElement;
758
- };
759
-
760
- this.hideForm = function (params) {
761
- // form check needed, see https://github.com/binarykitchen/videomail-client/issues/127
762
- form && form.hide();
763
- buttons && buttons.hide(params);
764
- };
765
-
766
- this.loadForm = function (videomail) {
767
- if (form) {
768
- form.loadVideomail(videomail);
769
- this.validate();
770
- }
771
- };
772
-
773
- this.enableAudio = function () {
774
- options.setAudioEnabled(true);
775
- this.emit(Events.ENABLING_AUDIO);
776
- };
777
-
778
- this.disableAudio = function () {
779
- options.setAudioEnabled(false);
780
- this.emit(Events.DISABLING_AUDIO);
781
- };
782
-
783
- this.submit = function () {
784
- lastValidation && form && form.doTheSubmit();
785
- };
786
-
787
- this.isCountingDown = visuals.isCountingDown.bind(visuals);
788
- this.isRecording = visuals.isRecording.bind(visuals);
789
- this.record = visuals.record.bind(visuals);
790
- this.resume = visuals.resume.bind(visuals);
791
- this.stop = visuals.stop.bind(visuals);
792
- this.recordAgain = visuals.recordAgain.bind(visuals);
793
- };
794
-
795
- inherits(Container, EventEmitter);
796
-
797
- export default Container;