docs: function setData(data) {
      let output = [];
  
      // Clone so that we have our own reference
      data = JSON.parse(JSON.stringify(data));
      
      // Calculate the total height that the scrollbar should be based on the root nodes
      // and all of the items that have been expanded so far.
      let totalHeight = 0;
      forEachExpansion(_(this).expansions, (expansion) => {
          totalHeight += expansion.children;
      });
  
      updateViewDimensions.call(this, totalHeight);
  
      // Apply the offsets to the items which is used to keep track of expansions.
      // By ensuring the data has the offset applied to them, we can calculate from
      // here exactly which items are above us and which ones are below us when we 
      // look at the position of the virtual scrollbar.
      for (let i = 0; i < data.length; i++) {
          for (let j = 0; j < data[i].items.length; j++) {
              let item = data[i].items[j];
              item.original = JSON.parse(JSON.stringify(item));
              item.offset = _(this).request[i].offset + j;
              item.parent = data[i].parent;
          }
      }
  
      // The response from the server should be in a heirarchical structure.
      // This means that we can iterate over this array, and the further we are in the array
      // the deeper the level we're in. 
      //
      // Eg. [null, 1, 1, 2, 3, 3]
      data.forEach((response) => {
          let index = output.findIndex((obj) => {
              return obj.id === response.parent;
          });
  
          if (index > -1) {
              // If the parent exists, we insert the items directly below it.
              output = output.slice(0, index + 1)
                          .concat(response.items)
                          .concat(output.slice(index + 1));
          } else {
              // If the parent doesn't exist, we prepend. We prepend because of this scenario:
              //
              //       Item 1.1.1
              //    Item 1.2
              //    Item 1.3
              //    Item 1.4
              // Item 2
              // Item 3
              // 
              // In this scenario, we've expanded Item 1, but because it doesn't exist in the output
              // we can only assume it's directly above us. We've also expanded Item 1.1, and again
              // because it's parent isn't visible, and because we know the data is in a heirarchical 
              // structure, we can prepend it.
              output = response.items.concat(output);
          }
      });
  
  
      if (_(this).items.length === 0 || _(this).diffMode === 'hard') {
          // Wipe out all of the current items since we don't need them anymore.
          // Easier code wise to just re-instantiate everything and doesn't impact performance.
          while (_(this).items.length) {
              _(this).items.pop().el.remove();
          }
  
  
          // Once we've figured out the output, start rendering them
          output.forEach((data) => {
              let item = createItem.call(this, data);
              _(this).view.content.appendChild(item.el);
              _(this).items.push(item);
          });
      } else if (_(this).diffMode === 'soft') {
          // First check what's common, and run updates.
          // We'll also discover what isn't common and therefore track them for removal.
          let removals = [];
          let updated = [];
          _(this).items.forEach((item, index) => {
              let found = false;
              for (let i = 0; i < output.length; i++) {
                  if (output[i].id === item.id) {
                      renderItem.call(this, item.el, output[i], true);
                      updated.push(i);
                      found = true;
                      break;
                  }
              } 
  
              if (!found) {
                  removals.push(index);
              }
          });
  
          // Lock the scroll events and track scroll position.
          // When we're adding and removing items, they can adjust
          // the scroll position. So after we made the updates we
          // need to restore the scroll position.
          let oldScrollTop = _(this).view.scrollbar.scrollTop;
          _(this).scrollLocked = true;
  
          // Remove the items.
          for (let i = removals.length - 1; i >= 0; i--) {
              let index = removals[i];
              _(this).items[index].el.remove();
              _(this).items.splice(index, 1);
          }
  
          // Insert all of the new items.
          for (let i = 0; i < output.length; i++) {
              if (updated.indexOf(i) === -1) {
                  let item = createItem.call(this, output[i]);
                   _(this).view.content.insertBefore(item.el, _(this).view.content.children[i]);
                   _(this).items.splice(i, 0, item);
              }
          }
  
          // TODO: This can cause a weird slow scrolling bug in Chrome when scrolling up.
          // Was triggered by console.log, could be a latency issue.
          _(this).view.scrollbar.scrollTop = oldScrollTop;
          _(this).scrollLocked = false;
      }
  }