Skip to content

Commit a4b34bb

Browse files
author
Satvik Kumar
committed
Operations for merging and splitting lists
1 parent c9346f2 commit a4b34bb

File tree

6 files changed

+318
-1
lines changed

6 files changed

+318
-1
lines changed

webodf/lib/manifest.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,9 @@
444444
"ops.OpInsertText": [
445445
"ops.OdtDocument"
446446
],
447+
"ops.OpMergeList": [
448+
"ops.OdtDocument"
449+
],
447450
"ops.OpMergeParagraph": [
448451
"odf.CollapsingRules",
449452
"ops.OdtDocument"
@@ -482,6 +485,10 @@
482485
"ops.OpSetParagraphStyle": [
483486
"ops.OdtDocument"
484487
],
488+
"ops.OpSplitList": [
489+
"odf.CollapsingRules",
490+
"ops.OdtDocument"
491+
],
485492
"ops.OpSplitParagraph": [
486493
"ops.OdtDocument"
487494
],
@@ -509,6 +516,7 @@
509516
"ops.OpInsertImage",
510517
"ops.OpInsertTable",
511518
"ops.OpInsertText",
519+
"ops.OpMergeList",
512520
"ops.OpMergeParagraph",
513521
"ops.OpMoveCursor",
514522
"ops.OpRemoveAnnotation",
@@ -521,6 +529,7 @@
521529
"ops.OpRemoveText",
522530
"ops.OpSetBlob",
523531
"ops.OpSetParagraphStyle",
532+
"ops.OpSplitList",
524533
"ops.OpSplitParagraph",
525534
"ops.OpUpdateMember",
526535
"ops.OpUpdateMetadata",

webodf/lib/ops/OpMergeList.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Copyright (C) 2010-2014 KO GmbH <[email protected]>
3+
*
4+
* @licstart
5+
* This file is part of WebODF.
6+
*
7+
* WebODF is free software: you can redistribute it and/or modify it
8+
* under the terms of the GNU Affero General Public License (GNU AGPL)
9+
* as published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* WebODF is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
19+
* @licend
20+
*
21+
* @source: http://www.webodf.org/
22+
* @source: https://github.com/kogmbh/WebODF/
23+
*/
24+
25+
/*global ops, runtime, odf, core */
26+
27+
/**
28+
*
29+
* @constructor
30+
* @implements ops.Operation
31+
*/
32+
ops.OpMergeList = function OpMergeList() {
33+
"use strict";
34+
35+
var memberid,
36+
timestamp,
37+
/**@type{!number}*/
38+
sourceStartPosition,
39+
/**@type{!number}*/
40+
destinationStartPosition,
41+
odfUtils = odf.OdfUtils;
42+
43+
/**
44+
* @param {!ops.OpMergeList.InitSpec} data
45+
*/
46+
this.init = function (data) {
47+
memberid = data.memberid;
48+
timestamp = data.timestamp;
49+
sourceStartPosition = data.sourceStartPosition;
50+
destinationStartPosition = data.destinationStartPosition;
51+
};
52+
53+
this.isEdit = true;
54+
this.group = undefined;
55+
56+
/**
57+
* @return {!ops.OpMergeList.Spec}
58+
*/
59+
this.spec = function () {
60+
return {
61+
optype: "MergeList",
62+
memberid: memberid,
63+
timestamp: timestamp,
64+
sourceStartPosition: sourceStartPosition,
65+
destinationStartPosition: destinationStartPosition
66+
};
67+
};
68+
69+
/**
70+
* @param {!ops.Document} document
71+
*/
72+
this.execute = function (document) {
73+
var odtDocument = /**@type{ops.OdtDocument}*/(document),
74+
rootNode = odtDocument.getRootNode(),
75+
sourceDomPosition = odtDocument.convertCursorStepToDomPoint(sourceStartPosition),
76+
destinationDomPosition = odtDocument.convertCursorStepToDomPoint(destinationStartPosition),
77+
sourceList,
78+
destinationList,
79+
childListItem;
80+
81+
sourceList = odfUtils.getTopLevelListElement(sourceDomPosition.node, rootNode);
82+
destinationList = odfUtils.getTopLevelListElement(destinationDomPosition.node, rootNode);
83+
childListItem = sourceList.firstElementChild;
84+
85+
while (childListItem) {
86+
destinationList.appendChild(childListItem);
87+
childListItem = sourceList.firstElementChild;
88+
}
89+
90+
sourceList.parentNode.removeChild(sourceList);
91+
return true;
92+
};
93+
};
94+
95+
/**@typedef{{
96+
optype: !string,
97+
memberid: !string,
98+
timestamp: !number,
99+
sourceStartPosition: !number,
100+
destinationStartPosition: !number
101+
}}*/
102+
ops.OpMergeList.Spec;
103+
104+
/**@typedef{{
105+
memberid: !string,
106+
timestamp:(!number|undefined),
107+
sourceStartPosition: !number,
108+
destinationStartPosition: !number
109+
}}*/
110+
ops.OpMergeList.InitSpec;

webodf/lib/ops/OpSplitList.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/**
2+
* Copyright (C) 2010-2014 KO GmbH <[email protected]>
3+
*
4+
* @licstart
5+
* This file is part of WebODF.
6+
*
7+
* WebODF is free software: you can redistribute it and/or modify it
8+
* under the terms of the GNU Affero General Public License (GNU AGPL)
9+
* as published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* WebODF is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
19+
* @licend
20+
*
21+
* @source: http://www.webodf.org/
22+
* @source: https://github.com/kogmbh/WebODF/
23+
*/
24+
25+
/*global ops, runtime, odf, core, Range*/
26+
27+
/**
28+
*
29+
* @constructor
30+
* @implements ops.Operation
31+
*/
32+
ops.OpSplitList = function OpSplitList() {
33+
"use strict";
34+
35+
var memberid,
36+
timestamp,
37+
/**@type{!number}*/
38+
sourceStartPosition,
39+
/**@type{!number}*/
40+
splitPosition,
41+
odfUtils = odf.OdfUtils,
42+
/**@const*/
43+
xmlns = odf.Namespaces.xmlns;
44+
45+
/**
46+
* @param {!ops.OpSplitList.InitSpec} data
47+
*/
48+
this.init = function (data) {
49+
memberid = data.memberid;
50+
timestamp = data.timestamp;
51+
sourceStartPosition = data.sourceStartPosition;
52+
splitPosition = data.splitPosition;
53+
};
54+
55+
this.isEdit = true;
56+
this.group = undefined;
57+
58+
/**
59+
* @return {!ops.OpSplitList.Spec}
60+
*/
61+
this.spec = function () {
62+
return {
63+
optype: "SplitList",
64+
memberid: memberid,
65+
timestamp: timestamp,
66+
sourceStartPosition: sourceStartPosition,
67+
splitPosition: splitPosition
68+
};
69+
};
70+
71+
/**
72+
* @param {!ops.Document} document
73+
*/
74+
this.execute = function (document) {
75+
var odtDocument = /**@type{ops.OdtDocument}*/(document),
76+
ownerDocument = odtDocument.getDOMDocument(),
77+
rootNode = odtDocument.getRootNode(),
78+
collapseRules = new odf.CollapsingRules(rootNode),
79+
sourceDomPosition = odtDocument.convertCursorStepToDomPoint(sourceStartPosition),
80+
splitDomPosition = odtDocument.convertCursorStepToDomPoint(splitPosition),
81+
sourceParagraph = /**@type{!Element}*/(odfUtils.getParagraphElement(sourceDomPosition.node, sourceDomPosition.offset)),
82+
splitParagraph = /**@type{!Element}*/(odfUtils.getParagraphElement(splitDomPosition.node, splitDomPosition.offset)),
83+
sourceList = odfUtils.getTopLevelListElement(sourceParagraph, rootNode),
84+
destinationList,
85+
splitPositionParentList,
86+
range = ownerDocument.createRange(),
87+
fragment;
88+
89+
if (!sourceList || !splitParagraph) {
90+
return false;
91+
}
92+
93+
destinationList = sourceList.cloneNode(false);
94+
destinationList.removeAttributeNS(xmlns, "id");
95+
96+
// create a range starting at before the list item element we split at and
97+
// ending at just after the last list item in the list
98+
range.setStartBefore(splitParagraph.parentNode);
99+
range.setEndAfter(sourceList.lastElementChild);
100+
101+
// extract the range to get all list items from the split position to the end of the list
102+
// then collapse any empty nodes at the parent text:list element of the paragraph at the split position
103+
splitPositionParentList = /**@type{!Node}*/(splitParagraph.parentNode.parentNode);
104+
fragment = range.extractContents();
105+
106+
// don't collapse nodes if its the top level list
107+
if (splitPositionParentList !== sourceList) {
108+
collapseRules.mergeChildrenIntoParent(splitPositionParentList);
109+
}
110+
111+
destinationList.appendChild(fragment);
112+
sourceList.parentNode.insertBefore(destinationList, sourceList.nextElementSibling);
113+
return true;
114+
};
115+
};
116+
117+
/**@typedef{{
118+
optype: !string,
119+
memberid: !string,
120+
timestamp: !number,
121+
sourceStartPosition: !number,
122+
splitPosition: !number
123+
}}*/
124+
ops.OpSplitList.Spec;
125+
126+
/**@typedef{{
127+
memberid: !string,
128+
timestamp:(number|undefined),
129+
sourceStartPosition: !number,
130+
splitPosition: !number
131+
}}*/
132+
ops.OpSplitList.InitSpec;

webodf/lib/ops/OperationFactory.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ ops.OperationFactory = function OperationFactory() {
103103
RemoveHyperlink: construct(ops.OpRemoveHyperlink),
104104
AddList: construct(ops.OpAddList),
105105
RemoveList: construct(ops.OpRemoveList),
106-
AddListStyle: construct(ops.OpAddListStyle)
106+
AddListStyle: construct(ops.OpAddListStyle),
107+
MergeList: construct(ops.OpMergeList),
108+
SplitList: construct(ops.OpSplitList)
107109
};
108110
}
109111

