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