Skip to content
Browse files

adding etherpad document type

  • Loading branch information...
1 parent c1b3c54 commit e654aee3266ee02b440712df31fac6320e60103d @jucovschi committed
Showing with 2,608 additions and 0 deletions.
  1. +112 −0 src/types/AttributePool.js
  2. +2,261 −0 src/types/Changeset.js
  3. +120 −0 src/types/etherpad-api.coffee
  4. +115 −0 src/types/etherpad.coffee
View
112 src/types/AttributePool.js
@@ -0,0 +1,112 @@
+/**
@wmertens
wmertens added a note

Not sure about this. Obviously it's a lot of work to convert this to coffeescript. Maybe this should be in a lib dir somewhere? It's not really code you'll change right?

@jucovschi Owner

No I will not change any of these libraries and so I'd rather put them in some lib directory. In the end there should be a nice way of using legacy JS code. BTW: I really do not like how I solved the problem of getting the Changeset library available in the browser (the window.ShareJS.Changeset). Are you ok with it?

@wmertens
wmertens added a note

@josephg your call :-)

@josephg
josephg added a note

... I don't mind, so long as someone's happy to maintain this code. My biggest concern is the lack of tests for any of this code.

@wmertens
wmertens added a note

Ok so how about putting the etherpad js under src/lib-etherpad and having just the coffeescript in src/types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ * This code represents the Attribute Pool Object of the original Etherpad.
+ * 90% of the code is still like in the original Etherpad
+ * Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
+ * You can find a explanation what a attribute pool is here:
+ * https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
+ */
+
+/*
+ * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ An AttributePool maintains a mapping from [key,value] Pairs called
+ Attributes to Numbers (unsigened integers) and vice versa. These numbers are
+ used to reference Attributes in Changesets.
+*/
+
+var AttributePool = function () {
+ this.numToAttrib = {}; // e.g. {0: ['foo','bar']}
+ this.attribToNum = {}; // e.g. {'foo,bar': 0}
+ this.nextNum = 0;
+};
+
+AttributePool.prototype.putAttrib = function (attrib, dontAddIfAbsent) {
+ var str = String(attrib);
+ if (str in this.attribToNum) {
+ return this.attribToNum[str];
+ }
+ if (dontAddIfAbsent) {
+ return -1;
+ }
+ var num = this.nextNum++;
+ this.attribToNum[str] = num;
+ this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
+ return num;
+};
+
+AttributePool.prototype.getAttrib = function (num) {
+ var pair = this.numToAttrib[num];
+ if (!pair) {
+ return pair;
+ }
+ return [pair[0], pair[1]]; // return a mutable copy
+};
+
+AttributePool.prototype.getAttribKey = function (num) {
+ var pair = this.numToAttrib[num];
+ if (!pair) return '';
+ return pair[0];
+};
+
+AttributePool.prototype.clone = function() {
+ var result = new AttributePool();
+ result.numToAttrib = this.numToAttrib;
+ result.attribToNum = this.attribToNum;
+ result.nextNum = this.nextNum;
+ return result;
+}
+
+AttributePool.prototype.getAttribValue = function (num) {
+ var pair = this.numToAttrib[num];
+ if (!pair) return '';
+ return pair[1];
+};
+
+AttributePool.prototype.eachAttrib = function (func) {
+ for (var n in this.numToAttrib) {
+ var pair = this.numToAttrib[n];
+ func(pair[0], pair[1]);
+ }
+};
+
+AttributePool.prototype.toJsonable = function () {
+ return {
+ numToAttrib: this.numToAttrib,
+ nextNum: this.nextNum
+ };
+};
+
+AttributePool.prototype.fromJsonable = function (obj) {
+ this.numToAttrib = obj.numToAttrib;
+ this.nextNum = obj.nextNum;
+ this.attribToNum = {};
+ for (var n in this.numToAttrib) {
+ this.attribToNum[String(this.numToAttrib[n])] = Number(n);
+ }
+ return this;
+};
+
+if (typeof window !== "undefined") {
+ if (typeof window.ShareJS === "undefined") {
+ window.ShareJS = {};
+ }
+ window.ShareJS.AttributePool = AttributePool
+} else {
+ exports.AttributePool = AttributePool;
+ module.exports = AttributePool;
+}
+
View
2,261 src/types/Changeset.js
2,261 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
120 src/types/etherpad-api.coffee
@@ -0,0 +1,120 @@
+# Text document API for text
+# :tabSize=4:indentSize=4:
+
+if WEB?
+ Changeset = window.ShareJS.Changeset
+ AttributePool = window.ShareJS.AttributePool
+else
+ etherpad = require './etherpad'
+ AttributePool = require './AttributePool'
+ Changeset = require './Changeset'
+
+etherpad.api =
+ provides: {text:true}
+
+ # The number of characters in the string
+ getLength: -> @snapshot.text.length
+
+ # Get the text contents of a document
+ getText: -> @snapshot.text
+
+ getInteraction: (state) ->
+ state.forward = true
+ this.connection.send({
+ doc: this.name,
+ meta: state
+ });
@wmertens
wmertens added a note