webodf/tests/ops/operationtests.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,4 +2205,66 @@
22052205
</ops>
22062206
<after><office:text><text:p><foreign:foo foreign:baz="bar"/>Sample Text</text:p></office:text></after>
22072207
</test>
2208+
<test name="MergeList_Simple">
2209+
<before>
2210+
<office:text>
2211+
<text:list>
2212+
<text:list-item>
2213+
<text:p>SampleText1</text:p>
2214+
</text:list-item>
2215+
</text:list>
2216+
<text:list>
2217+
<text:list-item>
2218+
<text:p>SampleText2</text:p>
2219+
</text:list-item>
2220+
</text:list>
2221+
</office:text>
2222+
</before>
2223+
<ops>
2224+
<op optype="MergeList" destinationStartPosition="0" sourceStartPosition="12" />
2225+
</ops>
2226+
<after>
2227+
<office:text>
2228+
<text:list>
2229+
<text:list-item>
2230+
<text:p>SampleText1</text:p>
2231+
</text:list-item>
2232+
<text:list-item>
2233+
<text:p>SampleText2</text:p>
2234+
</text:list-item>
2235+
</text:list>
2236+
</office:text>
2237+
</after>
2238+
</test>
2239+
<test name="SplitList_Simple">
2240+
<before>
2241+
<office:text>
2242+
<text:list>
2243+
<text:list-item>
2244+
<text:p>SampleText1</text:p>
2245+
</text:list-item>
2246+
<text:list-item>
2247+
<text:p>SampleText2</text:p>
2248+
</text:list-item>
2249+
</text:list>
2250+
</office:text>
2251+
</before>
2252+
<ops>
2253+
<op optype="SplitList" sourceStartPosition="0" splitPosition="12" />
2254+
</ops>
2255+
<after>
2256+
<office:text>
2257+
<text:list>
2258+
<text:list-item>
2259+
<text:p>SampleText1</text:p>
2260+
</text:list-item>
2261+
</text:list>
2262+
<text:list>
2263+
<text:list-item>
2264+
<text:p>SampleText2</text:p>
2265+
</text:list-item>
2266+
</text:list>
2267+
</office:text>
2268+
</after>
2269+
</test>
22082270
</tests>

webodf/tools/karma.conf.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ module.exports = function (config) {
9393
'lib/ops/OpInsertImage.js',
9494
'lib/ops/OpInsertTable.js',
9595
'lib/ops/OpInsertText.js',
96+
'lib/ops/OpMergeList.js',
9697
'lib/odf/CollapsingRules.js',
9798
'lib/ops/OpMergeParagraph.js',
9899
'lib/ops/OpMoveCursor.js',
@@ -106,6 +107,7 @@ module.exports = function (config) {
106107
'lib/ops/OpRemoveText.js',
107108
'lib/ops/OpSetBlob.js',
108109
'lib/ops/OpSetParagraphStyle.js',
110+
'lib/ops/OpSplitList.js',
109111
'lib/ops/OpSplitParagraph.js',
110112
'lib/ops/OpUpdateMember.js',
111113
'lib/ops/OpUpdateMetadata.js',

0 commit comments

Comments
 (0)