receiveChanges method
void
receiveChanges(
- Map<String, dynamic> message
)
Implementation
void receiveChanges(Map<String, dynamic> message) {
final nodeID = message["target"] as String?;
final target = message["root"] == true ? root : root.getNodeByID(nodeID!);
// process element deltas
num retain = 0;
for (final delta in message["elements"]) {
if (delta["retain"] != null) {
retain += delta["retain"];
}
if (delta["insert"] != null) {
for (final insert in delta["insert"] as List) {
if (insert["element"] != null) {
target!._children.insert(retain.toInt(), _createNode(target, insert));
retain++;
} else if (insert["text"] != null) {
target!._children.insert(retain.toInt(), _createNode(target, insert));
retain++;
} else {
throw Exception("Unsupported element delta");
}
}
} else if (delta["delete"] != null) {
target!._children.removeRange(retain.toInt(), (retain + (delta["delete"] as num)).toInt());
retain -= delta["delete"];
}
}
// process text deltas
List? text = message["text"];
if (text != null && text.isNotEmpty) {
if (target!.tagName != "text") {
throw Exception('Node is not a text node: $target.tagName');
}
final textNode = target._children[0] as TextElement;
num retain = 0;
int i = 0;
num offset = 0;
var targetDelta = textNode.delta;
for (final delta in text) {
if (delta["insert"] != null) {
if (i == targetDelta.length) {
targetDelta.add({"insert": delta["insert"], "attributes": delta["attributes"] ?? {}});
i++;
offset += (delta["insert"] as String).length;
retain += (delta["insert"] as String).length;
} else {
final str = targetDelta[i]["insert"] as String;
targetDelta[i]["insert"] =
str.substring(0, (retain - offset).toInt()) + delta["insert"] + str.substring((retain - offset).toInt());
retain += (delta["insert"] as String).length;
}
} else if (delta["delete"] != null) {
num deleted = 0;
while (delta["delete"] > deleted) {
num remaining = delta["delete"] - deleted;
// delete ends after item
if (retain > offset) {
// delete end
final str = targetDelta[i]["insert"] as String;
final start = str.substring(0, (retain - offset).toInt());
final end = str.substring((retain - offset).toInt());
if (remaining >= end.length) {
targetDelta[i]["insert"] = start;
deleted += end.length;
i++;
offset += str.length;
} else {
targetDelta[i]["insert"] = start + end.substring(remaining.toInt());
deleted += (targetDelta[i]["insert"] as String).length;
}
} else if (delta["delete"] - deleted >= (targetDelta[i]["insert"] as String).length) {
// delete segment
deleted += (targetDelta[i]["insert"] as String).length;
targetDelta.removeAt(i);
//offset += targetDelta.splice(i, 1);
} else {
// delete ends inside item, delete front
final str = targetDelta[i]["insert"] as String;
final start = str.substring(0, remaining.toInt());
final end = str.substring(remaining.toInt());
targetDelta[i]["insert"] = end;
deleted += start.length;
}
}
} else if (delta["attributes"] != null) {
num formatted = 0;
while (delta["retain"] as num > formatted) {
// format ends after item
num remaining = delta["retain"] - formatted;
if (targetDelta[i]["attributes"] == null) {
targetDelta[i]["attributes"] = <String, dynamic>{};
}
if (retain > offset) {
// format end
final str = targetDelta[i]["insert"] as String;
final start = str.substring(0, (retain - offset).toInt());
final end = str.substring((retain - offset).toInt());
if (remaining >= end.length) {
targetDelta[i]["insert"] = start;
targetDelta.insert(i + 1, {
"insert": end,
"attributes": {...targetDelta[i]["attributes"] as Map, ...delta["attributes"] as Map},
});
formatted += end.length;
// move to next item
i++;
i++;
offset += str.length;
} else {
targetDelta[i]["insert"] = start;
targetDelta.insert(i + 1, {
"insert": end.substring(0, remaining.toInt()),
"attributes": {...targetDelta[i]["attributes"] as Map, ...delta["attributes"] as Map},
});
targetDelta.insert(i + 2, {
"insert": end.substring(remaining.toInt()),
"attributes": {...targetDelta[i]["attributes"] as Map},
});
formatted += remaining;
i++;
i++;
i++;
offset += start.length + remaining;
}
} else if (delta["retain"] - formatted >= (targetDelta[i]["insert"] as String).length) {
formatted += (targetDelta[i]["insert"] as String).length;
// format whole item
for (final k in (delta["attributes"] as Map<String, dynamic>).keys) {
targetDelta[i]["attributes"][k] = delta["attributes"][k];
}
offset += (targetDelta[i]["insert"] as String).length;
i++;
} else {
// format ends inside item, format front
final str = targetDelta[i]["insert"] as String;
final start = str.substring(0, remaining.toInt());
final end = str.substring(remaining.toInt());
targetDelta[i]["insert"] = start;
targetDelta.add({
"insert": end,
"attributes": {...targetDelta[i]["attributes"] as Map},
});
for (final k in (delta["attributes"] as Map).keys) {
targetDelta[i]["attributes"][k] = delta["attributes"][k];
}
formatted += (delta["retain"] - formatted);
}
}
retain += delta["retain"];
} else if (delta["retain"] != null) {
if (delta["retain"] != null) {
retain += delta["retain"];
}
while (retain > (offset + ((targetDelta[i]["insert"] as String?)?.length ?? 0)).toInt()) {
offset += (targetDelta[i]["insert"] as String).length;
i++;
}
}
}
}
for (final change in message["attributes"]["set"] as List) {
target!.attributes[change["name"]] = change["value"];
target.notifyListeners();
}
for (final name in message["attributes"]["delete"] as List) {
target!.attributes.remove(name);
target.notifyListeners();
}
notifyListeners();
_changes.add(message);
}