@lookit/record 3.0.1 → 4.1.0

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.
package/README.md CHANGED
@@ -356,9 +356,21 @@ To use the CHS trial recording extension, you need to:
356
356
 
357
357
  ### Parameters
358
358
 
359
- This extension does not accept any parameters.
359
+ **`wait_for_upload_message` [`null` or HTML string | `null` ]**
360
+
361
+ This parameter determines what content should be displayed while the trial
362
+ recording is uploading. If `null` (the default), then the message 'uploading
363
+ video, please wait...' (or appropriate translation based on `locale`) will be
364
+ displayed. Otherwise this parameter can be set to a custom string and can
365
+ contain HTML markup. If you want to embed images/video/audio in this HTML
366
+ string, be sure to preload the media files with the `preload` plugin and
367
+ [manual preloading](https://www.jspsych.org/latest/overview/media-preloading/#manual-preloading).
368
+ Use a blank string (`""`) for no message/content. If a value is provided then
369
+ the `locale` parameter will be ignored.
360
370
 
361
- ### Example
371
+ ### Examples
372
+
373
+ **Basic usage**
362
374
 
363
375
  To record a single trial, you will have to first load the extension in
364
376
  `initJsPsych`.
@@ -375,7 +387,7 @@ record any trial you design.
375
387
 
376
388
  ```javascript
377
389
  const trialRec = {
378
- // ... Other trial paramters ...
390
+ // ... Other trial parameters ...
379
391
  extensions: [{ type: chsRecord.TrialRecordExtension }],
380
392
  };
381
393
  ```
@@ -386,6 +398,60 @@ Finally, insert the trials into the timeline.
386
398
  jsPsych.run([videoConfig, trialRec]);
387
399
  ```
388
400
 
401
+ **Setting parameters**
402
+
403
+ You can set the trial extension parameters when you load the extension with
404
+ `initJsPsych`.
405
+
406
+ In the example below, the default "uploading video, please wait..." message will
407
+ be displayed in French while a trial recording is uploading at the end of the
408
+ trial.
409
+
410
+ ```javascript
411
+ const jsPsych = initJsPsych({
412
+ extensions: [
413
+ {
414
+ type: chsRecord.TrialRecordExtension,
415
+ params: { locale: "fr" },
416
+ },
417
+ ],
418
+ });
419
+ ```
420
+
421
+ And in this example, the custom message "Please wait!" will be displayed while
422
+ the trial recording is uploading. You can include any HTML-formatted content in
423
+ this string, which means you can display images, videos, animations etc.
424
+
425
+ ```javascript
426
+ const jsPsych = initJsPsych({
427
+ extensions: [
428
+ {
429
+ type: chsRecord.TrialRecordExtension,
430
+ params: { wait_for_upload_message: "<div>Please wait!</div>" },
431
+ },
432
+ ],
433
+ });
434
+ ```
435
+
436
+ You can also set the parameters within individual trials. This will override any
437
+ parameters set during the extension initialization (in `initJsPsych`), which can
438
+ be useful if you want to change the parameter values during the experiment. In
439
+ this example, the `wait_for_upload_message` is set to an empty string, which
440
+ will prevent the default "uploading video, please wait..." message from
441
+ appearing after the trial finishes.
442
+
443
+ ```javascript
444
+ const trialRec = {
445
+ // ... Other trial parameters ...
446
+ extensions: [
447
+ {
448
+ type: chsRecord.TrialRecordExtension,
449
+ params: { wait_for_upload_message: "" },
450
+ },
451
+ ],
452
+ };
453
+ ```
454
+
389
455
  ## Session Recording
390
456
 
391
457
  You might prefer to record across multiple trials in a study session. This can
@@ -425,12 +491,37 @@ The plugin to stop session recording is called `chsRecord.StopRecordPlugin`.
425
491
  const stopRec = { type: chsRecord.StopRecordPlugin };
426
492
  ```
427
493
 
494
+ When the trial starts, by default, this plugin will display "uploading video,
495
+ please wait...", or the appropriate translation based on the `locale` parameter.
496
+ This message can be customized using the `wait_for_upload_message` parameter,
497
+ which is the HTML-formatted string to be displayed while the session recording
498
+ is uploading.
499
+
500
+ ```javascript
501
+ const stopRec = {
502
+ type: chsRecord.StopRecordPlugin,
503
+ wait_for_upload_message:
504
+ "<p style='color:red;'>Please wait while we upload your video.</p>",
505
+ };
506
+ ```
507
+
428
508
  #### Parameters
429
509
 
430
- This plugin does not accept any parameters, other those available in all
431
- plugins.
510
+ **`wait_for_upload_message` [`null` or HTML string | `null` ]**
432
511
 
433
- ### Example
512
+ This parameter determines what content should be displayed while the session
513
+ recording is uploading. If `null` (the default), then the message 'uploading
514
+ video, please wait...' (or appropriate translation based on `locale`) will be
515
+ displayed. Otherwise this parameter can be set to a custom string and can
516
+ contain HTML markup. If you want to embed images/video/audio in this HTML
517
+ string, be sure to preload the media files with the `preload` plugin and
518
+ [manual preloading](https://www.jspsych.org/latest/overview/media-preloading/#manual-preloading).
519
+ Use a blank string (`""`) for no message/content. If a value is provided then
520
+ the `locale` parameter will be ignored.
521
+
522
+ ### Examples
523
+
524
+ **Basic usage**
434
525
 
435
526
  First, make sure that you've added a video config trial to your experiment
436
527
  timeline. Then, create your start and stop recording trials:
@@ -476,3 +567,31 @@ jsPsych.run([
476
567
  stopRec,
477
568
  ]);
478
569
  ```
570
+
571
+ **Setting parameters**
572
+
573
+ By default, the stop session recording plugin will display "uploading video,
574
+ please wait..." while the session recording is uploading. You can set the
575
+ `locale` parameter value to translate this message to another language. For
576
+ example, the trial below will display the Brazilian Portuguese translation of
577
+ this message. If the locale string does not match any of the translation codes
578
+ that we support, then the message will be displayed in English.
579
+
580
+ ```javascript
581
+ const stopRec = {
582
+ type: chsRecord.StopRecordPlugin,
583
+ locale: "pt-br",
584
+ };
585
+ ```
586
+
587
+ You can also set custom content to be displayed while the session recording file
588
+ is uploading. The value must be a string. It can include HTML-formatted content,
589
+ which means that you can embed audio, video, images etc. (be sure to preload any
590
+ media files!).
591
+
592
+ ```javascript
593
+ const stopRec = {
594
+ type: chsRecord.StopRecordPlugin,
595
+ wait_for_upload_message: "<p style='color:red;'>Hang on a sec!</p>",
596
+ };
597
+ ```
@@ -32,7 +32,7 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
32
32
 
33
33
  var _package = {
34
34
  name: "@lookit/record",
35
- version: "3.0.1",
35
+ version: "4.1.0",
36
36
  description: "Recording extensions and plugins for CHS studies.",
37
37
  homepage: "https://github.com/lookit/lookit-jspsych#readme",
38
38
  bugs: {
@@ -74,7 +74,7 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
74
74
  },
75
75
  peerDependencies: {
76
76
  "@lookit/data": "^0.2.0",
77
- "@lookit/templates": "^2.0.0",
77
+ "@lookit/templates": "^2.1.0",
78
78
  jspsych: "^8.0.3"
79
79
  }
80
80
  };
@@ -167,21 +167,10 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
167
167
  this.name = "CreateURLError";
168
168
  }
169
169
  }
170
- class VideoContainerNotFoundError extends Error {
171
- constructor() {
172
- super("Video Container could not be found.");
173
- this.name = "VideoContainerError";
174
- }
175
- }
176
- class ButtonNotFoundError extends Error {
177
- constructor(id) {
178
- super(`"${id}" button not found.`);
179
- this.name = "ButtonNotFoundError";
180
- }
181
- }
182
- class ImageNotFoundError extends Error {
183
- constructor(id) {
184
- super(`"${id}" image not found.`);
170
+ class ElementNotFoundError extends Error {
171
+ constructor(id, tag) {
172
+ super(`"${id}" ${tag} not found.`);
173
+ this.name = "ElementNotFoundError";
185
174
  }
186
175
  }
187
176
 
@@ -8580,9 +8569,9 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8580
8569
  this.recorder.stop();
8581
8570
  this.stream.getTracks().map((t) => t.stop());
8582
8571
  }
8583
- stop() {
8572
+ stop(maintain_container_size = false) {
8573
+ this.clearWebcamFeed(maintain_container_size);
8584
8574
  this.stopTracks();
8585
- this.clearWebcamFeed();
8586
8575
  if (!this.stopPromise) {
8587
8576
  throw new NoStopPromiseError();
8588
8577
  }
@@ -8610,6 +8599,7 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8610
8599
  } else {
8611
8600
  await this.s3.completeUpload();
8612
8601
  }
8602
+ this.reset();
8613
8603
  resolve();
8614
8604
  };
8615
8605
  }
@@ -8627,11 +8617,20 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8627
8617
  link.click();
8628
8618
  }
8629
8619
  }
8630
- clearWebcamFeed() {
8620
+ clearWebcamFeed(maintain_container_size) {
8631
8621
  const webcam_feed_element = document.querySelector(
8632
8622
  `#${this.webcam_element_id}`
8633
8623
  );
8634
8624
  if (webcam_feed_element) {
8625
+ if (maintain_container_size) {
8626
+ const parent_div = webcam_feed_element.parentElement;
8627
+ if (parent_div) {
8628
+ const width = webcam_feed_element.offsetWidth;
8629
+ const height = webcam_feed_element.offsetHeight;
8630
+ parent_div.style.height = `${height}px`;
8631
+ parent_div.style.width = `${width}px`;
8632
+ }
8633
+ }
8635
8634
  webcam_feed_element.remove();
8636
8635
  }
8637
8636
  }
