docs: function main(options) {
      function markInterval(list, first, last, step, value) {
          if (step > 0) {
              for (var i = first; i <= last; i += step) {
                  if (i >= 0 && i < list.length) {
                      list[i] = value;
                  }
              }
          }
      }
  
      /*
       * Parse an expression and mark the corresponding frames with the given value.
       *
       * expr ::= interval ("," interval)*
       *
       * interval ::=
       *      INT                     // frame number
       *    | INT? ":" INT?           // first:last
       *    | INT? ":" INT? ":" INT?  // first:second:last
       *
       * If first is omitted, it is set to 1.
       * If second is omitted, it is set to first + 1.
       * If last is omitted, it is set to list.length.
       */
      function markFrames(list, expr, value) {
          switch (expr) {
              case "all":
                  markInterval(list, 0, list.length - 1, 1, value);
                  break;
              case "none":
                  break;
              default:
                  expr.split(",").forEach(function (intervalDef) {
                      var interval = intervalDef.split(":").map(function (s) { return s.trim(); });
                      if (interval.length > 0) {
                          var first = interval[0] !== "" ? parseInt(interval[0]) - 1 : 0;
                          var last = interval[interval.length - 1] !== "" ? parseInt(interval[interval.length - 1]) - 1 : list.length - 1;
                          var second = interval.length > 2 && interval[1] !== "" ? parseInt(interval[1]) - 1 : first + 1;
                          if (!isNaN(first) && !isNaN(second) && !isNaN(last)) {
                              markInterval(list, first, last, second - first, value);
                          }
                      }
                  });
          }
      }
  
      function zeroPadded(value, digits) {
          var result = value.toString();
          while(result.length < digits) {
              result = "0" + result;
          }
          return result;
      }
  
      var frameCount = sozi.presentation.frames.length;
  
      var frameSelection = new Array(frameCount);
      markInterval(frameSelection, 0, frameCount - 1, 1, false);
      markFrames(frameSelection, options.include, true);
      markFrames(frameSelection, options.exclude, false);
  
      var digits = frameCount.toString().length;
  
      sozi.player.addListener("frameChange", function () {
          var frameIndex = sozi.player.currentFrameIndex; // Sozi < 18
          if (typeof frameIndex !== "number") {
              frameIndex = sozi.player.currentFrame.index; // Sozi 18
          }
          window.callPhantom(zeroPadded((frameIndex + 1).toString(), digits));
      });
  
      document.querySelector(".sozi-blank-screen").style.display = "none";
      
      frameSelection.forEach(function (frameIsSelected, frameIndex) {
          if (frameIsSelected) {
              console.log("Exporting frame: " + (frameIndex + 1));
              sozi.player.jumpToFrame(frameIndex);
          }
      });
  }