docxmlater 10.2.3 → 10.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +9 -0
- package/dist/core/Document.js.map +1 -1
- package/dist/elements/Field.d.ts +9 -0
- package/dist/elements/Field.d.ts.map +1 -1
- package/dist/elements/Field.js +111 -0
- package/dist/elements/Field.js.map +1 -1
- package/package.json +1 -1
- package/src/core/Document.ts +13 -0
- package/src/elements/Field.ts +155 -0
package/src/elements/Field.ts
CHANGED
|
@@ -627,6 +627,7 @@ export class ComplexField {
|
|
|
627
627
|
*/
|
|
628
628
|
private _hasResultSection = false;
|
|
629
629
|
private _formFieldData?: FormFieldData;
|
|
630
|
+
private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
|
|
630
631
|
|
|
631
632
|
/**
|
|
632
633
|
* Creates a new complex field
|
|
@@ -843,6 +844,160 @@ export class ComplexField {
|
|
|
843
844
|
return [...this.resultContent];
|
|
844
845
|
}
|
|
845
846
|
|
|
847
|
+
/**
|
|
848
|
+
* Sets the tracking context for automatic change tracking
|
|
849
|
+
* @internal
|
|
850
|
+
*/
|
|
851
|
+
_setTrackingContext(context: import('../tracking/TrackingContext').TrackingContext): void {
|
|
852
|
+
this.trackingContext = context;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Gets the accepted (visible) text from resultContent XMLElements.
|
|
857
|
+
* Processes w:r (plain runs), w:ins (accepted insertions), skips w:del/w:moveFrom.
|
|
858
|
+
* Falls back to this.result if resultContent is empty.
|
|
859
|
+
*/
|
|
860
|
+
getAcceptedResultText(): string {
|
|
861
|
+
if (this.resultContent.length === 0) {
|
|
862
|
+
return this.result || '';
|
|
863
|
+
}
|
|
864
|
+
let text = '';
|
|
865
|
+
for (const element of this.resultContent) {
|
|
866
|
+
if (element.name === 'w:r') {
|
|
867
|
+
text += this.extractTextFromRunXml(element);
|
|
868
|
+
} else if (element.name === 'w:ins') {
|
|
869
|
+
for (const child of element.children || []) {
|
|
870
|
+
if (typeof child !== 'string' && child.name === 'w:r') {
|
|
871
|
+
text += this.extractTextFromRunXml(child);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
// Skip w:del, w:moveFrom (deleted/moved text not visible)
|
|
876
|
+
}
|
|
877
|
+
return text || this.result || '';
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Extracts text content from a w:r (run) XMLElement
|
|
882
|
+
*/
|
|
883
|
+
private extractTextFromRunXml(run: XMLElement): string {
|
|
884
|
+
let text = '';
|
|
885
|
+
for (const child of run.children || []) {
|
|
886
|
+
if (typeof child !== 'string' && (child.name === 'w:t' || child.name === 'w:delText')) {
|
|
887
|
+
for (const t of child.children || []) {
|
|
888
|
+
if (typeof t === 'string') text += t;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return text;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Sets the field result with tracked changes (del/ins pair).
|
|
897
|
+
* Creates a w:del wrapping the old text and a w:ins wrapping the new text,
|
|
898
|
+
* preserving formatting from the original result runs.
|
|
899
|
+
*
|
|
900
|
+
* @param newText - New result text to display
|
|
901
|
+
* @param author - Author name for the revision
|
|
902
|
+
* @param options - Optional formatting control
|
|
903
|
+
*/
|
|
904
|
+
setTrackedResult(
|
|
905
|
+
newText: string,
|
|
906
|
+
author: string,
|
|
907
|
+
options?: {
|
|
908
|
+
formatting?: RunFormatting;
|
|
909
|
+
preserveFormatting?: boolean;
|
|
910
|
+
}
|
|
911
|
+
): this {
|
|
912
|
+
const currentText = this.getAcceptedResultText();
|
|
913
|
+
if (currentText === newText) return this; // No-op if unchanged
|
|
914
|
+
|
|
915
|
+
const date = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
916
|
+
|
|
917
|
+
// Get revision IDs from TrackingContext if available, else Date.now()-based
|
|
918
|
+
let delId: number, insId: number;
|
|
919
|
+
if (this.trackingContext) {
|
|
920
|
+
const rm = this.trackingContext.getRevisionManager();
|
|
921
|
+
delId = rm.consumeNextId();
|
|
922
|
+
insId = rm.consumeNextId();
|
|
923
|
+
} else {
|
|
924
|
+
const base = Date.now() % 100000000;
|
|
925
|
+
delId = base;
|
|
926
|
+
insId = base + 1;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Resolve formatting: explicit > preserve > resultFormatting
|
|
930
|
+
let rPr: XMLElement | null = null;
|
|
931
|
+
if (options?.formatting) {
|
|
932
|
+
rPr = this.createRunProperties(options.formatting);
|
|
933
|
+
} else if (options?.preserveFormatting) {
|
|
934
|
+
rPr = this.extractFirstVisibleRunProperties();
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Build <w:del> with old text
|
|
938
|
+
const delRunChildren: (string | XMLElement)[] = [];
|
|
939
|
+
if (rPr) delRunChildren.push(rPr);
|
|
940
|
+
delRunChildren.push({
|
|
941
|
+
name: 'w:delText',
|
|
942
|
+
attributes: { 'xml:space': 'preserve' },
|
|
943
|
+
children: [currentText],
|
|
944
|
+
});
|
|
945
|
+
const delElement: XMLElement = {
|
|
946
|
+
name: 'w:del',
|
|
947
|
+
attributes: { 'w:id': String(delId), 'w:author': author, 'w:date': date },
|
|
948
|
+
children: [{ name: 'w:r', children: delRunChildren }],
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
// Build <w:ins> with new text
|
|
952
|
+
const insRunChildren: (string | XMLElement)[] = [];
|
|
953
|
+
if (rPr) insRunChildren.push(rPr);
|
|
954
|
+
insRunChildren.push({
|
|
955
|
+
name: 'w:t',
|
|
956
|
+
attributes: { 'xml:space': 'preserve' },
|
|
957
|
+
children: [newText],
|
|
958
|
+
});
|
|
959
|
+
const insElement: XMLElement = {
|
|
960
|
+
name: 'w:ins',
|
|
961
|
+
attributes: { 'w:id': String(insId), 'w:author': author, 'w:date': date },
|
|
962
|
+
children: [{ name: 'w:r', children: insRunChildren }],
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// Replace content: resultContent takes priority in toXML()
|
|
966
|
+
this.resultContent = [delElement, insElement];
|
|
967
|
+
this.result = newText;
|
|
968
|
+
this.resultRevisions = [];
|
|
969
|
+
|
|
970
|
+
return this;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Extracts w:rPr from the first visible run in resultContent
|
|
975
|
+
*/
|
|
976
|
+
private extractFirstVisibleRunProperties(): XMLElement | null {
|
|
977
|
+
for (const element of this.resultContent) {
|
|
978
|
+
if (element.name === 'w:r') {
|
|
979
|
+
const rPr = (element.children || []).find(
|
|
980
|
+
(c): c is XMLElement => typeof c !== 'string' && c.name === 'w:rPr'
|
|
981
|
+
);
|
|
982
|
+
if (rPr) return rPr;
|
|
983
|
+
} else if (element.name === 'w:ins') {
|
|
984
|
+
for (const child of element.children || []) {
|
|
985
|
+
if (typeof child !== 'string' && child.name === 'w:r') {
|
|
986
|
+
const rPr = (child.children || []).find(
|
|
987
|
+
(c): c is XMLElement => typeof c !== 'string' && c.name === 'w:rPr'
|
|
988
|
+
);
|
|
989
|
+
if (rPr) return rPr;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
// Fall back to resultFormatting
|
|
995
|
+
if (this.resultFormatting) {
|
|
996
|
+
return this.createRunProperties(this.resultFormatting);
|
|
997
|
+
}
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
|
|
846
1001
|
/**
|
|
847
1002
|
* Sets whether this field spans multiple paragraphs
|
|
848
1003
|
*/
|