Ogólne#
Range#
W tym miejscu umieszczam powtarzające się specyficzne pojęcia i algorytmy w interfejsie Range.
Pojęcia#
Obiekty typu Range
mogą zostać użyte do zaznaczenia pewnej części w drzewie węzłów.
boundary point
offset
Punkt graniczny # (boundary point) jest krotką w postaci (węzeł, przesunięcie # [offset]), np. (node, offset), gdzie offset jest nienegatywną liczbą typu całkowitego.
Generalnie rzecz biorąc, przesunięcie w punkcie granicznym będzie między zerem a długością dla węzła w punkcie granicznym, włącznie. Algorytmy modyfikujące drzewo węzłów (w szczególności wstawianie, usuwanie, normalizacja, zastępowanie danych tekstowych czy podział) również modyfikują zakresy rozciągnięte na tym drzewie (szczegóły).
start
end
content
Każdy zakres jest skojarzony z dwoma punktami granicznymi - początkiem # (start) i końcem # (end). Między nimi znajduje się jego zawartość # (content).
start node
start offset
end node
end offset
Dla wygody niech węzeł początkowy # (start node) wskazuje na węzeł w początku, przesunięcie początkowe # (start offset) wskazuje na przesunięcie w początku, węzeł końcowy # (end node) wskazuje na węzeł w końcu , i przesunięcie końcowe # (end offset) wskazuje na przesunięcie w końcu.
root
Korzeniem # (root) w zakresie jest korzeń jego węzła początkowego.
collapsed
Zakres jest zwinięty # (collapsed) kiedy jego początek i koniec to jeden i ten sam punkt graniczny. Zawartość w takim zakresie nie istnieje (jest pusta), co z perspektywy niektórych poleceń będzie równoważne ze zwracaniem pustego fragmentu dokumentu.
Oto kilka faktów dla lepszego zrozumienia tych definicji: #
- Zawartość należąca do zakresu obejmuje wszystkie zawarte w nim węzły, plus ewentualnie niektóre treści z węzła początkowego i węzła końcowego jeśli są nimi węzły typu
Text
,Comment
lubProcessingInstruction
. - Węzły, które są zawarte w zakresie generalnie nie są ciągłe/przylegające (contiguous), ponieważ rodzic w zawartym węźle nie zawsze jest zawarty.
- Jednakże, potomkowie w zawartym węźle są zawarci, i jeśli dwóch braci jest zawartych, to dotyczy też braci, którzy znajdują się między nimi.
- Pierwszy zawarty węzeł (jeśli istnieje) będzie zawsze za węzłem początkowym, i ostatni zawarty węzeł będzie równy lub przed ostatnim potomkiem w węźle końcowym.
- Węzeł początkowy i węzeł końcowy zakresu nigdy nie jest w nim zawarty.
- Istnieje pewien częściowo zawarty węzeł wtedy i tylko wtedy jeśli węzeł początkowy i węzeł końcowy są różne.
- Wartość atrybutu
commonAncestorContainer
nigdy nie jest zawarta ani częściowo zawarta. - Jeśli węzeł początkowy jest przodkiem dla węzła końcowego, to wspólny przodek obejmujący będzie węzłem początkowym. Dokładnie jedno z jego dzieci będzie częściowo zawarte, i dziecko będzie zawarte wtedy i tylko wtedy, gdy poprzedza częściowo zawarte dziecko. Jeśli węzeł końcowy jest przodkiem dla węzła początkowego, to zachowaj się przeciwnie.
- Jeśli węzeł początkowy nie jest przodkiem obejmującym dla węzła końcowego, ani odwrotnie, to wspólny przodek obejmujący będzie różnić się od nich obu. Dokładnie dwoje ich dzieci będzie częściowo zawarte, a dziecko będzie zawarte wtedy i tylko wtedy, gdy znajduje się pomiędzy tymi dwoma.
Algorytmy#
contained
Węzeł node jest zawarty # (contained) w zakresie range jeśli korzeń w node jest taki sam jak korzeń w range, i (node, 0
) jest za początkiem w range oraz (node, długość w node) jest przed końcem w range.
partially contained
Węzeł node jest częściowo zawarty # (partially contained) w zakresie range jeśli jest to przodek obejmujący dla węzła początkowego w range, ale nie jego węzła końcowego, lub odwrotnie.
position
Jeśli dwa węzły w punktach granicznych (node A, offset A) i (node B, offset B) mają ten sam korzeń, to pozycja # (position) pierwszego względem drugiego jest przed # (before), równa # (equal) lub za # (after), zwracana zgodnie z poniższym algorytmem:
- Jeśli node A jest taki sam jak node B, to zwróć równy jeśli offset A jest taki sam jak offset B, przed jeśli offset A jest mniejszy niż offset B, i za jeśli offset A jest większy niż offset B.
- Jeśli node A następuje po node B, to oblicz pozycję (node B, offset B) względem (node A, offset A). Jeśli jest przed, to zwróć za. Jeśli jest za, to zwróć przed.
Jeśli node A jest przodkiem dla node B:
- Zwróć przed.
set the start or end
Aby ustawić początek lub koniec # (set the start or end) w zakresie range na punkt graniczny (node, offset) należy wykonać następujące kroki:
- Jeśli node jest typu
DocumentType
, to zrzuć wyjątek"InvalidNodeTypeError"
. - Jeśli offset jest większy od długości w node, to zrzuć wyjątek
"IndexSizeError"
. - Niech bp będzie punktem granicznym (node, offset).
- Jeśli te kroki wywołane zostały jako "ustaw początek"
- Jeśli te kroki wywołane zostały jako "ustaw koniec"
select
Aby wybrać # (select) węzeł node w zakresie range należy wykonać następujące kroki:
- Niech parent będzie rodzicem w node.
- Jeśli parent ma wartość
null
, to zrzuć wyjątek"InvalidNodeTypeError"
. - Niech index będzie indeksem w node.
- Ustaw początek w range na punkt graniczny (parent, index).
- Ustaw koniec w range na punkt graniczny (parent, index plus jeden).
insert
Aby wstawić # (insert) węzeł node do zakresu range należy wykonać następujące kroki:
- Jeśli węzeł początkowy w range jest węzłem typu
ProcessingInstruction
lubComment
, lub węzłem typuText
, którego rodzic ma wartośćnull
, lub samym node, to zrzuć wyjątek"HierarchyRequestError"
. - Niech referenceNode ma wartość
null
. - Jeśli węzeł początkowy w range jest węzłem typu
Text
, to ustaw referenceNode na ten węzłe typuText
. - W przeciwnym razie niech referenceNode będzie dzieckiem w węźle początkowym, którego indeksem jest przesunięcie początkowe, lub wartością
null
, jeśli nie ma takiego dziecka. - Niech parent będzie węzłem początkowym w range jeśli referenceNode ma wartość
null
. W przeciwnym razie niech parent będzie rodzicem w referenceNode. - Zagwarantuj poprawność przed wstawieniem node do parent przed referenceNode.
- Jeśli węzeł początkowy w range jest węzłem typu
Text
, to podziel go z początkiem wskazywanym przez przesunięcie początkowe w range i ustaw referenceNode na zwracany wynik. - Jeśli node jest równy referenceNode to ustaw referenceNode na swojego brata następującego.
- Jeśli rodzic w node nie ma wartości
null
to usuń node z jego rodzica. - Niech newOffset będzie długością w parent jeśli referenceNode ma wartość
null
. W przeciwnym razie niech parent będzie indeksem w referenceNode. - Zwiększ newOffset o długość w node jeśli node jest węzłem typu
DocumentFragment
. W przeciwnym razie zwiększ newOffset o jeden. - Wykonaj przed wstawienie node do parent przed referenceNode.
- Jeśli początek i koniec w range są tacy sami, to ustaw koniec w range na (parent, newOffset).
clone content
Aby sklonować zawartość # (clone the content) zakresu range należy wykonać następujące kroki:
- Niech fragment będzie nowym węzłem typu
DocumentFragment
, którego właścicielem jest ten sam właściciel, jak w przypadku węzła początkowego w range. - Jeśli początek w range i koniec w range są tacy sami, to zwróć fragment.
- Niech original start node, original start offset, original end node i original end offset będą odpowiednio węzłem początkowym, przesunięciem początkowym, węzłem końcowym i przesunięciem końcowym w range.
Jeśli original start node i original end node są tacy sami, i są węzłami typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:- Niech clone będzie klonem original start node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original start node, początkiem original start offset i licznikiem w postaci original end offset minus original start offset.
- Dodaj clone do fragment.
- Zwróć fragment.
- Niech common ancestor będzie referencją do original start node.
- Dopóki (while) common ancestor nie jest przodkiem obejmującym dla original end node, to ustaw common ancestor na jego własnego rodzica.
- Niech first partially contained child będzie wartością
null
. - Jeśli original start node nie jest przodkiem obejmującym dla original end node, to ustaw first partially contained child na pierwsze dziecko w common ancestor, które jest częściowo zawarte w range.
- Niech last partially contained child będzie wartością
null
. Jeśli original end node nie jest przodkiem obejmującym dla original start node, to ustaw last partially contained child na ostatnie dziecko w common ancestor, które jest częściowo zawarte w range.
Te przypisania zmiennych faktycznie zawsze mają sens. Na przykład, jeśli original start node nie jest przodkiem obejmującym dla original end node, to original start node jest we własnej postaci częściowo zawarty w zakresie, tak samo wszyscy jego przodkowie, aż do dziecka w common ancestor. Sam common ancestor nie może być original start node ponieważ musi być przodkiem obejmującym dla original end node. Drugi przypadek jest bardzo podobny. Zauważ też, że dwoje dzieci nigdy nie będzie równe, jeśli oboje będą zdefiniowani.
- Niech contained children będzie listą wszystkich dzieci w common ancestor, którzy są zawarci w range, zgodnie z porządkiem drzewa.
Jeśli którykolwiek z członków w contained children jest węzłem typu
DocumentType
, to zrzuć wyjątek"HierarchyRequestError"
.Nie musimy się martwić o pierwszy lub ostatni częściowo zawarty węzeł ponieważ węzeł typu
DocumentType
nigdy nie może być częściowo zawarty. On nie może być punktem granicznym zakresu i nie może być przodkiem dla kogokolwiek.Jeśli first partially contained child jest węzłem typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:W tym przypadku first partially contained child jest referencją do original start node.
- Niech clone będzie klonem original start node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original start node, początkiem original start offset i licznikiem w postaci długość w original start node minus original start offset.
- Dodaj clone do fragment.
W przeciwnym razie, jeśli first partially contained child nie ma wartości
null
, to wykonaj poniższe podkroki:- Niech clone będzie klonem first partially contained child.
- Dodaj clone do fragment.
- Niech subrange będzie nowym obiektem typu
Range
z początkiem (original start node, original start offset) i końcem (first partially contained child, długość w first partially contained child). - Niech subfragment będzie rezultatem klonowania zawartości w subrange.
- Dodaj subfragment do clone.
Dla każdego (for each) zawartego dziecka contained child w contained children:
Jeśli last partially contained child jest węzłem typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:W tym przypadku last partially contained child jest referencją do original end node.
- Niech clone będzie klonem original end node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original end node, początkiem
0
i licznikiem original end offset. - Dodaj clone do fragment.
W przeciwnym razie, jeśli last partially contained child nie ma wartości
null
, to wykonaj poniższe podkroki:- Niech clone będzie klonem last partially contained child.
- Dodaj clone do fragment.
- Niech subrange będzie nowym obiektem typu
Range
z początkiem (last partially contained child,0
) i końcem (original end node, original end offset). - Niech subfragment będzie rezultatem klonowania zawartości w subrange.
- Dodaj subfragment do clone.
- Zwróć fragment.
extract content
Aby wyciągnąć zawartość # (extract content) zakresu range należy wykonać następujące kroki:
- Niech fragment będzie nowym węzłem typu
DocumentFragment
, którego właścicielem jest ten sam właściciel, jak w przypadku węzła początkowego w range. - Jeśli początek w range i koniec w range są tacy sami, to zwróć fragment.
- Niech original start node, original start offset, original end node i original end offset będą odpowiednio węzłem początkowym, przesunięciem początkowym, węzłem końcowym i przesunięciem końcowym w range.
Jeśli original start node i original end node są tacy sami, i są węzłami typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:- Niech clone będzie klonem original start node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original start node, początkiem original start offset i licznikiem w postaci original end offset minus original start offset.
- Dodaj clone do fragment.
- Zastąp dane tekstowe z węzłem original start node, początkiem original start offset, licznikiem w postaci original end offset minus original start offset i danymi tekstowymi w postaci pustego łańcucha znakowego.
- Zwróć fragment.
- Niech common ancestor będzie referencją do original start node.
- Dopóki (while) common ancestor nie jest przodkiem obejmującym dla original end node, to ustaw common ancestor na jego własnego rodzica.
- Niech first partially contained child będzie wartością
null
. - Jeśli original start node nie jest przodkiem obejmującym dla original end node, to ustaw first partially contained child na pierwsze dziecko w common ancestor, które jest częściowo zawarte w range.
- Niech last partially contained child będzie wartością
null
. Jeśli original end node nie jest przodkiem obejmującym dla original start node, to ustaw last partially contained child na ostatnie dziecko w common ancestor, które jest częściowo zawarte w range.
Te przypisania zmiennych faktycznie zawsze mają sens. Na przykład, jeśli original start node nie jest przodkiem obejmującym dla original end node, to original start node jest we własnej postaci częściowo zawarty w zakresie, tak samo wszyscy jego przodkowie, aż do dziecka w common ancestor. Sam common ancestor nie może być original start node ponieważ musi być przodkiem obejmującym dla original end node. Drugi przypadek jest bardzo podobny. Zauważ też, że dwoje dzieci nigdy nie będzie równe, jeśli oboje będą zdefiniowani.
- Niech contained children będzie listą wszystkich dzieci w common ancestor, którzy są zawarci w range, zgodnie z porządkiem drzewa.
Jeśli którykolwiek z członków w contained children jest węzłem typu
DocumentType
, to zrzuć wyjątek"HierarchyRequestError"
.Nie musimy się martwić o pierwszy lub ostatni częściowo zawarty węzeł ponieważ węzeł typu
DocumentType
nigdy nie może być częściowo zawarty. On nie może być punktem granicznym zakresu i nie może być przodkiem dla kogokolwiek.- Jeśli original start node jest przodkiem obejmującym dla original end node, to ustaw new node na original start node i new offset na original start offset.
W przeciwnym razie wykonaj poniższe podkroki:
- Niech reference node będzie referencją do original start node.
- Dopóki (while) rodzic w reference node nie ma wartości
null
i nie jest przodkiem obejmującym dla original end node, to ustaw reference node na jego własnego rodzica. Ustaw new node na rodzica w reference node i new offset na jeden plus indeks w reference node.
Jeśli rodzicem w reference node byłaby wartość
null
, to byłby on korzeniem dla range, więc byłby przodkiem obejmującym dla original end node, i moglibyśmy nie osiągnąć tego punktu.
Jeśli first partially contained child jest węzłem typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:W tym przypadku first partially contained child jest referencją do original start node.
- Niech clone będzie klonem original start node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original start node, początkiem original start offset i licznikiem w postaci długość w original start node minus original start offset.
- Dodaj clone do fragment.
- Zastąp dane tekstowe z węzłem original start node, początkiem original start offset, licznikiem w postaci original end offset minus original start offset i danymi tekstowymi w postaci pustego łańcucha znakowego.
W przeciwnym razie, jeśli first partially contained child nie ma wartości
null
, to wykonaj poniższe podkroki:- Niech clone będzie klonem first partially contained child.
- Dodaj clone do fragment.
- Niech subrange będzie nowym obiektem typu
Range
z początkiem (original start node, original start offset) i końcem (first partially contained child, długość w first partially contained child). - Niech subfragment będzie rezultatem wyciągania zawartości w subrange.
- Dodaj subfragment do clone.
Dla każdego (for each) zawartego dziecka contained child w contained children dodaj contained child do fragment:
Jeśli last partially contained child jest węzłem typu
Text
,ProcessingInstruction
lubComment
, to wykonaj poniższe podkroki:W tym przypadku last partially contained child jest referencją do original end node.
- Niech clone będzie klonem original end node.
- Ustaw dane tekstowe w clone na wynik wydobywania część danych tekstowych z węzłem original end node, początkiem
0
i licznikiem original end offset. - Dodaj clone do fragment.
- Zastąp dane tekstowe z węzłem original end node, początkiem
0
, licznikiem original end offset i danymi tekstowymi w postaci pustego łańcucha znakowego..
W przeciwnym razie, jeśli last partially contained child nie ma wartości
null
, to wykonaj poniższe podkroki:- Niech clone będzie klonem last partially contained child.
- Dodaj clone do fragment.
- Niech subrange będzie nowym obiektem typu
Range
z początkiem (last partially contained child,0
) i końcem (original end node, original end offset). - Niech subfragment będzie rezultatem wyciągania zawartości w subrange.
- Dodaj subfragment do clone.
- Ustaw początek i koniec w range na (new node, new offset).
- Zwróć fragment.