@@ -8695,6 +8694,11 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8695
8694
  prompt_only_adults: { type: jspsych.ParameterType.BOOL, default: false },
8696
8695
  consent_statement_text: { type: jspsych.ParameterType.STRING, default: "" },
8697
8696
  omit_injury_phrase: { type: jspsych.ParameterType.BOOL, default: false }
8697
+ },
8698
+ data: {
8699
+ chs_type: {
8700
+ type: jspsych.ParameterType.STRING
8701
+ }
8698
8702
  }
8699
8703
  };
8700
8704
  class VideoConsentPlugin {
@@ -8706,6 +8710,11 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8706
8710
  static info = info$3;
8707
8711
  recorder;
8708
8712
  video_container_id = "lookit-jspsych-video-container";
8713
+ msg_container_id = "lookit-jspsych-video-msg-container";
8714
+ uploadingMsg = null;
8715
+ startingMsg = null;
8716
+ recordingMsg = null;
8717
+ notRecordingMsg = null;
8709
8718
  trial(display, trial) {
8710
8719
  const consentVideo = chsTemplates.consentVideo(trial);
8711
8720
  display.insertAdjacentHTML("afterbegin", consentVideo);
@@ -8714,13 +8723,25 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8714
8723
  this.stopButton(display);
8715
8724
  this.playButton(display);
8716
8725
  this.nextButton(display);
8726
+ this.uploadingMsg = chsTemplates.translateString(
8727
+ "exp-lookit-video-consent.Stopping-and-uploading"
8728
+ );
8729
+ this.startingMsg = chsTemplates.translateString(
8730
+ "exp-lookit-video-consent.Starting-recorder"
8731
+ );
8732
+ this.recordingMsg = chsTemplates.translateString(
8733
+ "exp-lookit-video-consent.Recording"
8734
+ );
8735
+ this.notRecordingMsg = chsTemplates.translateString(
8736
+ "exp-lookit-video-consent.Not-recording"
8737
+ );
8717
8738
  }
8718
8739
  getVideoContainer(display) {
8719
8740
  const videoContainer = display.querySelector(
8720
8741
  `div#${this.video_container_id}`
8721
8742
  );
8722
8743
  if (!videoContainer) {
8723
- throw new VideoContainerNotFoundError();
8744
+ throw new ElementNotFoundError(this.video_container_id, "div");
8724
8745
  }
8725
8746
  return videoContainer;
8726
8747
  }
@@ -8731,14 +8752,31 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8731
8752
  }
