/// <summary> /// WinLive 107762: In IE8, the cursor can get stuck if a user continues to tap an arrow key in a single /// direction. To work around the issue, we wrap the <br>s in a single <div>. /// </summary> protected void FixUpStickyBrs(MarkupRange range) { IHTMLElementFilter brFilter = ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(_ELEMENT_TAG_ID.TAGID_BR)); foreach (IHTMLElement br in range.GetElements(brFilter, true)) { MarkupRange brRange = MarkupServices.CreateMarkupRange(br, true); // We want to be moving from left to right to get the correct _MARKUP_CONTEXT_TYPE. brRange.Start.Left(true); MarkupContext beforeBr = brRange.Start.Right(true); MarkupContext afterBr = brRange.End.Right(false); if (beforeBr.Element is IHTMLDivElement && afterBr.Element is IHTMLDivElement) { // There are three cases that IE8 screws up: // 1. <div><br><div>...</div></div> // 2. <div>...</div><br><div>...</div> // 3. <div><div>...</div><br></div> // The solution is to make sure the <br> is directly wrapped in a single <div>: // ...<div><br></div>... if (!(beforeBr.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && afterBr.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)) { HtmlStyleHelper.WrapRangeInElement(MarkupServices, brRange, _ELEMENT_TAG_ID.TAGID_DIV); } } } }
/// <summary> /// WinLive 209110: When IE renders tables in quirks mode, the table inherits its font color from the body /// element. We want the table to inherit its font color normally, so we force it to. /// </summary> protected void ForceTablesToInheritFontColor(MarkupRange range) { IHTMLElementFilter tableFilter = ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(_ELEMENT_TAG_ID.TAGID_TABLE)); foreach (IHTMLElement table in range.GetElements(tableFilter, true)) { if (table.style.color == null) { IHTMLElement2 tableParent = (IHTMLElement2)table.parentElement; if (tableParent != null) { // Setting the color to "inherit" doesn't work. We have to manually specify a color. table.style.color = tableParent.currentStyle.color; } } } }
/// <summary> /// Makes sure that whole (not parts of) lists are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeLists(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElementFilter listFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.LIST_ELEMENTS, ElementFilters.LIST_ITEM_ELEMENTS); IHTMLElement[] listElements = range.GetElements(listFilter, false); foreach (IHTMLElement element in listElements) { IHTMLElement parentList = element; while (parentList != null && !ElementFilters.IsListElement(parentList)) { parentList = parentList.parentElement; } if (parentList != null) { pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
protected void InflateEmptyParagraphs(MarkupRange range) { IHTMLElementFilter paragraphFilter = ElementFilters.CreateCompoundElementFilter( ElementFilters.CreateTagIdFilter("p"), ElementFilters.CreateTagIdFilter("div") ); foreach (IHTMLElement paragraphElement in range.GetElements(paragraphFilter, true)) { // We want only the "empty" paragraphs (e.g. <p> </p> or <p><br /></p>). bool paragraphIsEmpty = false; string innerHtml = paragraphElement.innerHTML ?? string.Empty; if (innerHtml.Equals(" ", StringComparison.OrdinalIgnoreCase)) { // Covers the case of <p> </p>. paragraphIsEmpty = true; } else if (String.IsNullOrEmpty(paragraphElement.innerText)) { IHTMLElementCollection children = (IHTMLElementCollection)paragraphElement.children; if (children != null && children.length == 1) { IHTMLElementCollection childBrs = (IHTMLElementCollection)children.tags("br"); if (childBrs != null && childBrs.length == 1) { // Covers the case of <p><br></p>. paragraphIsEmpty = true; } } } if (paragraphIsEmpty) { // Force MSHTML to render it even though its empty. IHTMLElement3 paragraphElement3 = (IHTMLElement3)paragraphElement; paragraphElement3.inflateBlock = true; // MSHTML will add the back in anyway, but in a special way so that it does not appear // selectable to the user. It seems to be their internal way of doing inflateBlock. paragraphElement.innerHTML = ""; } } }
/// <summary> /// Makes sure that whole (not parts of) tables are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeTables(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElement[] tableElements = range.GetElements(ElementFilters.TABLE_ELEMENTS, false); foreach (IHTMLElement element in tableElements) { IHTMLElement parentTable = element; while (parentTable != null && markupServices.GetElementTagId(parentTable) != _ELEMENT_TAG_ID.TAGID_TABLE) { parentTable = parentTable.parentElement; } if (parentTable != null) { pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
public static void RemoveAttributes(MshtmlMarkupServices markupServices, MarkupRange selection, string[] attributesToRemove) { IHTMLElementFilter[] filters = new IHTMLElementFilter[attributesToRemove.Length]; for (int i = 0; i < attributesToRemove.Length; i++) filters[i] = ElementFilters.CreateElementAttributeFilter(attributesToRemove[i]); IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false); foreach (IHTMLElement element in elements) { foreach (string attribute in attributesToRemove) element.removeAttribute(attribute, 0); } }
public static void ClearBackgroundColor(MshtmlMarkupServices markupServices, MarkupRange selection) { HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices); htmlStyleHelper.SplitInlineTags(selection.Start); htmlStyleHelper.SplitInlineTags(selection.End); IHTMLElement[] elements = selection.GetElements(ElementFilters.CreateTagIdFilter("font"), false); foreach (IHTMLElement element in elements) { element.style.backgroundColor = ""; } // We may now be left with empty font tags, e.g. <font>blah</font>. // After switching between editors this becomes <font size="+0">blah</font>, which // causes blah to be rendered differently. // To avoid that we need to remove any empty-attribute font tags. selection.RemoveElementsByTagId(_ELEMENT_TAG_ID.TAGID_FONT, true); }
public static string[] GetSmartContentIds(MarkupRange range) { ArrayList ids = new ArrayList(); foreach (IHTMLElement el in range.GetElements(new IHTMLElementFilter(IsSmartContentContainer), false)) { if (el.id != null) ids.Add(el.id); } return (string[])ids.ToArray(typeof(string)); }
private void FindCellRange(MarkupRange selectedRange, out ArrayList selectedCells, out IHTMLTableCell beginCell, out IHTMLTableCell endCell) { // default to null beginCell = null; endCell = null; selectedCells = new ArrayList(); // JJA: fix bug #476623 -- at document initialization the selected markup range // may not yet be positioned so protect ourselves in this case if (!selectedRange.Positioned) return; // query for all of the table cells within the range selectedCells.AddRange(selectedRange.GetElements(ElementFilters.TABLE_CELL_ELEMENT, false)); // extract the begin and end cells if (selectedCells.Count == 0) { // see if the selection is contained within a single cell beginCell = selectedRange.Start.GetParentElement(ElementFilters.TABLE_CELL_ELEMENT) as IHTMLTableCell; if (beginCell != null) { // make sure the cell is content editable (it would not be in the case // where the call to GetParentElement went all the way out of the body // and found a cell that was part of the containing template) if (!(beginCell as IHTMLElement3).isContentEditable) beginCell = null; } endCell = beginCell; selectedCells.Add(beginCell); } else if (selectedCells.Count == 1) { beginCell = selectedCells[0] as IHTMLTableCell; endCell = selectedCells[0] as IHTMLTableCell; } else { beginCell = selectedCells[0] as IHTMLTableCell; endCell = selectedCells[selectedCells.Count - 1] as IHTMLTableCell; } }