/// <summary> /// Replaces some data starting at the given offset with the given /// length. /// </summary> /// <param name="offset">The start index.</param> /// <param name="count">The length of the replacement.</param> /// <param name="data">The data to insert at the replacement.</param> public void Replace(Int32 offset, Int32 count, String data) { var owner = Owner; var length = _content.Length; if (offset > length) { throw new DomException(DomError.IndexSizeError); } if (offset + count > length) { count = length - offset; } owner.QueueMutation(MutationRecord.CharacterData(target: this, previousValue: _content)); var deleteOffset = offset + data.Length; _content = _content.Insert(offset, data).Remove(deleteOffset, count); owner.ForEachRange(m => m.Head == this && m.Start > offset && m.Start <= offset + count, m => m.StartWith(this, offset)); owner.ForEachRange(m => m.Tail == this && m.End > offset && m.End <= offset + count, m => m.EndWith(this, offset)); owner.ForEachRange(m => m.Head == this && m.Start > offset + count, m => m.StartWith(this, m.Start + data.Length - count)); owner.ForEachRange(m => m.Tail == this && m.End > offset + count, m => m.EndWith(this, m.End + data.Length - count)); }
internal void RemoveChild(Node node, Boolean suppressObservers) { var document = Owner; var index = _children.Index(node); if (document != null) { document.ForEachRange(m => m.Head.IsInclusiveDescendantOf(node), m => m.StartWith(this, index)); document.ForEachRange(m => m.Tail.IsInclusiveDescendantOf(node), m => m.EndWith(this, index)); document.ForEachRange(m => m.Head == this && m.Start > index, m => m.StartWith(this, m.Start - 1)); document.ForEachRange(m => m.Tail == this && m.End > index, m => m.EndWith(this, m.End - 1)); } var oldPreviousSibling = index > 0 ? _children[index - 1] : null; if (!suppressObservers && document != null) { var removedNodes = new NodeList { node }; document.QueueMutation(MutationRecord.ChildList( target: this, removedNodes: removedNodes, previousSibling: oldPreviousSibling, nextSibling: node.NextSibling)); document.AddTransientObserver(node); } RemoveNode(index, node); NodeIsRemoved(node, oldPreviousSibling); }
internal INode ReplaceChild(Node node, Node child, Boolean suppressObservers) { if (this.IsEndPoint() || node.IsHostIncludingInclusiveAncestor(this)) { throw new DomException(DomError.HierarchyRequest); } if (child.Parent != this) { throw new DomException(DomError.NotFound); } if (node.IsInsertable()) { var parent = this as IDocument; var referenceChild = child.NextSibling; var document = Owner; var addedNodes = new NodeList(); var removedNodes = new NodeList(); if (parent != null && IsChangeForbidden(node, parent, child)) { throw new DomException(DomError.HierarchyRequest); } if (Object.ReferenceEquals(referenceChild, node)) { referenceChild = node.NextSibling; } document?.AdoptNode(node); RemoveChild(child, true); InsertBefore(node, referenceChild, true); removedNodes.Add(child); if (node._type == NodeType.DocumentFragment) { addedNodes.AddRange(node._children); } else { addedNodes.Add(node); } if (!suppressObservers && document != null) { document.QueueMutation(MutationRecord.ChildList( target: this, addedNodes: addedNodes, removedNodes: removedNodes, previousSibling: child.PreviousSibling, nextSibling: referenceChild)); } return(child); } throw new DomException(DomError.HierarchyRequest); }
internal void AttributeChanged(String localName, String namespaceUri, String oldValue) { Owner.QueueMutation(MutationRecord.Attributes( target: this, attributeName: localName, attributeNamespace: namespaceUri, previousValue: oldValue)); }
/// <summary> /// Queues a record. /// </summary> /// <param name="record">The record to queue up.</param> internal void Enqueue(MutationRecord record) { if (_records.Count > 0) { //Here we could schedule a callback! } _records.Enqueue(record); }
/// <summary> /// Queues a record. /// </summary> /// <param name="record">The record to queue up.</param> internal void Enqueue(MutationRecord record) { if (_records.Count > 0) { //Here we could schedule a callback! } _records.Enqueue(record); }
/// <summary> /// Queues a mutation record for the corresponding observers. /// </summary> /// <param name="document">The document to use.</param> /// <param name="record">The record to enqueue.</param> public static void QueueMutation(this Document document, MutationRecord record) { if (document == null) return; var observers = document.Mutations.Observers.ToArray(); if (observers.Length == 0) return; var nodes = record.Target.GetInclusiveAncestors(); for (var i = 0; i < observers.Length; i++) { var observer = observers[i]; var clearPreviousValue = default(bool?); foreach (var node in nodes) { var options = observer.ResolveOptions(node); if (options.IsInvalid) continue; else if (node != record.Target && options.IsObservingSubtree == false) continue; else if (record.IsAttribute && options.IsObservingAttributes == false) continue; else if (record.IsAttribute && options.AttributeFilters != null && (options.AttributeFilters.Contains(record.AttributeName) == false || record.AttributeNamespace != null)) continue; else if (record.IsCharacterData && options.IsObservingCharacterData == false) continue; else if (record.IsChildList && options.IsObservingChildNodes == false) continue; if (clearPreviousValue.HasValue == false || clearPreviousValue.Value == true) { clearPreviousValue = (record.IsAttribute && options.IsExaminingOldAttributeValue == false) || (record.IsCharacterData && options.IsExaminingOldCharacterData == false); } } if (clearPreviousValue == null) continue; observer.Enqueue(record.Copy(clearPreviousValue.Value)); } document.PerformMicrotaskCheckpoint(); }
internal void AttributeChanged(String localName, String namespaceUri, String oldValue, String newValue) { var callback = GetOrCreateCallback(GetType()); if (namespaceUri == null) { callback.Invoke(this, localName, newValue); } Owner.QueueMutation(MutationRecord.Attributes( target: this, attributeName: localName, attributeNamespace: namespaceUri, previousValue: oldValue)); }
internal void AttributeChanged(String localName, String namespaceUri, String oldValue, String newValue) { if (namespaceUri == null) { foreach (var observer in Owner.Options.GetServices <IAttributeObserver>()) { observer.NotifyChange(this, localName, newValue); } } Owner.QueueMutation(MutationRecord.Attributes( target: this, attributeName: localName, attributeNamespace: namespaceUri, previousValue: oldValue)); }
internal void ReplaceAll(Node node, Boolean suppressObservers) { var document = Owner; if (node != null) { document.AdoptNode(node); } var removedNodes = new NodeList(); var addedNodes = new NodeList(); removedNodes.AddRange(_children); if (node != null) { if (node.NodeType == NodeType.DocumentFragment) { addedNodes.AddRange(node._children); } else { addedNodes.Add(node); } } for (var i = 0; i < removedNodes.Length; i++) { RemoveChild(removedNodes[i], true); } for (var i = 0; i < addedNodes.Length; i++) { InsertBefore(addedNodes[i], null, true); } if (!suppressObservers) { document.QueueMutation(MutationRecord.ChildList( target: this, addedNodes: addedNodes, removedNodes: removedNodes)); } }
/// <summary> /// Queues a mutation record for the corresponding observers. /// </summary> /// <param name="document">The document to use.</param> /// <param name="record">The record to enqueue.</param> internal static void QueueMutation(this Document document, MutationRecord record) { var observers = document.Mutations.Observers.ToArray(); if (observers.Length > 0) { var nodes = record.Target.GetInclusiveAncestors(); for (var i = 0; i < observers.Length; i++) { var observer = observers[i]; var clearPreviousValue = default(Boolean?); foreach (var node in nodes) { var options = observer.ResolveOptions(node); if (options.IsInvalid || (node != record.Target && !options.IsObservingSubtree) || (record.IsAttribute && !options.IsObservingAttributes) || (record.IsAttribute && options.AttributeFilters != null && (!options.AttributeFilters.Contains(record.AttributeName) || record.AttributeNamespace != null)) || (record.IsCharacterData && !options.IsObservingCharacterData) || (record.IsChildList && !options.IsObservingChildNodes)) { continue; } if (!clearPreviousValue.HasValue || clearPreviousValue.Value) { clearPreviousValue = (record.IsAttribute && !options.IsExaminingOldAttributeValue) || (record.IsCharacterData && !options.IsExaminingOldCharacterData); } } if (clearPreviousValue != null) { observer.Enqueue(record.Copy(clearPreviousValue.Value)); } } document.PerformMicrotaskCheckpoint(); } }
internal void AttributeChanged(String localName, String namespaceUri, String oldValue, Boolean suppressMutationObservers = false) { Action <String> handler = null; if (_attributeHandlers.TryGetValue(localName, out handler)) { var attr = _attributes.Get(localName); handler(attr != null ? attr.Value : null); } if (!suppressMutationObservers) { Owner.QueueMutation(MutationRecord.Attributes( target: this, attributeName: localName, attributeNamespace: namespaceUri, previousValue: oldValue)); } }
internal INode ReplaceChild(Node node, Node child, Boolean suppressObservers) { if (this.IsEndPoint() || node.IsHostIncludingInclusiveAncestor(this)) { throw new DomException(DomError.HierarchyRequest); } if (child.Parent != this) { throw new DomException(DomError.NotFound); } if (node.IsInsertable()) { var parent = _parent as IDocument; var referenceChild = child.NextSibling; var document = Owner; var addedNodes = new NodeList(); var removedNodes = new NodeList(); if (parent != null) { var forbidden = false; switch (node._type) { case NodeType.DocumentType: forbidden = parent.Doctype != child || child.IsPrecededByElement(); break; case NodeType.Element: forbidden = parent.DocumentElement != child || child.IsFollowedByDoctype(); break; case NodeType.DocumentFragment: var elements = node.GetElementCount(); forbidden = elements > 1 || node.HasTextNodes() || (elements == 1 && (parent.DocumentElement != child || child.IsFollowedByDoctype())); break; } if (forbidden) { throw new DomException(DomError.HierarchyRequest); } } if (referenceChild == node) { referenceChild = node.NextSibling; } document?.AdoptNode(node); RemoveChild(child, true); InsertBefore(node, referenceChild, true); removedNodes.Add(child); if (node._type == NodeType.DocumentFragment) { addedNodes.AddRange(node._children); } else { addedNodes.Add(node); } if (!suppressObservers && document != null) { document.QueueMutation(MutationRecord.ChildList( target: this, addedNodes: addedNodes, removedNodes: removedNodes, previousSibling: child.PreviousSibling, nextSibling: referenceChild)); } return(child); } throw new DomException(DomError.HierarchyRequest); }
internal INode InsertBefore(Node newElement, Node referenceElement, Boolean suppressObservers) { var document = Owner; var count = newElement.NodeType == NodeType.DocumentFragment ? newElement.ChildNodes.Length : 1; if (referenceElement != null && document != null) { var childIndex = referenceElement.Index(); document.ForEachRange(m => m.Head == this && m.Start > childIndex, m => m.StartWith(this, m.Start + count)); document.ForEachRange(m => m.Tail == this && m.End > childIndex, m => m.EndWith(this, m.End + count)); } if (newElement.NodeType == NodeType.Document || newElement.Contains(this)) { throw new DomException(DomError.HierarchyRequest); } var addedNodes = new NodeList(); var n = _children.Index(referenceElement); if (n == -1) { n = _children.Length; } if (newElement._type == NodeType.DocumentFragment) { var end = n; var start = n; while (newElement.HasChildNodes) { var child = newElement.ChildNodes[0]; newElement.RemoveChild(child, true); InsertNode(end, child); end++; } while (start < end) { var child = _children[start]; addedNodes.Add(child); NodeIsInserted(child); start++; } } else { addedNodes.Add(newElement); InsertNode(n, newElement); NodeIsInserted(newElement); } if (!suppressObservers && document != null) { document.QueueMutation(MutationRecord.ChildList( target: this, addedNodes: addedNodes, previousSibling: n > 0 ? _children[n - 1] : null, nextSibling: referenceElement)); } return(newElement); }