Recurse into the data and produce DOM node output

@param {string} sub_text  Plain-text upon which ranges act
@param {Array} sub_ranges Position/applicable ranges array
@param {Array} range_data All range data
@param {DocumentFragment} container Destination DOM fragment for output
@param {object} options
function build_chunks( sub_text, sub_ranges, range_data, container, options ) {
	let text_start = null,
		text_stop = null,
		i,
		remove_r_id,
		r_id,
		sr_id,
		range_id,
		range_info,
		new_i,
		new_sub_text,
		new_sub_range;

	// We use sub_ranges and not sub_text because we *can* have an empty string with a range
	// acting upon it. For example an a tag with just an alt-text-less image tag inside of it
	for ( i = 0; i < sub_ranges.length; i++ ) {
		if ( sub_ranges[ i ].index.length == 0 ) {
			// This is a simple text element without applicable ranges
			if ( text_start == null ) {
				// This is the beginning of the text element
				text_start = i;
			}
		} else {
			if ( text_start != null ) {
				text_stop = i;
				// We're in a range now, but, we were just in text,
				// so create the DOM elements for the just-finished text range
				container.appendChild(
					document.createTextNode( sub_text.substring( text_start, text_stop ) )
				);
				text_start = null;
				text_stop = null;
			}

			// At this point we have one or more ranges we could be entering. We need to decide
			// which one. For recursion to work we must pick the longest range. If there are
			// ties for longest range from this position then it doesn't matter which one wins.
			// This may not be true for all cases forever. If we find a bug where a particular
			// range needs to win out over another then this is the place for that logic.
			//
			// sub_ranges[i].index looks like:
			// [ { id: [index of range in range_data], len: [span of range indices] }, { id: x, len: y }, ..., { id: n, len: m } ]
			remove_r_id = null;
			for ( r_id in sub_ranges[ i ].index ) {
				if (
					null === remove_r_id ||
					sub_ranges[ i ].index[ r_id ].len > sub_ranges[ i ].index[ remove_r_id ].len
				) {
					remove_r_id = r_id;
				}
			}

			// Since we've picked a range we'll record some information for future reference
			range_id = sub_ranges[ i ].index[ remove_r_id ].id; // To be able to reference thr origin range
			range_info = range_data[ range_id ]; // the origin range data
			new_i = i + sub_ranges[ i ].index[ remove_r_id ].len - 1; // the position we will be jumping to after resursing
			new_sub_text = sub_text.substring( i, i + sub_ranges[ i ].index[ remove_r_id ].len ); // the text we will be recursing with
			new_sub_range = sub_ranges.slice( i, 1 + i + sub_ranges[ i ].index[ remove_r_id ].len ); // the new ranges we'll be recursing with

			// Remove the range we are recursing into from the ranges we're recursing with.
			// Otherwise we will end up in an infinite loop and everybody will be mad at us.
			for ( sr_id = 0; sr_id < new_sub_range.length; sr_id++ ) {
				new_sub_range[ sr_id ].index.splice( remove_r_id, 1 );
			}

			container.appendChild(
				render_range( new_sub_text, new_sub_range, range_info, range_data, options )
			);
			i = new_i;
		}
	}
	if ( text_start != null ) {
		// We're done, at and below this depth but we finished in a text range, so we need to
		// handle the last bit of text
		container.appendChild(
			document.createTextNode( sub_text.substring( text_start, sub_text.length ) )
		);
	}
	// Just in case we have anything like a bunch of small Text() blocks together, etc, lets
	// normalize the document
	container.normalize();
}