1 /**
  2  * Copyright (C) 2012-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, core, runtime, Node */
 26 
 27 /**
 28  * @constructor
 29  * @implements ops.Operation
 30  */
 31 ops.OpApplyHyperlink = function OpApplyHyperlink() {
 32     "use strict";
 33 
 34     var memberid, timestamp, position, length, hyperlink,
 35         domUtils = core.DomUtils,
 36         odfUtils = odf.OdfUtils;
 37 
 38     /**
 39      * @param {!ops.OpApplyHyperlink.InitSpec} data
 40      */
 41     this.init = function (data) {
 42         memberid = data.memberid;
 43         timestamp = data.timestamp;
 44         position = data.position;
 45         length = data.length;
 46         hyperlink = data.hyperlink;
 47     };
 48 
 49     this.isEdit = true;
 50     this.group = undefined;
 51 
 52     /**
 53      * @param {!Document} document
 54      * @param {string} hyperlink
 55      * @return {!Element}
 56      */
 57     function createHyperlink(document, hyperlink) {
 58         var node = document.createElementNS(odf.Namespaces.textns, 'text:a');
 59         node.setAttributeNS(odf.Namespaces.xlinkns, 'xlink:type', 'simple');
 60         node.setAttributeNS(odf.Namespaces.xlinkns, 'xlink:href', hyperlink);
 61         return node;
 62     }
 63 
 64     /**
 65      * @param {?Node} node
 66      * @return {boolean}
 67      */
 68     function isPartOfLink(node) {
 69         while (node) {
 70             if (odfUtils.isHyperlink(node)) {
 71                 return true;
 72             }
 73             node = node.parentNode;
 74         }
 75         return false;
 76     }
 77 
 78     /**
 79      * TODO: support adding image link
 80      * @param {!ops.Document} document
 81      */
 82     this.execute = function (document) {
 83         var odtDocument = /**@type{ops.OdtDocument}*/(document),
 84             ownerDocument = odtDocument.getDOMDocument(),
 85             range = odtDocument.convertCursorToDomRange(position, length),
 86             boundaryNodes = domUtils.splitBoundaries(range),
 87             /**@type{!Array.<!Element>}*/
 88             modifiedParagraphs = [],
 89             textNodes = odfUtils.getTextNodes(range, false);
 90 
 91         if (textNodes.length === 0) {
 92             return false;
 93         }
 94 
 95         textNodes.forEach(function (node) {
 96             var linkNode,
 97                 paragraph = odfUtils.getParagraphElement(node);
 98             runtime.assert(isPartOfLink(node) === false, "The given range should not contain any link.");
 99             // TODO there could be a small efficiency gain by merging adjacent text nodes into a single hyperlink parent.
100             // This is probably only a corner case however and there are not likely to be major repercussions for not doing it
101             linkNode = createHyperlink(ownerDocument, hyperlink);
102             node.parentNode.insertBefore(linkNode, node);
103             linkNode.appendChild(node);
104             if (modifiedParagraphs.indexOf(paragraph) === -1) {
105                 modifiedParagraphs.push(paragraph);
106             }
107         });
108 
109 
110         boundaryNodes.forEach(domUtils.normalizeTextNodes);
111         range.detach();
112 
113         odtDocument.fixCursorPositions();
114         odtDocument.getOdfCanvas().refreshSize();
115         odtDocument.getOdfCanvas().rerenderAnnotations();
116         modifiedParagraphs.forEach(function (paragraph) {
117             odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {
118                 paragraphElement: paragraph,
119                 memberId: memberid,
120                 timeStamp: timestamp
121             });
122         });
123 
124         return true;
125     };
126 
127     /**
128      * @return {!ops.OpApplyHyperlink.Spec}
129      */
130     this.spec = function () {
131         return {
132             optype: "ApplyHyperlink",
133             memberid: memberid,
134             timestamp: timestamp,
135             position: position,
136             length: length,
137             hyperlink: hyperlink
138         };
139     };
140 };
141 /**@typedef{{
142     optype:string,
143     memberid:string,
144     timestamp:number,
145     position:number,
146     length:number,
147     hyperlink:string
148 }}*/
149 ops.OpApplyHyperlink.Spec;
150 /**@typedef{{
151     memberid:string,
152     timestamp:(number|undefined),
153     position:number,
154     length:number,
155     hyperlink:string
156 }}*/
157 ops.OpApplyHyperlink.InitSpec;
158