ParagraphsToMerge(Location previous, List <List <IDomNode> > selected, OffsetInElement start, OffsetInElement end) { m_previous = previous; m_selected = selected; m_start = start; m_end = end; }
static ParagraphsToMerge create(IDomRange domRange) { //find the block-level element associated with the start of the range IDomNode startBlock = findBlock(domRange.startContainer, domRange.startOffset, true); //find the block-level element associated with the end of the range IDomNode endBlock = findBlock(domRange.endContainer, domRange.endOffset, false); //block may be null if it's positioned directly under a <td> //which (removing things from a table) isn't a scenario supported by this class if ((startBlock == null) || (endBlock == null)) { return(null); } //iterate the inline-level elements to be moved List <List <IDomNode> > selected = new List <List <IDomNode> >(); IDomNode currentBlock = startBlock; for (; ;) { //get the inline elements inside this block List <IDomNode> children = null; bool childIsBlock = false; foreach (IDomNode child in currentBlock.childNodes.elements) { if (!isBlock(child)) { //found an inline child if (children == null) { children = new List <IDomNode>(); } children.Add(child); } else { //child of this block is itself a block currentBlock = child; childIsBlock = true; break; } } if (children != null) { //found some inline children selected.Add(children); } if (childIsBlock) { //descend: iterate this child block continue; } //else finished iterating children of currentBlock if (ReferenceEquals(currentBlock, endBlock)) { //currentBlock is the last block we wanted to iterate break; } //find the next block to iterate for (; ;) { IDomNode currentParent = currentBlock.parentNode; int currentIndex = currentParent.childNodes.indexOf(currentBlock); if (currentIndex < (currentParent.childNodes.count - 1)) { //next we'll iterate the next sibling after currentBlock currentBlock = currentParent.childNodes.itemAt(currentIndex + 1); break; } else { //no next sibling after currentBlock //so iterate next sibling after parent block currentBlock = currentParent; continue; } } if (!isBlock(currentBlock)) { //this can happen if an 'anonymous block' is not the first block of a parent block //which isn't supported by the current version of the renderer throw new ApplicationException("unexpected"); } } //find the block before the selected block //into which we'll merge the inline elements currentBlock = startBlock; Location previous; for (; ;) { //find the block before the first block IDomNode parent = currentBlock.parentNode; int index = parent.childNodes.indexOf(currentBlock); if (index > 0) { //found a previous element IDomNode previousNode = parent.childNodes.itemAt(index - 1); if (isBlock(previousNode)) { //previous element is a block //but it might have block-level descendents while ((previousNode.childNodes.count > 0) && isBlock(previousNode.childNodes.itemAt(previousNode.childNodes.count - 1))) { previousNode = previousNode.childNodes.itemAt(previousNode.childNodes.count - 1); } //want to insert at the end (after the last child) of the previousNode previous = new Location(previousNode, null); break; } else { //previous is not a block, //so it's an anonymous block //near the start of a list item or table cell previous = new Location(parent, previousNode); break; } } if (parent.nodeName == "body") { //startBlock is the first block in the body //there is no previous block //so don't merge return(null); } currentBlock = parent; continue; } //search up to find the <body> element IDomNode body = startBlock; while (body.nodeName != "body") { body = body.parentNode; } //remember whether the selection range starts and ends //in order to restore it after we edit the DOM OffsetInElement start = new OffsetInElement(body, domRange.startContainer, domRange.startOffset); OffsetInElement end = new OffsetInElement(body, domRange.endContainer, domRange.endOffset); return(new ParagraphsToMerge(previous, selected, start, end)); }