1 /**
  2  * Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
  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, odf, runtime*/
 26 
 27 /**
 28  * @constructor
 29  * @implements ops.Operation
 30  */
 31 ops.OpInsertTable = function OpInsertTable() {
 32     "use strict";
 33 
 34     var memberid, timestamp, initialRows, initialColumns, position, tableName, tableStyleName,
 35         tableColumnStyleName,
 36         /**@type{!Array.<!Array.<string>>}*/
 37         tableCellStyleMatrix,
 38         /**@const*/
 39         tablens = "urn:oasis:names:tc:opendocument:xmlns:table:1.0",
 40         /**@const*/
 41         textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
 42         odfUtils = odf.OdfUtils;
 43 
 44     /**
 45      * @param {!ops.OpInsertTable.InitSpec} data
 46      */
 47     this.init = function (data) {
 48         memberid = data.memberid;
 49         timestamp = data.timestamp;
 50         position = data.position;
 51         initialRows = data.initialRows;
 52         initialColumns = data.initialColumns;
 53         tableName = data.tableName;
 54         tableStyleName = data.tableStyleName;
 55         tableColumnStyleName = data.tableColumnStyleName;
 56         tableCellStyleMatrix = data.tableCellStyleMatrix;
 57     };
 58 
 59     this.isEdit = true;
 60     this.group = undefined;
 61 
 62     /**
 63      * @param {!number} row
 64      * @param {!number} column
 65      * @return {!string|undefined}
 66      */
 67     function getCellStyleName(row, column) {
 68         var rowStyles;
 69         if (tableCellStyleMatrix.length === 1) {
 70             rowStyles = tableCellStyleMatrix[0];
 71         } else if (tableCellStyleMatrix.length === 3) {
 72             switch (row) {
 73             case 0:
 74                 rowStyles = tableCellStyleMatrix[0];
 75                 break;
 76             case initialRows - 1:
 77                 rowStyles = tableCellStyleMatrix[2];
 78                 break;
 79             default:
 80                 rowStyles = tableCellStyleMatrix[1];
 81                 break;
 82             }
 83         } else {
 84             rowStyles = tableCellStyleMatrix[row];
 85         }
 86 
 87         if (rowStyles.length === 1) {
 88             return rowStyles[0];
 89         }
 90         if (rowStyles.length === 3) {
 91             switch (column) {
 92             case 0:
 93                 return rowStyles[0];
 94             case initialColumns - 1:
 95                 return rowStyles[2];
 96             default:
 97                 return rowStyles[1];
 98             }
 99         }
100         return rowStyles[column];
101     }
102 
103     /**
104      * @param {!Document} document
105      * @return {!Element}
106      */
107     function createTableNode(document) {
108         var tableNode = document.createElementNS(tablens, "table:table"),
109             columns = document.createElementNS(tablens, "table:table-column"),
110             row, cell, paragraph,
111             rowCounter, columnCounter, cellStyleName;
112 
113         if (tableStyleName) {
114             tableNode.setAttributeNS(tablens, "table:style-name", tableStyleName);
115         }
116         if (tableName) {
117             tableNode.setAttributeNS(tablens, "table:name", tableName);
118         }
119 
120         columns.setAttributeNS(tablens, "table:number-columns-repeated", initialColumns);
121         if (tableColumnStyleName) {
122             columns.setAttributeNS(tablens, "table:style-name", tableColumnStyleName);
123         }
124 
125         tableNode.appendChild(columns);
126         for (rowCounter = 0; rowCounter < initialRows; rowCounter += 1) {
127             row = document.createElementNS(tablens, "table:table-row");
128             for (columnCounter = 0; columnCounter < initialColumns; columnCounter += 1) {
129                 cell = document.createElementNS(tablens, "table:table-cell");
130                 cellStyleName = getCellStyleName(rowCounter, columnCounter);
131                 if (cellStyleName) {
132                     cell.setAttributeNS(tablens, "table:style-name", cellStyleName);
133                 }
134                 paragraph = document.createElementNS(textns, "text:p");
135                 cell.appendChild(paragraph);
136                 row.appendChild(cell);
137             }
138             tableNode.appendChild(row);
139         }
140         return tableNode;
141     }
142 
143     /**
144      * @param {!ops.Document} document
145      */
146     this.execute = function (document) {
147         var odtDocument = /**@type{ops.OdtDocument}*/(document),
148             domPosition = odtDocument.getTextNodeAtStep(position),
149             rootNode = odtDocument.getRootNode(),
150             previousSibling,
151             tableNode;
152 
153         if (domPosition) {
154             tableNode = createTableNode(odtDocument.getDOMDocument());
155             // For now assume the table should be inserted after the current paragraph
156             // or failing that, as the first element in the root node
157             previousSibling = odfUtils.getParagraphElement(domPosition.textNode);
158             rootNode.insertBefore(tableNode, previousSibling.nextSibling);
159             // The parent table counts for 1 position, and 1 paragraph is added per cell
160             odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position: position});
161 
162             odtDocument.getOdfCanvas().refreshSize();
163             odtDocument.emit(ops.OdtDocument.signalTableAdded, {
164                 tableElement: tableNode,
165                 memberId: memberid,
166                 timeStamp: timestamp
167             });
168 
169             odtDocument.getOdfCanvas().rerenderAnnotations();
170             return true;
171         }
172         return false;
173     };
174 
175     /**
176      * @return {!ops.OpInsertTable.Spec}
177      */
178     this.spec = function () {
179         return {
180             optype: "InsertTable",
181             memberid: memberid,
182             timestamp: timestamp,
183             position: position,
184             initialRows: initialRows,
185             initialColumns: initialColumns,
186             tableName: tableName,
187             tableStyleName: tableStyleName,
188             tableColumnStyleName: tableColumnStyleName,
189             tableCellStyleMatrix: tableCellStyleMatrix
190         };
191     };
192 };
193 /**@typedef{{
194     optype:string,
195     memberid:string,
196     timestamp:number,
197     position:number,
198     initialRows:number,
199     initialColumns:number,
200     tableName:string,
201     tableStyleName:string,
202     tableColumnStyleName:string,
203     tableCellStyleMatrix:!Array.<!Array.<string>>
204 }}*/
205 ops.OpInsertTable.Spec;
206 /**@typedef{{
207     memberid:string,
208     timestamp:(number|undefined),
209     position:number,
210     initialRows:number,
211     initialColumns:number,
212     tableName:string,
213     tableStyleName:string,
214     tableColumnStyleName:string,
215     tableCellStyleMatrix:!Array.<!Array.<string>>
216 }}*/
217 ops.OpInsertTable.InitSpec;
218