docs: function ( range, frag, root ) {
      var node, block, blockContentsAfterSplit, stopPoint, container, offset;
      var firstBlockInFrag, nodeAfterSplit, nodeBeforeSplit, tempRange;
  
      // Fixup content: ensure no top-level inline, and add cursor fix elements.
      fixContainer( frag, root );
      node = frag;
      while ( ( node = getNextBlock( node, root ) ) ) {
        fixCursor( node, root );
      }
  
      // Delete any selected content.
      if ( !range.collapsed ) {
        deleteContentsOfRange( range, root );
      }
  
      // Move range down into text nodes.
      moveRangeBoundariesDownTree( range );
      range.collapse( false ); // collapse to end
  
      // Where will we split up to? First blockquote parent, otherwise root.
      stopPoint = getNearest( range.endContainer, root, 'BLOCKQUOTE' ) || root;
  
      // Merge the contents of the first block in the frag with the focused block.
      // If there are contents in the block after the focus point, collect this
      // up to insert in the last block later
      block = getStartBlockOfRange( range, root );
      firstBlockInFrag = getNextBlock( frag, frag );
      if ( block && firstBlockInFrag &&
           // Don't merge table cells or PRE elements into block
           !getNearest( firstBlockInFrag, frag, 'PRE' ) &&
           !getNearest( firstBlockInFrag, frag, 'TABLE' ) ) {
        moveRangeBoundariesUpTree( range, block, block, root );
        range.collapse( true ); // collapse to start
        container = range.endContainer;
        offset = range.endOffset;
        // Remove trailing <br> – we don't want this considered content to be
        // inserted again later
        cleanupBRs( block, root, false );
        if ( isInline( container ) ) {
          // Split up to block parent.
          nodeAfterSplit = split(
            container, offset, getPreviousBlock( container, root ), root );
          container = nodeAfterSplit.parentNode;
          offset = indexOf.call( container.childNodes, nodeAfterSplit );
        }
        if ( /*isBlock( container ) && */offset !== getLength( container ) ) {
          // Collect any inline contents of the block after the range point
          blockContentsAfterSplit =
            root.ownerDocument.createDocumentFragment();
          while ( ( node = container.childNodes[ offset ] ) ) {
            blockContentsAfterSplit.appendChild( node );
          }
        }
        // And merge the first block in.
        mergeWithBlock( container, firstBlockInFrag, range, root );
  
        // And where we will insert
        offset = indexOf.call( container.parentNode.childNodes, container ) + 1;
        container = container.parentNode;
        range.setEnd( container, offset );
      }
  
      // Is there still any content in the fragment?
      if ( getLength( frag ) ) {
        moveRangeBoundariesUpTree( range, stopPoint, stopPoint, root );
        // Now split after block up to blockquote (if a parent) or root
        nodeAfterSplit = split(
          range.endContainer, range.endOffset, stopPoint, root );
        nodeBeforeSplit = nodeAfterSplit ?
          nodeAfterSplit.previousSibling :
          stopPoint.lastChild;
        stopPoint.insertBefore( frag, nodeAfterSplit );
        if ( nodeAfterSplit ) {
          range.setEndBefore( nodeAfterSplit );
        } else {
          range.setEnd( stopPoint, getLength( stopPoint ) );
        }
        block = getEndBlockOfRange( range, root );
  
        // Get a reference that won't be invalidated if we merge containers.
        moveRangeBoundariesDownTree( range );
        container = range.endContainer;
        offset = range.endOffset;
  
        // Merge inserted containers with edges of split
        if ( nodeAfterSplit && isContainer( nodeAfterSplit ) ) {
          mergeContainers( nodeAfterSplit, root );
        }
        nodeAfterSplit = nodeBeforeSplit && nodeBeforeSplit.nextSibling;
        if ( nodeAfterSplit && isContainer( nodeAfterSplit ) ) {
          mergeContainers( nodeAfterSplit, root );
        }
        range.setEnd( container, offset );
      }
  
      // Insert inline content saved from before.
      if ( blockContentsAfterSplit ) {
        tempRange = range.cloneRange();
        mergeWithBlock( block, blockContentsAfterSplit, tempRange, root );
        range.setEnd( tempRange.endContainer, tempRange.endOffset );
      }
      moveRangeBoundariesDownTree( range );
    }