1 /** 2 * 2011 Peter 'Pita' Martischka 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS-IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 The Pad Module trys to simulate the pad object from EtherPad. You can find the original code in /etherpad/src/etherpad/pad/model.js 19 see https://github.com/ether/pad/blob/master/etherpad/src/etherpad/pad/model.js 20 */ 21 22 var Changeset = require("./Changeset"); 23 var AttributePoolFactory = require("./AttributePoolFactory"); 24 25 /** 26 * The initial Text of a Pad 27 */ 28 exports.startText = "Hello World\nGoodbye Etherpad"; 29 30 /** 31 * A Array with all known Pads 32 */ 33 globalPads = []; 34 35 /** 36 * Return a Function Wrapper to work with the Pad 37 * @param id A String with the id of the pad 38 * @param createIfNotExist A Boolean which says the function if it should create the Pad if it not exist 39 */ 40 exports.getPad = function(id, createIfNotExist) 41 { 42 if(!globalPads[id] && createIfNotExist == true) 43 { 44 createPad(id); 45 } 46 47 if(!globalPads[id]) 48 return null; 49 50 globalPads[id].timestamp = new Date().getTime(); 51 52 var functionWrapper = {}; 53 54 functionWrapper.id = id; 55 functionWrapper.appendRevision = function (theChangeset, author) {return appendRevision(id, theChangeset, author)}; 56 functionWrapper.text = function () {return text(id)}; 57 functionWrapper.atext = function () {return atext(id)}; 58 functionWrapper.pool = function () {return pool(id)}; 59 functionWrapper.getHeadRevisionNumber = function () {return getHeadRevisionNumber(id)}; 60 functionWrapper.getRevisionChangeset = function (revNum) {return getRevisionChangeset(id, revNum)}; 61 functionWrapper.getRevisionAuthor = function (revNum) {return getRevisionAuthor(id, revNum)}; 62 functionWrapper.getAllAuthors = function () {return getAllAuthors(id)}; 63 64 return functionWrapper; 65 } 66 67 /** 68 * Ensures that the Pad exists 69 * @param id The Pad id 70 */ 71 exports.ensurePadExists = function(id) 72 { 73 if(!globalPads[id]) 74 { 75 createPad(id); 76 } 77 } 78 79 /** 80 * Creates an empty pad 81 * @param id The Pad id 82 */ 83 function createPad(id) 84 { 85 var pad = {}; 86 globalPads[id] = pad; 87 88 pad.id = id; 89 pad.rev = []; 90 pad.head = -1; 91 pad.atext = Changeset.makeAText("\n"); 92 pad.apool = AttributePoolFactory.createAttributePool(); 93 pad.authors = []; 94 95 var firstChangeset = Changeset.makeSplice("\n", 0, 0, 96 exports.cleanText(exports.startText)); 97 appendRevision(id, firstChangeset, ''); 98 } 99 100 /** 101 * Append a changeset to a pad 102 * @param id The Pad id 103 * @param theChangeset the changeset which should apply to the text 104 * @param The author of the revision, can be null 105 */ 106 function appendRevision(id, theChangeset, author) 107 { 108 throwExceptionIfPadDontExist(id); 109 110 if(!author) 111 author = ''; 112 113 var atext = globalPads[id].atext; 114 var apool = globalPads[id].apool; 115 var newAText = Changeset.applyToAText(theChangeset, atext, apool); 116 Changeset.copyAText(newAText, atext); 117 118 var newRev = ++globalPads[id].head; 119 globalPads[id].rev[newRev] = {}; 120 globalPads[id].rev[newRev].changeset = theChangeset; 121 globalPads[id].rev[newRev].meta = {}; 122 globalPads[id].rev[newRev].meta.author = author; 123 globalPads[id].rev[newRev].meta.timestamp = new Date().getTime(); 124 125 //ex. getNumForAuthor 126 apool.putAttrib(['author',author||'']); 127 128 if(newRev%100==0) 129 { 130 globalPads[id].rev[newRev].meta.atext=atext; 131 } 132 } 133 134 /** 135 * Returns all Authors of a Pad 136 * @param id The Pad id 137 */ 138 function getAllAuthors(id) 139 { 140 var authors = []; 141 142 for(key in globalPads[id].apool.numToAttrib) 143 { 144 if(globalPads[id].apool.numToAttrib[key][0] == "author" && globalPads[id].apool.numToAttrib[key][1] != "") 145 { 146 authors.push(globalPads[id].apool.numToAttrib[key][1]); 147 } 148 } 149 150 return authors; 151 } 152 153 /** 154 * Returns the plain text of a pad 155 * @param id The Pad id 156 */ 157 158 function text(id) 159 { 160 throwExceptionIfPadDontExist(id); 161 162 return globalPads[id].atext.text; 163 } 164 165 /** 166 * Returns the Attributed Text of a pad 167 * @param id The Pad id 168 */ 169 function atext(id) 170 { 171 throwExceptionIfPadDontExist(id); 172 173 return globalPads[id].atext; 174 } 175 176 /** 177 * Returns the Attribute Pool whichs the Pad is using 178 * @param id The Pad id 179 */ 180 function pool(id) 181 { 182 throwExceptionIfPadDontExist(id); 183 184 return globalPads[id].apool; 185 } 186 187 /** 188 * Returns the latest Revision Number of the Pad 189 * @param id The Pad id 190 */ 191 function getHeadRevisionNumber(id) 192 { 193 throwExceptionIfPadDontExist(id); 194 195 return globalPads[id].head; 196 } 197 198 /** 199 * Returns the changeset of a specific revision 200 * @param id The Pad id 201 * @param revNum The Revision Number 202 */ 203 function getRevisionChangeset(id, revNum) 204 { 205 throwExceptionIfPadDontExist(id); 206 throwExceptionIfRevDontExist(id, revNum); 207 208 return globalPads[id].rev[revNum].changeset; 209 } 210 211 /** 212 * Returns the author of a specific revision 213 * @param id The Pad id 214 * @param revNum The Revision Number 215 */ 216 function getRevisionAuthor(id, revNum) 217 { 218 throwExceptionIfPadDontExist(id); 219 throwExceptionIfRevDontExist(id, revNum); 220 221 return globalPads[id].rev[revNum].meta.author; 222 } 223 224 /** 225 * Check if the ID is a valid Pad ID and trows an Exeption if not 226 * @param id The Pad id 227 */ 228 function throwExceptionIfPadDontExist(id) 229 { 230 if(id == null) 231 { 232 throw "Padname is null!"; 233 } 234 if(!globalPads[id]) 235 { 236 throw "Pad don't exist!'"; 237 } 238 } 239 240 /** 241 * Check if the Revision of a Pad is valid and throws an Exeption if not 242 * @param id The Pad id 243 */ 244 function throwExceptionIfRevDontExist(id, revNum) 245 { 246 if(revNum == null) 247 throw "revNum is null"; 248 249 if((typeof revNum) != "number") 250 throw revNum + " is no Number"; 251 252 if(revNum < 0 || revNum > globalPads[id].head) 253 throw "The Revision " + revNum + " don't exist'"; 254 } 255 256 /** 257 * Copied from the Etherpad source code, don't know what its good for 258 * @param txt 259 */ 260 exports.cleanText = function (txt) { 261 return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' '); 262 } 263 264 265