There's a lof of JS-isms in this code. For example, this could be written instead as:

  @connection.send
    doc: @name
    meta: state

Could you fix these to adhere to the style of the project?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ # Get metadata starting from offset startOffset and having length length
+ getMeta: (startOffset, length) ->
+ if (typeof @snapshot.pool.getAttrib == "undefined")
+ @snapshot = etherpad.tryDeserializeSnapshot(@snapshot);
+ snapshot = @snapshot;
+ iter = Changeset.opIterator(snapshot.attribs)
+ offset = 0;
+ result = [];
+ rangeStart = Changeset.numToString(@snapshot.pool.putAttrib(["range.start",1], true));
+ rangeEnd = Changeset.numToString(@snapshot.pool.putAttrib(["range.end",1], true));
+ rangeProps = [];
+ inRange = false;
+ clearRange = false
+ while iter.hasNext()
+ o = iter.next()
+ if (o.opcode=='-')
+ continue;
+ if (clearRange)
+ rangeProps = "";
+ inRange = true;
+ clearRange = false;
+ # range is started
+ if (o.attribs.match("\\*"+rangeStart))
+ rangeProps = o.attribs;
+ inRange = true;
+ # range finishes but needs to take effect starting from next token
+ if (o.attribs.match("\\*"+rangeEnd))
+ clearRange = true;
+
+ if (offset + o.chars < startOffset)
+ offset = offset + o.chars;
+ continue;
+ if (offset > startOffset + length)
+ break;
+
+ if (inRange)
+ att = rangeProps;
+ valList = [];
+ att.replace(/\*([0-9a-zA-Z]+)/g, (match...) ->
+ attid = Changeset.parseNum(match[1]);
+ att = snapshot.pool.getAttrib(attid);
+ valList.push({
+ key:att[0],
+ value:att[1]
+ });
+ )
+ if (valList.length>0)
+ result.push({
+ start: Math.max(startOffset, offset),
+ end: Math.min(startOffset+length, offset + o.chars),
+ attributes: valList,
+ })
+ offset = offset + o.chars
+ return result
+
+ insert: (pos, text, callback) ->
+ result = {};
+ result.pool = new AttributePool();
+ result.changeset = Changeset.builder(@snapshot.text.length)
+ .keep(pos,0).insert(text, "", result.pool).toString()
+ @submitOp result, callback
+ result
+
+ del: (pos, length, callback) ->
+ result = {};
+ result.pool = new AttributePool()
+ result.changeset = Changeset.builder(@snapshot.text.length).keep(pos,0).remove(length,0).toString()
+ @submitOp result, callback
+ result
+
+ _register: ->
+ @on 'remoteop', (op) ->
+ unpacked = Changeset.unpack(op.changeset);
+ iter = Changeset.opIterator(unpacked.ops)
+ strIter = Changeset.stringIterator(unpacked.charBank);
+ offset = 0;
+ refreshFirstOffset = 10000000;
+ refreshLastOffset = -1;
+ while iter.hasNext()
+ o = iter.next()
+ switch (o.opcode)
+ when '+'
+ @emit 'insert', offset, strIter.take(o.chars);
+ offset = offset + o.chars
+ when '-'
+ @emit 'delete', offset, { length: o.chars }
+ when '='
+ if o.attribs.length > 0
+ refreshFirstOffset = Math.min(offset, refreshFirstOffset);
+ refreshLastOffset = Math.max(offset + o.chars, refreshLastOffset);
+ offset = offset + o.chars
+ #if (refreshLastOffset > 0)
+ # @emit 'refresh', refreshFirstOffset, refreshLastOffset - refreshFirstOffset
View
115 src/types/etherpad.coffee
@@ -0,0 +1,115 @@
+# This is the type for etherpad changesets
+# The snapshot has a JSON structure of
+# {
+# "text" - the text of the pad
+# "attribs" - attributes
+# "pool" - the attribute pool
+# }
+
+# The Changesets have the structure
+# {
+# "changeset" - serialized version of the changeset
+# "pool" - the pool
+# }
+
+if WEB?
+ Changeset = window.ShareJS.Changeset
+ AttributePool = window.ShareJS.AttributePool
+else
+ Changeset = require("./Changeset");
+ AttributePool = require("./AttributePool");
+
+etherpad = {}
+
+etherpad.name = "etherpad"
+etherpad.create = ->
+ {
+ "text" : "",
+ "attribs" : Changeset.makeAttribution(""),
+ "pool" : new AttributePool()
+ }
+
+etherpad.tryDeserializeSnapshot = (snapshot) ->
+ if (snapshot.pool.clone)
+ return snapshot
+ snapshot.pool = new AttributePool().fromJsonable(snapshot.pool)
+ return snapshot;
+
+etherpad.tryDeserializeOp = (op) ->
+ if (op.pool.clone)
+ return op;
+ if (op.pool.numToAttrib)
+ op.pool = new AttributePool().fromJsonable(op.pool)
+ else
+ op.pool = new AttributePool().fromJsonable(JSON.parse(op.pool))
+ return op
+
+etherpad.apply = (snapshot, op) ->
+ snapshot = etherpad.tryDeserializeSnapshot(snapshot)
+ op = etherpad.tryDeserializeOp(op)
+ result = {}
+ result.pool = snapshot.pool.clone();
+
+ newCS = Changeset.moveOpsToNewPool(op.changeset, op.pool, result.pool);
+ result.text = Changeset.applyToText(newCS, snapshot.text);
+ console.log(newCS);
+ result.attribs = Changeset.applyToAttribution(newCS, snapshot.attribs, result.pool);
+ return result
+
+etherpad.transform = (op1, op2, side) ->
+ op1 = etherpad.tryDeserializeOp(op1)
+ op2 = etherpad.tryDeserializeOp(op2)
+ result = {}
+ # join the operation pools into a new one
+ newPool = op1.pool.clone();
+ # newPool will hold the combined pool
+ # op2cs will hold the rewritten op2 cs
+ op2cs = Changeset.moveOpsToNewPool(op2.changeset, op2.pool, newPool);
+ result.changeset = Changeset.follow(op1.changeset, op2cs, side=="right", newPool);
+ result.pool = newPool
+ return result
+
+etherpad.compose = (op1, op2) ->
+ op1 = etherpad.tryDeserializeOp(op1)
+ op2 = etherpad.tryDeserializeOp(op2)
+ result = {}
+ # join the operation pools into a new one
+ newPool = op1.pool.clone();
+ # newPool will hold the combined pool
+ # op2cs will hold the rewritten op2 cs
+ op2cs = Changeset.moveOpsToNewPool(op2.changeset, op2.pool, newPool);
+ result.changeset = Changeset.compose(op1.changeset, op2cs, newPool);
+ result.pool = newPool
+ return result
+
+etherpad.serialize = (snapshot) ->
+ result = {}
+ result.text = snapshot.text
+ result.attribs = snapshot.attribs
+ result.pool = snapshot.pool.toJsonable()
+ return result
@wmertens
wmertens added a note

Likewise:

etherpad.serialize = (snapshot) ->
  text: snapshot.text
  attribs: snapshot.attribs
  pool: snapshot.pool.toJsonable()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+etherpad.serializeOp = (snapshot) ->
+ result = {}
+ result.changeset = snapshot.changeset
+ result.pool = JSON.stringify(snapshot.pool)
+ return result
+
+
+etherpad.deserialize = (obj) ->
+ result = {}
+ result.text = obj.text
+ result.attribs = obj.attribs
+ result.pool = new AttributePool().fromJsonable(obj.pool)
+ return result
+
+if WEB?
+ exports.types ||= {}
+
+ # [] is used to prevent closure from renaming types.text
+ exports.types.etherpad = etherpad
+else
+ module.exports = etherpad
+
+ # require('./helpers').bootstrapTransform(json, json.transformComponent, json.checkValidOp, json.append)
+

0 comments on commit e654aee

Please sign in to comment.
Something went wrong with that request. Please try again.