Using JavaScript to Change Completion Status for Failed Courses in SCORM 2004

NOTE: Programming changes made through JavaScript and CSS are not supported by the dominKnow support team.  

In SCORM 2004, a Completion Status and a Success Status are both sent.

The default settings for dominKnow | ONE result in the following scenario when there is a test:

  • If you pass the test you get: Completed and Passed 
  • If you fail the test or don't take it and you have test attempts left you get: In Progress and Unknown
  • If you fail the test and have no more attempts left you get: Completed and Failed

In some cases, your organization may want the Completion status to stay at In Progress if the learner has Failed the test and there are no more attempts. 

While dominKnow | ONE's theme designer is quite flexible, you are not currently able to override the default processes for this. However, you can take advantage of the "Add JS" option in the theme builder to make these types of changes. 

Adding JavaScript to a Theme

Steps:

  1. Edit your desired theme.
  2. Select Add JS.
    1. NOTE: If you do not see this option, then it has been disabled by your admin for your user role. There are a variety of permission settings that an Admin (with the right permissions) can turn on and off. They will need to turn this option on to enable you to add JS to a theme.
  3. Paste in the JavaScript.
    1. NOTE: Do NOT include tags in your code. The code will automatically be put into script tags within the HTML.
  4. Select OK.
  5. Select Save.
  6. Test your changes.

Code for changing the Completion Status in SCORM 2004 when the Success Status is Failed

This sample code is changing the third use case. Instead of receiving a Completed and Failed, the LMS will receive an In Progress and Failed. As these types of changes affect the tracking of your content and the dominKnow | ONE course player can be changed, we highly recommend you thoroughly test any overrides such as this before depoying the content to your learners. 

NOTE: dominKnow Support does NOT support troubleshooting JavaScript and CSS changes made to your content.

// keep PASS_FAIL_SETS_COMPLETION_FOR_2004 false globally
// so failed/unknown do not automatically become completed
(function () {
  function patchScormFlag() {
    var patched = false;

    try {
      if (typeof parent.PASS_FAIL_SETS_COMPLETION_FOR_2004 !== "undefined") {
        console.log(
          "[SCORM patch] parent before:",
          parent.PASS_FAIL_SETS_COMPLETION_FOR_2004
        );
        parent.PASS_FAIL_SETS_COMPLETION_FOR_2004 = false;
        console.log(
          "[SCORM patch] parent after:",
          parent.PASS_FAIL_SETS_COMPLETION_FOR_2004
        );
        patched = true;
      }
    } catch (e) {
      console.error("[SCORM patch] Could not patch parent", e);
    }

    try {
      if (typeof top.PASS_FAIL_SETS_COMPLETION_FOR_2004 !== "undefined") {
        console.log(
          "[SCORM patch] top before:",
          top.PASS_FAIL_SETS_COMPLETION_FOR_2004
        );
        top.PASS_FAIL_SETS_COMPLETION_FOR_2004 = false;
        console.log(
          "[SCORM patch] top after:",
          top.PASS_FAIL_SETS_COMPLETION_FOR_2004
        );
        patched = true;
      }
    } catch (e) {
      console.error("[SCORM patch] Could not patch top", e);
    }

    return patched;
  }

  if (!patchScormFlag()) {
    var tries = 0;
    var maxTries = 60;

    var timer = setInterval(function () {
      tries++;
      if (patchScormFlag() || tries >= maxTries) {
        clearInterval(timer);
      }
    }, 250);
  }
})();


// patch SCORM2004_SetPassed only
// passed should also mark completion complete
// failed/unknown should remain incomplete
(function () {
  function patchSetPassedOnly() {
    var patched = false;

    function applyPatch(scormWin, label) {
      try {
        if (
          scormWin &&
          typeof scormWin.SCORM2004_SetPassed === "function" &&
          !scormWin.SCORM2004_SetPassed._patchedSetPassedComplete
        ) {
          scormWin.SCORM2004_SetPassed = function () {
            scormWin.WriteToDebug("In SCORM2004_SetPassed [patched]");
            var blnResult;

            scormWin.SCORM2004_ClearErrorInfo();

            blnResult = scormWin.SCORM2004_CallSetValue(
              "cmi.success_status",
              scormWin.SCORM2004_PASSED
            );

            blnResult =
              scormWin.SCORM2004_CallSetValue(
                "cmi.completion_status",
                scormWin.SCORM2004_COMPLETED
              ) && blnResult;

            return blnResult;
          };

          scormWin.SCORM2004_SetPassed._patchedSetPassedComplete = true;
          console.log("[SCORM patch] " + label + ".SCORM2004_SetPassed patched");
          return true;
        }
      } catch (e) {
        console.error("[SCORM patch] Could not patch " + label + ".SCORM2004_SetPassed", e);
      }

      return false;
    }

    patched = applyPatch(parent, "parent") || patched;
    patched = applyPatch(top, "top") || patched;

    return patched;
  }

  if (!patchSetPassedOnly()) {
    var tries = 0;
    var maxTries = 60;

    var timer = setInterval(function () {
      tries++;
      if (patchSetPassedOnly() || tries >= maxTries) {
        clearInterval(timer);
      }
    }, 250);
  }
})();


// safely wrap onCourseComplete instead of replacing it outright
// suppress SetReachedEnd only when completionStatus = 1 and lessonStatus = failed
(function () {
  function patchOnCourseComplete() {
    try {
      if (
        typeof window.DKI === "undefined" ||
        !window.DKI.SCORMDataStorage ||
        !window.DKI.SCORMDataStorage.prototype ||
        typeof window.DKI.SCORMDataStorage.prototype.onCourseComplete !== "function"
      ) {
        return false;
      }

      var proto = window.DKI.SCORMDataStorage.prototype;

      if (proto.onCourseComplete._patchedInterceptSetReachedEnd) {
        return true;
      }

      var originalOnCourseComplete = proto.onCourseComplete;

      proto.onCourseComplete = function () {
        var self = this;
        var originalProcess = this.scormInterface && this.scormInterface.process;

        if (typeof originalProcess !== "function") {
          return originalOnCourseComplete.apply(this, arguments);
        }

        this.scormInterface.process = function (action, args, callback) {
          var suppressSetReachedEnd =
            action === "SetReachedEnd" &&
            playerBehaviour &&
            playerBehaviour.completionStatus === 1 &&
            self.lessonStatus === "failed";

          if (suppressSetReachedEnd) {
            console.log(
              "[SCORM patch] Suppressed SetReachedEnd for completionStatus=1, lessonStatus=" +
                self.lessonStatus
            );

            if (typeof callback === "function") {
              try {
                callback();
              } catch (e) {}
            }
            return;
          }

          return originalProcess.apply(this, arguments);
        };

        try {
          return originalOnCourseComplete.apply(this, arguments);
        } finally {
          this.scormInterface.process = originalProcess;
        }
      };

      proto.onCourseComplete._patchedInterceptSetReachedEnd = true;
      console.log("[SCORM patch] onCourseComplete wrapped successfully");
      return true;
    } catch (e) {
      console.error("[SCORM patch] Failed to patch onCourseComplete", e);
      return false;
    }
  }

  if (!patchOnCourseComplete()) {
    var tries = 0;
    var maxTries = 120;

    var timer = setInterval(function () {
      tries++;
      if (patchOnCourseComplete() || tries >= maxTries) {
        clearInterval(timer);
      }
    }, 250);
  }
})();
  0     0

Similar Projects

Questions  ( 0 )