fromRange(range, base, ignoreClass) {
    var cfi = {
      range: false,
      base: {},
      path: {},
      start: null,
      end: null
    };
    var start = range.startContainer;
    var end = range.endContainer;
    var startOffset = range.startOffset;
    var endOffset = range.endOffset;
    var needsIgnoring = false;

    if (ignoreClass) {
      // Tell pathTo if / what to ignore
      needsIgnoring = start.ownerDocument.querySelector("." + ignoreClass) != null;
    }

    if (typeof base === "string") {
      cfi.base = this.parseComponent(base);
      cfi.spinePos = cfi.base.steps[1].index;
    } else if (typeof base === "object") {
      cfi.base = base;
    }

    if (range.collapsed) {
      if (needsIgnoring) {
        startOffset = this.patchOffset(start, startOffset, ignoreClass);
      }

      cfi.path = this.pathTo(start, startOffset, ignoreClass);
    } else {
      cfi.range = true;

      if (needsIgnoring) {
        startOffset = this.patchOffset(start, startOffset, ignoreClass);
      }

      cfi.start = this.pathTo(start, startOffset, ignoreClass);

      if (needsIgnoring) {
        endOffset = this.patchOffset(end, endOffset, ignoreClass);
      }

      cfi.end = this.pathTo(end, endOffset, ignoreClass); // Create a new empty path

      cfi.path = {
        steps: [],
        terminal: null
      }; // Push steps that are shared between start and end to the common path

      var len = cfi.start.steps.length;
      var i;

      for (i = 0; i < len; i++) {
        if (this.equalStep(cfi.start.steps[i], cfi.end.steps[i])) {
          if (i === len - 1) {
            // Last step is equal, check terminals
            if (cfi.start.terminal === cfi.end.terminal) {
              // CFI's are equal
              cfi.path.steps.push(cfi.start.steps[i]); // Not a range

              cfi.range = false;
            }
          } else {
            cfi.path.steps.push(cfi.start.steps[i]);
          }
        } else {
          break;
        }
      }

      cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length);
      cfi.end.steps = cfi.end.steps.slice(cfi.path.steps.length); // TODO: Add Sanity check to make sure that the end if greater than the start
    }

    return cfi;
  }
Create a CFI object from a Range
@param {Range} range
@param {string | object} base
@param {string} [ignoreClass]
@returns {object} cfi