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 core, gui, runtime*/ 26 27 /** 28 * @constructor 29 * @implements {core.Destroyable} 30 * @param {!ops.EditInfo} editInfo marker takes ownership 31 * @param {boolean} initialVisibility Sets the initial edit info marker visibility 32 */ 33 gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) { 34 "use strict"; 35 36 var self = this, 37 /**@type{!Element}*/ 38 editInfoNode, 39 /**@type{!gui.EditInfoHandle}*/ 40 handle, 41 /**@type{!HTMLDivElement}*/ 42 marker, 43 editinfons = 'urn:webodf:names:editinfo', 44 decayTimer0, 45 decayTimer1, 46 decayTimer2, 47 decayTimeStep = 10000; // 10 seconds 48 49 /** 50 * Runs and returns a timer that sets the marker's opacity 51 * to the specified value after the specified delay. 52 * @param {!number} opacity 53 * @param {!number} delay 54 * @return {!number} 55 */ 56 function applyDecay(opacity, delay) { 57 return runtime.setTimeout(function () { 58 marker.style.opacity = opacity; 59 }, delay); 60 } 61 62 /** 63 * Stops the specified timer 64 * @param {!number} timerId 65 */ 66 function deleteDecay(timerId) { 67 runtime.clearTimeout(timerId); 68 } 69 70 /** 71 * @param {!string} memberid 72 */ 73 function setLastAuthor(memberid) { 74 marker.setAttributeNS(editinfons, 'editinfo:memberid', memberid); 75 } 76 77 /** 78 * @param {!string} memberid 79 * @param {!Date} timestamp 80 */ 81 this.addEdit = function (memberid, timestamp) { 82 var age = (Date.now() - timestamp); 83 84 editInfo.addEdit(memberid, timestamp); 85 handle.setEdits(editInfo.getSortedEdits()); 86 setLastAuthor(memberid); 87 88 // Since a new edit has arrived, stop decaying for the old edits 89 deleteDecay(decayTimer1); 90 deleteDecay(decayTimer2); 91 92 // Decide the decay path: 93 // this decides the initial opacity and subsequent decays to apply 94 // depending on the age of the edit (for example the edit might have arrived 95 // here a long time after it was added to the master session. We don't want 96 // an opaque marker in that case, we would want it to start with a lower opacity 97 // and decay accordingly further, if possible. 98 if (age < decayTimeStep) { 99 decayTimer0 = applyDecay(1, 0); 100 decayTimer1 = applyDecay(0.5, decayTimeStep - age); 101 decayTimer2 = applyDecay(0.2, decayTimeStep * 2 - age); 102 } else if (age >= decayTimeStep && age < decayTimeStep * 2) { 103 decayTimer0 = applyDecay(0.5, 0); 104 decayTimer2 = applyDecay(0.2, decayTimeStep * 2 - age); 105 } else { 106 decayTimer0 = applyDecay(0.2, 0); 107 } 108 }; 109 this.getEdits = function () { 110 return editInfo.getEdits(); 111 }; 112 this.clearEdits = function () { 113 editInfo.clearEdits(); 114 handle.setEdits([]); 115 if (marker.hasAttributeNS(editinfons, 'editinfo:memberid')) { 116 marker.removeAttributeNS(editinfons, 'editinfo:memberid'); 117 } 118 }; 119 this.getEditInfo = function () { 120 return editInfo; 121 }; 122 123 /** 124 * Shows the edit information marker 125 */ 126 this.show = function () { 127 marker.style.display = 'block'; 128 }; 129 130 /** 131 * Hides the edit information marker 132 */ 133 this.hide = function () { 134 self.hideHandle(); 135 // edit decays are not cleared as the marker should be properly 136 // faded if it is re-shown 137 marker.style.display = 'none'; 138 }; 139 140 this.showHandle = function () { 141 handle.show(); 142 }; 143 144 this.hideHandle = function () { 145 handle.hide(); 146 }; 147 148 /** 149 * @param {!function(!Error=)} callback, passing an error object in case of error 150 * @return {undefined} 151 */ 152 this.destroy = function (callback) { 153 deleteDecay(decayTimer0); 154 deleteDecay(decayTimer1); 155 deleteDecay(decayTimer2); 156 editInfoNode.removeChild(marker); 157 handle.destroy(function (err) { 158 if (err) { 159 callback(err); 160 } else { 161 editInfo.destroy(callback); 162 } 163 }); 164 }; 165 166 function init() { 167 var dom = editInfo.getOdtDocument().getDOMDocument(), 168 htmlns = dom.documentElement.namespaceURI; 169 170 marker = /**@type{!HTMLDivElement}*/(dom.createElementNS(htmlns, "div")); 171 marker.setAttribute("class", "editInfoMarker"); 172 173 marker.onmouseover = function () { 174 self.showHandle(); 175 }; 176 marker.onmouseout = function () { 177 self.hideHandle(); 178 }; 179 180 editInfoNode = editInfo.getNode(); 181 editInfoNode.appendChild(marker); 182 handle = new gui.EditInfoHandle(editInfoNode); 183 if (!initialVisibility) { 184 self.hide(); 185 } 186 } 187 188 init(); 189 }; 190