8732
8753
  playbackFeed(display) {
8733
8754
  const videoContainer = this.getVideoContainer(display);
8734
- this.recorder.insertPlaybackFeed(videoContainer, this.onEnded(display));
8755
+ this.recorder.insertPlaybackFeed(
8756
+ videoContainer,
8757
+ this.onPlaybackEnded(display)
8758
+ );
8759
+ }
8760
+ getMessageContainer(display) {
8761
+ const msgContainer = display.querySelector(
8762
+ `div#${this.msg_container_id}`
8763
+ );
8764
+ if (!msgContainer) {
8765
+ throw new ElementNotFoundError(this.msg_container_id, "div");
8766
+ }
8767
+ return msgContainer;
8768
+ }
8769
+ addMessage(display, message) {
8770
+ const msgContainer = this.getMessageContainer(display);
8771
+ msgContainer.innerHTML = message;
8735
8772
  }
8736
- onEnded(display) {
8773
+ onPlaybackEnded(display) {
8737
8774
  return () => {
8738
8775
  const next = this.getButton(display, "next");
8739
8776
  const play = this.getButton(display, "play");
8740
8777
  const record = this.getButton(display, "record");
8741
8778
  this.recordFeed(display);
8779
+ this.addMessage(display, this.notRecordingMsg);
8742
8780
  next.disabled = false;
8743
8781
  play.disabled = false;
8744
8782
  record.disabled = false;
@@ -8747,14 +8785,14 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8747
8785
  getButton(display, id) {
8748
8786
  const btn = display.querySelector(`button#${id}`);
8749
8787
  if (!btn) {
8750
- throw new ButtonNotFoundError(id);
8788
+ throw new ElementNotFoundError(id, "button");
8751
8789
  }
8752
8790
  return btn;
8753
8791
  }
8754
8792
  getImg(display, id) {
8755
8793
  const img = display.querySelector(`img#${id}`);
8756
8794
  if (!img) {
8757
- throw new ImageNotFoundError(id);
8795
+ throw new ElementNotFoundError(id, "img");
8758
8796
  }
8759
8797
  return img;
8760
8798
  }
@@ -8764,12 +8802,14 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8764
8802
  const play = this.getButton(display, "play");
8765
8803
  const next = this.getButton(display, "next");
8766
8804
  record.addEventListener("click", async () => {
8805
+ this.addMessage(display, this.startingMsg);
8767
8806
  record.disabled = true;
8768
- stop.disabled = false;
8769
8807
  play.disabled = true;
8770
8808
  next.disabled = true;
8771
- this.getImg(display, "record-icon").style.visibility = "visible";
8772
8809
  await this.recorder.start(true, VideoConsentPlugin.info.name);
8810
+ this.getImg(display, "record-icon").style.visibility = "visible";
8811
+ this.addMessage(display, this.recordingMsg);
8812
+ stop.disabled = false;
8773
8813
  });
8774
8814
  }
8775
8815
  playButton(display) {
@@ -8787,11 +8827,13 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8787
8827
  const play = this.getButton(display, "play");
8788
8828
  stop.addEventListener("click", async () => {
8789
8829
  stop.disabled = true;
8790
- record.disabled = false;
8791
- play.disabled = false;
8792
- await this.recorder.stop();
8793
- this.recorder.reset();
8830
+ this.addMessage(display, this.uploadingMsg);
8831
+ await this.recorder.stop(true);
8794
8832
  this.recordFeed(display);
8833
+ this.getImg(display, "record-icon").style.visibility = "hidden";
8834
+ this.addMessage(display, this.notRecordingMsg);
8835
+ play.disabled = false;
8836
+ record.disabled = false;
8795
8837
  });
8796
8838
  }
8797
8839
  nextButton(display) {
@@ -8809,7 +8851,12 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8809
8851
  }
8810
8852
  }
8811
8853
 
8812
- const info$2 = { name: "start-record-plugin", parameters: {} };
8854
+ const info$2 = {
8855
+ name: "start-record-plugin",
8856
+ version: _package.version,
8857
+ parameters: {},
8858
+ data: {}
8859
+ };
8813
8860
  class StartRecordPlugin {
8814
8861
  constructor(jsPsych) {
8815
8862
  this.jsPsych = jsPsych;
@@ -8831,9 +8878,18 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8831
8878
 
8832
8879
  const info$1 = {
8833
8880
  name: "stop-record-plugin",
8881
+ version: _package.version,
8834
8882
  parameters: {
8835
- locale: { type: jspsych.ParameterType.STRING, default: "en-us" }
8836
- }
8883
+ wait_for_upload_message: {
8884
+ type: jspsych.ParameterType.HTML_STRING,
8885
+ default: null
8886
+ },
8887
+ locale: {
8888
+ type: jspsych.ParameterType.STRING,
8889
+ default: "en-us"
8890
+ }
8891
+ },
8892
+ data: {}
8837
8893
  };
8838
8894
  class StopRecordPlugin {
8839
8895
  constructor(jsPsych) {
@@ -8847,11 +8903,17 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8847
8903
  static info = info$1;
8848
8904
  recorder;
8849
8905
  trial(display_element, trial) {
8850
- display_element.innerHTML = chsTemplates.uploadingVideo(trial);
8906
+ if (trial.wait_for_upload_message == null) {
8907
+ display_element.innerHTML = chsTemplates.uploadingVideo(trial);
8908
+ } else {
8909
+ display_element.innerHTML = trial.wait_for_upload_message;
8910
+ }
8851
8911
  this.recorder.stop().then(() => {
8852
8912
  window.chs.sessionRecorder = null;
8853
8913
  display_element.innerHTML = "";
8854
8914
  this.jsPsych.finishTrial();
8915
+ }).catch((err) => {
8916
+ console.error("StopRecordPlugin: recorder stop/upload failed.", err);
8855
8917
  });
8856
8918
  }
8857
8919
  }
@@ -8862,21 +8924,54 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
8862
8924
  autoBind(this);
8863
8925
  }
8864
8926
  static info = {
8865
- name: "chs-trial-record-extension"
8927
+ name: "chs-trial-record-extension",
8928
+ version: _package.version,
8929
+ data: {}
8866
8930
  };
8867
8931
  recorder;
8868
8932
  pluginName;
8869
- async initialize() {
8933
+ uploadMsg = null;
8934
+ locale = "en-us";
8935
+ async initialize(params) {
8936
+ await new Promise((resolve) => {
8937
+ this.uploadMsg = params?.wait_for_upload_message ? params.wait_for_upload_message : null;
8938
+ this.locale = params?.locale ? params.locale : "en-us";
8939
+ console.log(this.uploadMsg);
8940
+ console.log(this.locale);
8941
+ resolve();
8942
+ });
8870
8943
  }
8871
- on_start() {
8944
+ on_start(startParams) {
8945
+ if (startParams?.wait_for_upload_message) {
8946
+ this.uploadMsg = startParams.wait_for_upload_message;
8947
+ }
8948
+ if (startParams?.locale) {
8949
+ this.locale = startParams.locale;
8950
+ }
8951
+ console.log(this.uploadMsg);
8952
+ console.log(this.locale);
8872
8953
  this.recorder = new Recorder(this.jsPsych);
8873
8954
  }
8874
8955
  on_load() {
8875
8956
  this.pluginName = this.getCurrentPluginName();
8876
8957
  this.recorder?.start(false, `${this.pluginName}`);
8877
8958
  }
8878
- on_finish() {
8879
- this.recorder?.stop();
8959
+ async on_finish() {
8960
+ const displayEl = this.jsPsych.getDisplayElement();
8961
+ if (this.uploadMsg == null) {
8962
+ displayEl.innerHTML = chsTemplates.uploadingVideo({
8963
+ type: this.jsPsych.getCurrentTrial().type,
8964
+ locale: this.locale
8965
+ });
8966
+ } else {
8967
+ displayEl.innerHTML = this.uploadMsg;
8968
+ }
8969
+ try {
8970
+ await this.recorder?.stop();
8971
+ displayEl.innerHTML = "";
8972
+ } catch (err) {
8973
+ console.error("TrialRecordExtension: recorder stop/upload failed.", err);
8974
+ }
8880
8975
  return {};
8881
8976
  }
8882
8977
  getCurrentPluginName() {
@@ -9288,4 +9383,4 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
9288
9383
  return index;
9289
9384
 
9290
9385
  })(chsData, chsTemplates, jsPsychModule);
9291
- //# sourceMappingURL=https://unpkg.com/@lookit/record@3.0.1/dist/index.browser.js.map
9386
+ //# sourceMappingURL=https://unpkg.com/@lookit/record@4.1.0/dist/index.browser.js.map