-
Notifications
You must be signed in to change notification settings - Fork 0
Conflict management
⚠️ This article has a mix of developer documentation and general information. Conflict management API is not expected to be used or understood by projects using Nilbog.
One potential issue with Nilbog is that multiple observers operating on the same element could create a feedback loop. Example lifecycle without conflict management:
- Observer A is created, protecting text on elements that match
.class-a
. - Observer B is created, protecting text on elements that match
.class-b
. - Element with classes
.class-a.class-b
has its text mutated.- Observer A catches this.
- Reverts text
- Observer B notices the reversion
- Reverts reversion
- Observer A notices
- ad infinitum
- Observer A notices
- Reverts reversion
- Observer B notices the reversion
- Reverts text
- Observer A catches this.
This will continue forever, exhausting the user's CPU and probably crashing the page (although browsers can be slow on killing the page when this occurs).
This is why Nilbog has a safe mode that implements conflict management.
Here is an example lifecycle with safe mode on:
- Nilbog observer is created (for example,
nilbog.protectText('.protect-text')
)- This observer is registered with the
ConflictManager
- The
ConflictManager
assigns it to the appropriateObserverList
(in this case, theprotectText
observer list).
- The
- This observer is registered with the
- Mutation event occurs:
- Before operating on the element, the observer asks the
ConflictManager
to resolve any conflicts that might exist.- The
ConflictManager
tells the appropriateObserverList
to resolve conflicts.- The
ObserverList
identifies conflicting observers that are watching on the same parent, are connected, and match with mutated element. - If any conflicts exist, the
ObserverList
applies some sort of tiebreaker.- Current tiebreaker sorts the observers by
uid
and returns the first in the list. This is repetable (same observer on subsequent calls) but not replicable (on a different page load, theuid
will be different so a different observer might be called).
- Current tiebreaker sorts the observers by
- If the observer has no conflicts or won the tiebreaker, returns
true
. Otherwise returnsfalse
.
- The
-
ConflictManager
passes on theObserverList
's decision.
- The
- If the
ConflictManager
returnstrue
, the observer is cleared to operate. Otherwise, the observer is told to refrain.
- Before operating on the element, the observer asks the
Different types of observers have specific ways of resolving conflicts.
If there's an observer of both of these types that operates on the same node, a feedback loop may occur. In the case that such a conflict is found, the observers are invariably told to do nothing, allowing the mutation to stay.
Both of these types of observers can choose to operate on specific changes (either created, deleted, or modified) and properties (for example, preventing creation of certain attributes). Unless there are no conflicts at all or the observer is the ultimate winner of all conflicts, conflicts are resolved on a case-by-case basis.
Routes resolution requests and registration to the appropriate ObserverList
.
Initialize ConflictManager
.
-
noop
(optional, default:false
) -noop
istrue
when safe mode is on. Essentially doesn't register any observers and always resolvestrue
.
Register observer in the appropriate ObserverList
. If noop
, does nothing.
-
type
(required) - Observer type. EitherpreventCreate
,preventDelete
,protectText
,protectAttributes
, orprotectClasses
. -
observer
(required) -Observer
instance.
Ask conflict manager to resolve any conflicts with observed mutation. If noop
, always gives the go-ahead.
-
action
(required) - Observer type. EitherpreventCreate
,preventDelete
,protectText
,protectAttributes
, orprotectClasses
. -
observer
(required) - InquiringObserver
instance. -
node
(required) - Matched node that the observer wants to revert. -
extras
(optional) - Rest of the parameters are collected to an array to be passed on.
-
true
- Ifnoop
is true - Whatever the
ObserverList
resolves with
-
noop
-true
if safe mode is off. Will bypass conflict management processes. -
observers
- Collection ofObserverList
s-
observers.preventCreate
:PreventObserverList
-
observers.preventDelete
:PreventObserverList
-
observers.protectText
:ObserverList
-
observers.protectAttributes
:ChangelogObserverList
-
observers.protectClasses
:ChangelogObserverList
-
Collection of observers
Initialize observer list.
Add to observer list.
-
observer
(required) -Observer
to add to list.
Get a list of conflicting observers that may also want to operate on the node.
-
observer
(required) - InquiringObserver
-
node
(required) - DOM element wanting to be operated on.
- List of conflicting observers. Criteria:
- not equal to
observer
- currently observing
- matches
node
- parents are either equal or have parent-child relationship
- not equal to
Determines whether observer gets to operate based on conflicting observers. Implementation determines this by sorting the observers by their unique ID, and picking the first one.
-
observer
(required) - InquiringObserver
-
conflicts
(required) - Array of conflictingObserver
s
-
true
- Won the tiebreaker -
false
- Lost the tiebreaker
Resolves conflicts if they exist.
-
observer
(required) - InquiringObserver
-
node
(required) - DOM element wanting to be operated on.
-
true
- Go ahead and operate -
false
- Don't operate, a differentObserver
might operate
-
observers
- Array ofObserver
s
PreventObserverList
class (extends ObserverList
)
ObserverList
for preventCreate
and preventDelete
.
conflicts(observer, node)
tiebreaker(observer, conflicts)
observers
Creates new observer list.
-
error
(required) - Warning displayed in the console when a conflict betweenpreventCreate
andpreventDelete
observers occur.
Resolve conflicts if they exist. Overrides observerList.resolve(observer, node)
.
-
observer
(required) - InquiringObserver
-
node
(required) - DOM element wanting to be operated on. -
other
(required) - CounterpartPreventObserverList
(preventCreate
's forpreventDelete
and vice-versa).
-
true
- Go ahead and operate -
false
- Don't operate, a differentObserver
might operate or there was a conflict betweenpreventCreate
andpreventDelete
.
-
error
(default:"Nilbog conflict: preventCreate and preventDelete targeting same node. No action taken."
) - Warning to be displayed in console when a conflict betweenpreventCreate
andpreventDelete
observers is found.
ChangelogObserverList
class (extends ObserverList
)
ObserverList
for protectAttributes
and protectClasses
.
constructor
conflicts(observer, node)
tiebreaker(observer, conflicts)
observers
Based on list on conflicts and what would be undone by the observer, breaks ties on a property-by-property, attribute-by-attribute basis.
-
undo
(required) - What would be undone by the observer. Each property has an array of stringsundo.create
undo.delete
undo.modify
-
observer
(required) - InquiringObserver
-
conflicts
(required) - ConflictingObserver
s
-
undo
- Inputundo
modified, removing any attributes that the observer loses in a tiebreaker with conflicts, and retaining uncontested attributes and attributes that were won in tiebreakers.undo.create
undo.delete
undo.modify
Resolve conflicts if they exist. Will resolve on a property-by-property, attribute-by-attribute basis if conflicts are found. Overrides observerList.resolve(observer, node)
.
-
observer
(required) - InquiringObserver
-
node
(required) - DOM element wanting to be operated on. -
undo
(required) - What would be undone by the observer. Each property has an array of stringsundo.create
undo.delete
undo.modify
-
true
- No conflicts or would ultimately win all tiebreakers so go ahead and operate. -
undo
- List of things that are allowed to be undone by this observer. Other observers will be operating on missing changes.undo.create
undo.delete
undo.modify