/// <summary> /// Inserts foster parented characters. /// </summary> /// /// <param name="text"> /// The text. /// </param> /// <param name="table"> /// The table. /// </param> /// <param name="stackParent"> /// The stack parent. /// </param> override protected void InsertFosterParentedCharacters(string text, DomObject table, DomObject stackParent) { IDomObject parent = table.ParentNode; if (parent != null) { // always an element if not null IDomObject previousSibling = table.PreviousSibling; if (previousSibling != null && previousSibling.NodeType == NodeType.TEXT_NODE) { IDomText lastAsText = (IDomText)previousSibling; lastAsText.NodeValue += text; return; } parent.InsertBefore(Document.CreateTextNode(text), table); return; } // fall through IDomText lastChild = stackParent.LastChild as IDomText; if (lastChild != null) { lastChild.NodeValue += text; return; } else { stackParent.AppendChildUnsafe(Document.CreateTextNode(text)); } }
/// <summary> /// Appends text a node. /// </summary> /// /// <param name="parent"> /// The parent. /// </param> /// <param name="text"> /// The text. /// </param> override protected void AppendCharacters(DomObject parent, string text) { IDomText lastChild = parent.LastChild as IDomText; if (lastChild != null) { lastChild.NodeValue += text; } else { lastChild = Document.CreateTextNode(text); parent.AppendChildUnsafe(lastChild); } }
/// <summary> /// Sets a child text for this element, using the text node type appropriate for this element's type /// </summary> /// /// <param name="el"> /// The element to add text to /// </param> /// <param name="text"> /// The text. /// </param> private void SetChildText(IDomElement el, string text) { if (el.ChildrenAllowed) { el.ChildNodes.Clear(); // Element types that cannot have HTML contents should not have the value encoded. // use DomInnerText node for those node types to preserve the raw text value IDomText textEl = el.InnerHtmlAllowed ? new DomText(text) : new DomInnerText(text); el.ChildNodes.Add(textEl); } }
void editDom(IDomDocument domDocument) { //increment the serialized range location start only once //to account for extra whitespace we add before the first block m_start.increment(); foreach (List <IDomNode> elements in m_selected) { //insert a whitespace, instead of the 'carriage return' which used to separate the blocks IDomText whitespace = domDocument.createTextNode(" "); m_previous.insert(whitespace); //increment the serialized range location end multiple times //to account for extra whitespace we just added m_end.increment(); IDomNode parent = elements[0].parentNode; //insert each element foreach (IDomNode element in elements) { //remove from current parent before inerting into new parent parent.removeChild(element); //insert into new parent m_previous.insert(element); } if (parent.childNodes.count == 0) { //this block is now empty (all its children removed) //so remove it from the document IDomNode grandparent = parent.parentNode; while (grandparent.childNodes.count == 1) { parent = grandparent; grandparent = grandparent.parentNode; } grandparent.removeChild(parent); } } }
/// <summary> /// Adds the contents to 'node' to the StringBuilder. /// </summary> /// /// <param name="sb"> /// The StringBuilder. /// </param> /// <param name="node"> /// The node. /// </param> /// <param name="skipWhitespace"> /// true to skip any leading whitespace for this node. /// </param> protected void AddContents(StringBuilder sb, IDomObject node, bool skipWhitespace) { // always skip the opening whitespace of a new child block if (node.HasChildren) { foreach (IDomObject el in node.ChildNodes) { if (el.NodeType == NodeType.TEXT_NODE) { IDomText txtNode = (IDomText)el; stringInfo.Target = el.NodeValue; if (stringInfo.Whitespace) { if (!skipWhitespace) { sb.Append(" "); skipWhitespace = true; } } else { string val = CleanFragment(el.Render()); if (skipWhitespace) { val = val.TrimStart(); skipWhitespace = false; } sb.Append(val); } } else if (el.NodeType == NodeType.ELEMENT_NODE) { IDomElement elNode = (IDomElement)el; // first add any inner contents if (el.NodeName != "HEAD" && el.NodeName != "STYLE" && el.NodeName != "SCRIPT") { switch (elNode.NodeName) { case "BR": sb.Append(System.Environment.NewLine); break; case "PRE": RemoveTrailingWhitespace(sb); sb.Append(System.Environment.NewLine); sb.Append(ToStandardLineEndings(el.TextContent)); RemoveTrailingWhitespace(sb); sb.Append(System.Environment.NewLine); skipWhitespace = true; break; case "A": sb.Append(el.TextContent + " (" + el["href"] + ")"); break; default: if (elNode.IsBlock && sb.Length > 0) { RemoveTrailingWhitespace(sb); sb.Append(System.Environment.NewLine); } AddContents(sb, el, elNode.IsBlock); RemoveTrailingWhitespace(sb); if (elNode.IsBlock) { sb.Append(System.Environment.NewLine); skipWhitespace = true; } break; } } } } } }
protected void AddContents(StringBuilder sb, IDomObject startEl) { if (startEl.HasChildren) { foreach (IDomObject el in startEl.ChildNodes) { if (el.NodeType == NodeType.TEXT_NODE) { IDomText txtNode = (IDomText)el; stringInfo.Target = el.NodeValue; if (!stringInfo.Whitespace) { string val = txtNode.NodeValue; if (skipWhitespace) { val = CleanFragment(val); } // always add if there's actually content endingBlock = false; skipWhitespace = false; sb.Append(val); } else { // just whitespace if (!skipWhitespace) { // if not an ending block convert all whitespace to a single space, and // act like it was an ending block (preventing further whitespace from being added) sb.Append(" "); skipWhitespace = true; } } } else if (el.NodeType == NodeType.ELEMENT_NODE) { IDomElement elNode = (IDomElement)el; // first add any inner contents if (el.NodeName != "HEAD" && el.NodeName != "STYLE" && el.NodeName != "SCRIPT") { AddContents(sb, el); switch (elNode.NodeName) { case "BR": sb.Append(System.Environment.NewLine); skipWhitespace = true; break; case "PRE": sb.Append(el.Render()); break; case "A": sb.Append(el.Cq().Children().RenderSelection() + " (" + el["href"] + ")"); break; default: if (elNode.IsBlock) { if (!endingBlock) { // erase ending whitespace -- scan backwards until non-whitespace int i = sb.Length - 1; int count = 0; while (i >= 0 && CharacterData.IsType(sb[i], CharacterType.Whitespace)) { i--; count++; } if (i < sb.Length - 1) { sb.Remove(i + 1, count); } endingBlock = true; skipWhitespace = true; sb.Append(System.Environment.NewLine + System.Environment.NewLine); } } break; } } } } } }
void onInsertHyperlink() { //get the document fragment which the user has currently selected using mouse and/or cursor IWindowSelection windowSelection = modelEdit.windowSelection; //verify that there's only one selection if (windowSelection.rangeCount != 1) { //this can happen when the user has selected several cell in a table, //in which case each cell is a separate selection/range MessageBox.Show("Can't insert a hyperlink when more than one range in the document is selected"); return; } using (IDomRange domRange = windowSelection.getRangeAt(0)) { //verify that only one node is selected if (!domRange.startContainer.isSameNode(domRange.endContainer)) { //this can happen for example when the selection spans multiple paragraphs MessageBox.Show("Can't insert a hyperlink when more than one node in the document is selected"); return; } IDomNode container = domRange.startContainer; //already just checked that this is the same as domRange.endContainer //read existing values from the current selection string url; string visibleText; IDomElement existingHyperlink; switch (container.nodeType) { case DomNodeType.Text: //selection is a text fragment visibleText = container.nodeValue.Substring(domRange.startOffset, domRange.endOffset - domRange.startOffset); IDomNode parentNode = container.parentNode; if ((parentNode.nodeType == DomNodeType.Element) && (parentNode.nodeName == "a")) { //parent of this text node is a <a> element existingHyperlink = parentNode as IDomElement; url = existingHyperlink.getAttribute("href"); visibleText = container.nodeValue; if ((existingHyperlink.childNodes.count != 1) || (existingHyperlink.childNodes.itemAt(0).nodeType != DomNodeType.Text)) { //this can happen when an anchor tag wraps more than just a single, simple text node //for example when it contains inline elements like <strong> MessageBox.Show("Can't edit a complex hyperlink"); return; } } else { existingHyperlink = null; url = null; } break; default: //unexpected MessageBox.Show("Can't insert a hyperlink when more than one node in the document is selected"); return; } //display the modal dialog box using (FormInsertHyperlink formInsertHyperlink = new FormInsertHyperlink()) { formInsertHyperlink.url = url; formInsertHyperlink.visibleText = visibleText; DialogResult dialogResult = formInsertHyperlink.ShowDialog(); if (dialogResult != DialogResult.OK) { //user cancelled return; } //get new values from the dialog box //the FormInsertHyperlink.onEditTextChanged method assures that both strings are non-empty url = formInsertHyperlink.url; visibleText = formInsertHyperlink.visibleText; } //need to change href, change text, and possibly delete existing text; //do this within the scope of a single IEditorTransaction instance so //that if the user does 'undo' then it will undo all these operations at once, instead of one at a time using (IEditorTransaction editorTransaction = modelEdit.createEditorTransaction()) { if (existingHyperlink != null) { //changing an existing hyperlink ... //... change the href attribute value existingHyperlink.setAttribute("href", url); //... change the text, by removing the old text node and inserting a new text node IDomText newDomText = modelEdit.domDocument.createTextNode(visibleText); IDomNode oldDomText = existingHyperlink.childNodes.itemAt(0); existingHyperlink.removeChild(oldDomText); existingHyperlink.insertBefore(newDomText, null); } else { //creating a new hyperlink IDomElement newHyperlink = modelEdit.domDocument.createElement("a"); IDomText newDomText = modelEdit.domDocument.createTextNode(visibleText); newHyperlink.insertBefore(newDomText, null); newHyperlink.setAttribute("href", url); //remove whatever was previously selected, if anything if (!domRange.collapsed) { domRange.deleteContents(); } //insert the new hyperlink domRange.insertNode(newHyperlink); } } } }