Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save adelriosantiago/f4838efa52f67e54825c9e4f7fdb26b6 to your computer and use it in GitHub Desktop.
Save adelriosantiago/f4838efa52f67e54825c9e4f7fdb26b6 to your computer and use it in GitHub Desktop.
SO question libraries
//Fast-diff library (https://github.com/jhchen/fast-diff)
var DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;function diff_main(n,r,t){if(n==r)return n?[[DIFF_EQUAL,n]]:[];(t<0||n.length<t)&&(t=null);var i=diff_commonPrefix(n,r),e=n.substring(0,i);i=diff_commonSuffix(n=n.substring(i),r=r.substring(i));var f=n.substring(n.length-i),s=diff_compute_(n=n.substring(0,n.length-i),r=r.substring(0,r.length-i));return e&&s.unshift([DIFF_EQUAL,e]),f&&s.push([DIFF_EQUAL,f]),diff_cleanupMerge(s),null!=t&&(s=fix_cursor(s,t)),s=fix_emoji(s)}function diff_compute_(n,r){var t;if(!n)return[[DIFF_INSERT,r]];if(!r)return[[DIFF_DELETE,n]];var i=n.length>r.length?n:r,e=n.length>r.length?r:n,f=i.indexOf(e);if(-1!=f)return t=[[DIFF_INSERT,i.substring(0,f)],[DIFF_EQUAL,e],[DIFF_INSERT,i.substring(f+e.length)]],n.length>r.length&&(t[0][0]=t[2][0]=DIFF_DELETE),t;if(1==e.length)return[[DIFF_DELETE,n],[DIFF_INSERT,r]];var s=diff_halfMatch_(n,r);if(s){var l=s[0],u=s[1],g=s[2],h=s[3],_=s[4],c=diff_main(l,g),F=diff_main(u,h);return c.concat([[DIFF_EQUAL,_]],F)}return diff_bisect_(n,r)}function diff_bisect_(n,r){for(var t=n.length,i=r.length,e=Math.ceil((t+i)/2),f=e,s=2*e,l=new Array(s),u=new Array(s),g=0;g<s;g++)l[g]=-1,u[g]=-1;l[f+1]=0,u[f+1]=0;for(var h=t-i,_=h%2!=0,c=0,F=0,a=0,o=0,E=0;E<e;E++){for(var D=-E+c;D<=E-F;D+=2){for(var I=f+D,b=(L=D==-E||D!=E&&l[I-1]<l[I+1]?l[I+1]:l[I-1]+1)-D;L<t&&b<i&&n.charAt(L)==r.charAt(b);)L++,b++;if(l[I]=L,L>t)F+=2;else if(b>i)c+=2;else if(_){if((m=f+h-D)>=0&&m<s&&-1!=u[m])if(L>=(v=t-u[m]))return diff_bisectSplit_(n,r,L,b)}}for(var d=-E+a;d<=E-o;d+=2){for(var v,m=f+d,A=(v=d==-E||d!=E&&u[m-1]<u[m+1]?u[m+1]:u[m-1]+1)-d;v<t&&A<i&&n.charAt(t-v-1)==r.charAt(i-A-1);)v++,A++;if(u[m]=v,v>t)o+=2;else if(A>i)a+=2;else if(!_){if((I=f+h-d)>=0&&I<s&&-1!=l[I]){var L;b=f+(L=l[I])-I;if(L>=(v=t-v))return diff_bisectSplit_(n,r,L,b)}}}}return[[DIFF_DELETE,n],[DIFF_INSERT,r]]}function diff_bisectSplit_(n,r,t,i){var e=n.substring(0,t),f=r.substring(0,i),s=n.substring(t),l=r.substring(i),u=diff_main(e,f),g=diff_main(s,l);return u.concat(g)}function diff_commonPrefix(n,r){if(!n||!r||n.charAt(0)!=r.charAt(0))return 0;for(var t=0,i=Math.min(n.length,r.length),e=i,f=0;t<e;)n.substring(f,e)==r.substring(f,e)?f=t=e:i=e,e=Math.floor((i-t)/2+t);return e}function diff_commonSuffix(n,r){if(!n||!r||n.charAt(n.length-1)!=r.charAt(r.length-1))return 0;for(var t=0,i=Math.min(n.length,r.length),e=i,f=0;t<e;)n.substring(n.length-e,n.length-f)==r.substring(r.length-e,r.length-f)?f=t=e:i=e,e=Math.floor((i-t)/2+t);return e}function diff_halfMatch_(n,r){var t=n.length>r.length?n:r,i=n.length>r.length?r:n;if(t.length<4||2*i.length<t.length)return null;function e(n,r,t){for(var i,e,f,s,l=n.substring(t,t+Math.floor(n.length/4)),u=-1,g="";-1!=(u=r.indexOf(l,u+1));){var h=diff_commonPrefix(n.substring(t),r.substring(u)),_=diff_commonSuffix(n.substring(0,t),r.substring(0,u));g.length<_+h&&(g=r.substring(u-_,u)+r.substring(u,u+h),i=n.substring(0,t-_),e=n.substring(t+h),f=r.substring(0,u-_),s=r.substring(u+h))}return 2*g.length>=n.length?[i,e,f,s,g]:null}var f,s,l,u,g,h=e(t,i,Math.ceil(t.length/4)),_=e(t,i,Math.ceil(t.length/2));return h||_?(f=_?h&&h[4].length>_[4].length?h:_:h,n.length>r.length?(s=f[0],l=f[1],u=f[2],g=f[3]):(u=f[0],g=f[1],s=f[2],l=f[3]),[s,l,u,g,f[4]]):null}function diff_cleanupMerge(n){n.push([DIFF_EQUAL,""]);for(var r,t=0,i=0,e=0,f="",s="";t<n.length;)switch(n[t][0]){case DIFF_INSERT:e++,s+=n[t][1],t++;break;case DIFF_DELETE:i++,f+=n[t][1],t++;break;case DIFF_EQUAL:i+e>1?(0!==i&&0!==e&&(0!==(r=diff_commonPrefix(s,f))&&(t-i-e>0&&n[t-i-e-1][0]==DIFF_EQUAL?n[t-i-e-1][1]+=s.substring(0,r):(n.splice(0,0,[DIFF_EQUAL,s.substring(0,r)]),t++),s=s.substring(r),f=f.substring(r)),0!==(r=diff_commonSuffix(s,f))&&(n[t][1]=s.substring(s.length-r)+n[t][1],s=s.substring(0,s.length-r),f=f.substring(0,f.length-r))),0===i?n.splice(t-e,i+e,[DIFF_INSERT,s]):0===e?n.splice(t-i,i+e,[DIFF_DELETE,f]):n.splice(t-i-e,i+e,[DIFF_DELETE,f],[DIFF_INSERT,s]),t=t-i-e+(i?1:0)+(e?1:0)+1):0!==t&&n[t-1][0]==DIFF_EQUAL?(n[t-1][1]+=n[t][1],n.splice(t,1)):t++,e=0,i=0,f="",s=""}""===n[n.length-1][1]&&n.pop();var l=!1;for(t=1;t<n.length-1;)n[t-1][0]==DIFF_EQUAL&&n[t+1][0]==DIFF_EQUAL&&(n[t][1].substring(n[t][1].length-n[t-1][1].length)==n[t-1][1]?(n[t][1]=n[t-1][1]+n[t][1].substring(0,n[t][1].length-n[t-1][1].length),n[t+1][1]=n[t-1][1]+n[t+1][1],n.splice(t-1,1),l=!0):n[t][1].substring(0,n[t+1][1].length)==n[t+1][1]&&(n[t-1][1]+=n[t+1][1],n[t][1]=n[t][1].substring(n[t+1][1].length)+n[t+1][1],n.splice(t+1,1),l=!0)),t++;l&&diff_cleanupMerge(n)}var diff=diff_main;function cursor_normalize_diff(n,r){if(0===r)return[DIFF_EQUAL,n];for(var t=0,i=0;i<n.length;i++){var e=n[i];if(e[0]===DIFF_DELETE||e[0]===DIFF_EQUAL){var f=t+e[1].length;if(r===f)return[i+1,n];if(r<f){n=n.slice();var s=r-t,l=[e[0],e[1].slice(0,s)],u=[e[0],e[1].slice(s)];return n.splice(i,1,l,u),[i+1,n]}t=f}}throw new Error("cursor_pos is out of bounds!")}function fix_cursor(n,r){var t=cursor_normalize_diff(n,r),i=t[1],e=t[0],f=i[e],s=i[e+1];if(null==f)return n;if(f[0]!==DIFF_EQUAL)return n;if(null!=s&&f[1]+s[1]===s[1]+f[1])return i.splice(e,2,s,f),merge_tuples(i,e,2);if(null!=s&&0===s[1].indexOf(f[1])){i.splice(e,2,[s[0],f[1]],[0,f[1]]);var l=s[1].slice(f[1].length);return l.length>0&&i.splice(e+2,0,[s[0],l]),merge_tuples(i,e,3)}return n}function fix_emoji(n){for(var r,t=!1,i=function(n){return n.charCodeAt(0)>=56320&&n.charCodeAt(0)<=57343},e=2;e<n.length;e+=1)n[e-2][0]===DIFF_EQUAL&&((r=n[e-2][1]).charCodeAt(r.length-1)>=55296&&r.charCodeAt(r.length-1)<=56319)&&n[e-1][0]===DIFF_DELETE&&i(n[e-1][1])&&n[e][0]===DIFF_INSERT&&i(n[e][1])&&(t=!0,n[e-1][1]=n[e-2][1].slice(-1)+n[e-1][1],n[e][1]=n[e-2][1].slice(-1)+n[e][1],n[e-2][1]=n[e-2][1].slice(0,-1));if(!t)return n;var f=[];for(e=0;e<n.length;e+=1)n[e][1].length>0&&f.push(n[e]);return f}function merge_tuples(n,r,t){for(var i=r+t-1;i>=0&&i>=r-1;i--)if(i+1<n.length){var e=n[i],f=n[i+1];e[0]===f[1]&&n.splice(i,2,[e[0],e[1]+f[1]])}return n}diff.INSERT=DIFF_INSERT,diff.DELETE=DIFF_DELETE,diff.EQUAL=DIFF_EQUAL;
//Changesets
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var Changeset = require('./Changeset')
, Retain = require('./operations/Retain')
, Skip = require('./operations/Skip')
, Insert = require('./operations/Insert')
function Builder() {
this.ops = []
this.addendum = ''
this.removendum = ''
}
module.exports = Builder
Builder.prototype.keep =
Builder.prototype.retain = function(len) {
this.ops.push(new Retain(len))
return this
}
Builder.prototype.delete =
Builder.prototype.skip = function(str) {
this.removendum += str
this.ops.push(new Skip(str.length))
return this
}
Builder.prototype.add =
Builder.prototype.insert = function(str) {
this.addendum += str
this.ops.push(new Insert(str.length))
return this
}
Builder.prototype.end = function() {
var cs = new Changeset(this.ops)
cs.addendum = this.addendum
cs.removendum = this.removendum
return cs
}
},{"./Changeset":2,"./operations/Insert":7,"./operations/Retain":8,"./operations/Skip":9}],2:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational transformation (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* A sequence of consecutive operations
*
* @param ops.. <Operation> all passed operations will be added to the changeset
*/
function Changeset(ops/*or ops..*/) {
this.addendum = ""
this.removendum = ""
this.inputLength = 0
this.outputLength = 0
if(!Array.isArray(ops)) ops = arguments
for(var i=0; i<ops.length; i++) {
this.push(ops[i])
this.inputLength += ops[i].input
this.outputLength += ops[i].output
}
}
// True inheritance
Changeset.prototype = Object.create(Array.prototype, {
constructor: {
value: Changeset,
enumerable: false,
writable: true,
configurable: true
}
});
module.exports = Changeset
var TextTransform = require('./TextTransform')
, ChangesetTransform = require('./ChangesetTransform')
var Retain = require('./operations/Retain')
, Skip = require('./operations/Skip')
, Insert = require('./operations/Insert')
var Builder = require('./Builder')
/**
* Returns an array containing the ops that are within the passed range
* (only op.input is counted; thus not counting inserts to the range length, yet they are part of the range)
*/
Changeset.prototype.subrange = function(start, len) {
var range = []
, op, oplen
, l=0
for(var i=0, pos=0; i<this.length && l < len; i++) {
op = this[i]
if(op.length+pos > start) {
if(op.input) {
if(op.length != Infinity) oplen = op.length -Math.max(0, start-pos) -Math.max(0, (op.length+pos)-(start+len))
else oplen = len
range.push( op.derive(oplen) ) // (Don't copy over more than len param allows)
}
else {
range.push( op.derive(op.length) )
oplen = 0
}
l += oplen
}
pos += op.input
}
return range
}
/**
* Merge two changesets (that are based on the same state!) so that the resulting changseset
* has the same effect as both orignal ones applied one after the other
*
* @param otherCs <Changeset>
* @param left <boolean> Which op to choose if there's an insert tie (If you use this function in a distributed, synchronous environment, be sure to invert this param on the other site, otherwise it can be omitted safely))
* @returns <Changeset>
*/
Changeset.prototype.merge = function(otherCs, left) {
if(!(otherCs instanceof Changeset)) {
throw new Error('Argument must be a #<Changeset>, but received '+otherCs.__proto__.constructor.name)
}
if(otherCs.inputLength !== this.outputLength) {
throw new Error("Changeset lengths for merging don't match! Input length of younger cs: "+otherCs.inputLength+', output length of older cs:'+this.outputLength)
}
var newops = []
, addPtr1 = 0
, remPtr1 = 0
, addPtr2 = 0
, remPtr2 = 0
, newaddendum = ''
, newremovendum = ''
zip(this, otherCs, function(op1, op2) {
// console.log(newops)
// console.log(op1, op2)
// I'm deleting something -- the other cs can't know that, so just overtake my op
if(op1 && !op1.output) {
newops.push(op1.merge().clone())
newremovendum += this.removendum.substr(remPtr1, op1.length) // overtake added chars
remPtr1 += op1.length
op1.length = 0 // don't gimme that one again.
return
}
// op2 is an insert
if(op2 && !op2.input) {
newops.push(op2.merge().clone())
newaddendum += otherCs.addendum.substr(addPtr2, op2.length) // overtake added chars
addPtr2 += op2.length
op2.length = 0 // don't gimme that one again.
return
}
// op2 is either a retain or a skip
if(op2 && op2.input && op1) {
// op2 retains whatever we do here (retain or insert), so just clone my op
if(op2.output) {
newops.push(op1.merge(op2).clone())
if(!op1.input) { // overtake addendum
newaddendum += this.addendum.substr(addPtr1, op1.length)
addPtr1 += op1.length
}
op1.length = 0 // don't gimme these again
op2.length = 0
}else
// op2 deletes my retain here, so just clone the delete
// (op1 can only be a retain and no skip here, cause we've handled skips above already)
if(!op2.output && op1.input) {
newops.push(op2.merge(op1).clone())
newremovendum += otherCs.removendum.substr(remPtr2, op2.length) // overtake added chars
remPtr2 += op2.length
op1.length = 0 // don't gimme these again
op2.length = 0
}else
//otherCs deletes something I added (-1) +1 = 0
{
addPtr1 += op1.length
op1.length = 0 // don't gimme these again
op2.length = 0
}
return
}
console.log('oops', arguments)
throw new Error('oops. This case hasn\'t been considered by the developer (error code: PBCAC)')
}.bind(this))
var newCs = new Changeset(newops)
newCs.addendum = newaddendum
newCs.removendum = newremovendum
return newCs
}
/**
* A private and quite handy function that slices ops into equally long pieces and applies them on a mapping function
* that can determine the iteration steps by setting op.length to 0 on an op (equals using .next() in a usual iterator)
*/
function zip(cs1, cs2, func) {
var opstack1 = cs1.map(function(op) {return op.clone()}) // copy ops
, opstack2 = cs2.map(function(op) {return op.clone()})
var op2, op1
while(opstack1.length || opstack2.length) {// iterate through all outstanding ops of this cs
op1 = opstack1[0]? opstack1[0].clone() : null
op2 = opstack2[0]? opstack2[0].clone() : null
if(op1) {
if(op2) op1 = op1.derive(Math.min(op1.length, op2.length)) // slice 'em into equally long pieces
if(opstack1[0].length > op1.length) opstack1[0] = opstack1[0].derive(opstack1[0].length-op1.length)
else opstack1.shift()
}
if(op2) {
if(op1) op2 = op2.derive(Math.min(op1.length, op2.length)) // slice 'em into equally long pieces
if(opstack2[0].length > op2.length) opstack2[0] = opstack2[0].derive(opstack2[0].length-op2.length)
else opstack2.shift()
}
func(op1, op2)
if(op1 && op1.length) opstack1.unshift(op1)
if(op2 && op2.length) opstack2.unshift(op2)
}
}
/**
* Inclusion Transformation (IT) or Forward Transformation
*
* transforms the operations of the current changeset against the
* all operations in another changeset in such a way that the
* effects of the latter are effectively included.
* This is basically like a applying the other cs on this one.
*
* @param otherCs <Changeset>
* @param left <boolean> Which op to choose if there's an insert tie (If you use this function in a distributed, synchronous environment, be sure to invert this param on the other site, otherwise it can be omitted safely)
*
* @returns <Changeset>
*/
Changeset.prototype.transformAgainst = function(otherCs, left) {
if(!(otherCs instanceof Changeset)) {
throw new Error('Argument to Changeset#transformAgainst must be a #<Changeset>, but received '+otherCs.__proto__.constructor.name)
}
if(this.inputLength != otherCs.inputLength) {
throw new Error('Can\'t transform changesets with differing inputLength: '+this.inputLength+' and '+otherCs.inputLength)
}
var transformation = new ChangesetTransform(this, [new Retain(Infinity)])
otherCs.forEach(function(op) {
var nextOp = this.subrange(transformation.pos, Infinity)[0] // next op of this cs
if(nextOp && !nextOp.input && !op.input && left) { // two inserts tied; left breaks it
transformation.writeOutput(transformation.readInput(nextOp.length))
}
op.apply(transformation)
}.bind(this))
return transformation.result()
}
/**
* Exclusion Transformation (ET) or Backwards Transformation
*
* transforms all operations in the current changeset against the operations
* in another changeset in such a way that the impact of the latter are effectively excluded
*
* @param changeset <Changeset> the changeset to substract from this one
* @param left <boolean> Which op to choose if there's an insert tie (If you use this function in a distributed, synchronous environment, be sure to invert this param on the other site, otherwise it can be omitted safely)
* @returns <Changeset>
*/
Changeset.prototype.substract = function(changeset, left) {
// The current operations assume that the changes in
// `changeset` happened before, so for each of those ops
// we create an operation that undoes its effect and
// transform all our operations on top of the inverse changes
return this.transformAgainst(changeset.invert(), left)
}
/**
* Returns the inverse Changeset of the current one
*
* Changeset.invert().apply(Changeset.apply(document)) == document
*/
Changeset.prototype.invert = function() {
// invert all ops
var newCs = new Changeset(this.map(function(op) {
return op.invert()
}))
// removendum becomes addendum and vice versa
newCs.addendum = this.removendum
newCs.removendum = this.addendum
return newCs
}
/**
* Applies this changeset on a text
*/
Changeset.prototype.apply = function(input) {
// pre-requisites
if(input.length != this.inputLength) throw new Error('Input length doesn\'t match expected length. expected: '+this.inputLength+'; actual: '+input.length)
var operation = new TextTransform(input, this.addendum, this.removendum)
this.forEach(function(op) {
// each Operation has access to all pointers as well as the input, addendum and removendum (the latter are immutable)
op.apply(operation)
}.bind(this))
return operation.result()
}
/**
* Returns an array of strings describing this changeset's operations
*/
Changeset.prototype.inspect = function() {
var j = 0
return this.map(function(op) {
var string = ''
if(!op.input) { // if Insert
string = this.addendum.substr(j,op.length)
j += op.length
return string
}
for(var i=0; i<op.length; i++) string += op.symbol
return string
}.bind(this)).join('')
}
/**
* Serializes the given changeset in order to return a (hopefully) more compact representation
* than json that can be sent through a network or stored in a database
*
* Numbers are converted to the base 36, unsafe chars in the text are urlencoded
*
* @param cs <Changeset> The changeset to be serialized
* @returns <String> The serialized changeset
*/
Changeset.prototype.pack = function() {
var packed = this.map(function(op) {
return op.pack()
}).join('')
var addendum = this.addendum.replace(/%/g, '%25').replace(/\|/g, '%7C')
, removendum = this.removendum.replace(/%/g, '%25').replace(/\|/g, '%7C')
return packed+'|'+addendum+'|'+removendum
}
Changeset.prototype.toString = function() {
return this.pack()
}
/**
* Unserializes the output of cs.text.Changeset#toString()
*
* @param packed <String> The serialized changeset
* @param <cs.Changeset>
*/
Changeset.unpack = function(packed) {
if(packed == '') throw new Error('Cannot unpack from empty string')
var components = packed.split('|')
, opstring = components[0]
, addendum = components[1].replace(/%7c/gi, '|').replace(/%25/g, '%')
, removendum = components[2].replace(/%7c/gi, '|').replace(/%25/g, '%')
var matches = opstring.match(/[=+-]([^=+-])+/g)
if(!matches) throw new Error('Cannot unpack invalidly serialized op string')
var ops = []
matches.forEach(function(s) {
var symbol = s.substr(0,1)
, data = s.substr(1)
if(Skip.prototype.symbol == symbol) return ops.push(Skip.unpack(data))
if(Insert.prototype.symbol == symbol) return ops.push(Insert.unpack(data))
if(Retain.prototype.symbol == symbol) return ops.push(Retain.unpack(data))
throw new Error('Invalid changeset representation passed to Changeset.unpack')
})
var cs = new Changeset(ops)
cs.addendum = addendum
cs.removendum = removendum
return cs
}
Changeset.create = function() {
return new Builder
}
/**
* Returns a Changeset containing the operations needed to transform text1 into text2
*
* @param text1 <String>
* @param text2 <String>
*/
Changeset.fromDiff = function(diff) {
/**
* The data structure representing a diff is an array of tuples:
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
* which means: delete 'Hello', add 'Goodbye' and keep ' world.'
*/
var DIFF_DELETE = -1;
var DIFF_INSERT = 1;
var DIFF_EQUAL = 0;
var ops = []
, removendum = ''
, addendum = ''
diff.forEach(function(d) {
if (DIFF_DELETE == d[0]) {
ops.push(new Skip(d[1].length))
removendum += d[1]
}
if (DIFF_INSERT == d[0]) {
ops.push(new Insert(d[1].length))
addendum += d[1]
}
if(DIFF_EQUAL == d[0]) {
ops.push(new Retain(d[1].length))
}
})
var cs = new Changeset(ops)
cs.addendum = addendum
cs.removendum = removendum
return cs
}
},{"./Builder":1,"./ChangesetTransform":3,"./TextTransform":5,"./operations/Insert":7,"./operations/Retain":8,"./operations/Skip":9}],3:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational ChangesetTransform (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Retain = require('./operations/Retain')
, Skip = require('./operations/Skip')
, Insert = require('./operations/Insert')
, Changeset = require('./Changeset')
function ChangesetTransform(inputCs, addendum) {
this.output = []
this.addendum = addendum
this.newRemovendum = ''
this.newAddendum = ''
this.cs = inputCs
this.pos = 0
this.addendumPointer = 0
this.removendumPointer = 0
}
module.exports = ChangesetTransform
ChangesetTransform.prototype.readInput = function (len) {
var ret = this.cs.subrange(this.pos, len)
this.pos += len
return ret
}
ChangesetTransform.prototype.readAddendum = function (len) {
//return [new Retain(len)]
var ret = this.subrange(this.addendum, this.addendumPointer, len)
this.addendumPointer += len
return ret
}
ChangesetTransform.prototype.writeRemovendum = function (range) {
range
.filter(function(op) {return !op.output})
.forEach(function(op) {
this.removendumPointer += op.length
}.bind(this))
}
ChangesetTransform.prototype.writeOutput = function (range) {
this.output = this.output.concat(range)
range
.filter(function(op) {return !op.output})
.forEach(function(op) {
this.newRemovendum += this.cs.removendum.substr(this.removendumPointer, op.length)
this.removendumPointer += op.length
}.bind(this))
}
ChangesetTransform.prototype.subrange = function (range, start, len) {
if(len) return this.cs.subrange.call(range, start, len)
else return range.filter(function(op){ return !op.input})
}
ChangesetTransform.prototype.result = function() {
this.writeOutput(this.readInput(Infinity))
var newCs = new Changeset(this.output)
newCs.addendum = this.cs.addendum
newCs.removendum = this.newRemovendum
return newCs
}
},{"./Changeset":2,"./operations/Insert":7,"./operations/Retain":8,"./operations/Skip":9}],4:[function(require,module,exports){
function Operator() {
}
module.exports = Operator
Operator.prototype.clone = function() {
return this.derive(this.length)
}
Operator.prototype.derive = function(len) {
return new (this.constructor)(len)
}
Operator.prototype.pack = function() {
return this.symbol + (this.length).toString(36)
}
},{}],5:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational Apply (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Retain = require('./operations/Retain')
, Skip = require('./operations/Skip')
, Insert = require('./operations/Insert')
, Insert = require('./Changeset')
function TextTransform(input, addendum, removendum) {
this.output = ''
this.input = input
this.addendum = addendum
this.removendum = removendum
this.pos = 0
this.addPos = 0
this.remPos = 0
}
module.exports = TextTransform
TextTransform.prototype.readInput = function (len) {
var ret = this.input.substr(this.pos, len)
this.pos += len
return ret
}
TextTransform.prototype.readAddendum = function (len) {
var ret = this.addendum.substr(this.addPos, len)
this.addPos += len
return ret
}
TextTransform.prototype.writeRemovendum = function (range) {
//var expected = this.removendum.substr(this.remPos, range.length)
//if(range != expected) throw new Error('Removed chars don\'t match removendum. expected: '+expected+'; actual: '+range)
this.remPos += range.length
}
TextTransform.prototype.writeOutput = function (range) {
this.output += range
}
TextTransform.prototype.subrange = function (range, start, len) {
return range.substr(start, len)
}
TextTransform.prototype.result = function() {
this.writeOutput(this.readInput(Infinity))
return this.output
}
},{"./Changeset":2,"./operations/Insert":7,"./operations/Retain":8,"./operations/Skip":9}],6:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational transformation (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Changeset = require('./Changeset')
, Retain = require('./operations/Retain')
, Skip = require('./operations/Skip')
, Insert = require('./operations/Insert')
exports.Operator = require('./Operator')
exports.Changeset = Changeset
exports.Insert = Insert
exports.Retain = Retain
exports.Skip = Skip
if('undefined' != typeof window) window.changesets = exports
/**
* Serializes the given changeset in order to return a (hopefully) more compact representation
* that can be sent through a network or stored in a database
* @alias cs.text.Changeset#pack
*/
exports.pack = function(cs) {
return cs.pack()
}
/**
* Unserializes the output of cs.text.pack
* @alias cs.text.Changeset.unpack
*/
exports.unpack = function(packed) {
return Changeset.unpack(packed)
}
/**
* shareJS ot type API sepc support
*/
exports.name = 'changesets'
exports.url = 'https://github.com/marcelklehr/changesets'
/**
* create([initialText])
*
* creates a snapshot (optionally with supplied intial text)
*/
exports.create = function(initText) {
return initText || ''
}
/**
* Apply a changeset on a snapshot creating a new one
*
* The old snapshot object mustn't be used after calling apply on it
* returns the resulting
*/
exports.apply = function(snapshot, op) {
op = exports.unpack(op)
return op.apply(snapshot)
}
/**
* Transform changeset1 against changeset2
*/
exports.transform = function (op1, op2, side) {
op1 = exports.unpack(op1)
op2 = exports.unpack(op2)
return exports.pack(op1.transformAgainst(op2, ('left'==side)))
}
/**
* Merge two changesets into one
*/
exports.compose = function (op1, op2) {
op1 = exports.unpack(op1)
op2 = exports.unpack(op2)
return exports.pack(op1.merge(op2))
}
/**
* Invert a changeset
*/
exports.invert = function(op) {
return op.invert()
}
},{"./Changeset":2,"./Operator":4,"./operations/Insert":7,"./operations/Retain":8,"./operations/Skip":9}],7:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational transformation (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Operator = require('../Operator')
/**
* Insert Operator
* Defined by:
* - length
* - input=0
* - output=length
*
* @param length <Number> How many chars to be inserted
*/
function Insert(length) {
this.length = length
this.input = 0
this.output = length
}
// True inheritance
Insert.prototype = Object.create(Operator.prototype, {
constructor: {
value: Insert,
enumerable: false,
writable: true,
configurable: true
}
});
module.exports = Insert
Insert.prototype.symbol = '+'
var Skip = require('./Skip')
, Retain = require('./Retain')
Insert.prototype.apply = function(t) {
t.writeOutput(t.readAddendum(this.output))
}
Insert.prototype.merge = function() {
return this
}
Insert.prototype.invert = function() {
return new Skip(this.length)
}
Insert.unpack = function(data) {
return new Insert(parseInt(data, 36))
}
},{"../Operator":4,"./Retain":8,"./Skip":9}],8:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational transformation (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Operator = require('../Operator')
/**
* Retain Operator
* Defined by:
* - length
* - input=output=length
*
* @param length <Number> How many chars to retain
*/
function Retain(length) {
this.length = length
this.input = length
this.output = length
}
// True inheritance
Retain.prototype = Object.create(Operator.prototype, {
constructor: {
value: Retain,
enumerable: false,
writable: true,
configurable: true
}
});
module.exports = Retain
Retain.prototype.symbol = '='
Retain.prototype.apply = function(t) {
t.writeOutput(t.readInput(this.input))
}
Retain.prototype.invert = function() {
return this
}
Retain.prototype.merge = function(op2) {
return this
}
Retain.unpack = function(data) {
return new Retain(parseInt(data, 36))
}
},{"../Operator":4}],9:[function(require,module,exports){
/*!
* changesets
* A Changeset library incorporating operational transformation (OT)
* Copyright 2012 by Marcel Klehr <mklehr@gmx.net>
*
* (MIT LICENSE)
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Operator = require('../Operator')
/**
* Skip Operator
* Defined by:
* - length
* - input=length
* - output=0
*
* @param length <Number> How many chars to be Skip
*/
function Skip(length) {
this.length = length
this.input = length
this.output = 0
}
// True inheritance
Skip.prototype = Object.create(Operator.prototype, {
constructor: {
value: Skip,
enumerable: false,
writable: true,
configurable: true
}
});
module.exports = Skip
Skip.prototype.symbol = '-'
var Insert = require('./Insert')
, Retain = require('./Retain')
, Changeset = require('../Changeset')
Skip.prototype.apply = function(t) {
var input = t.readInput(this.input)
t.writeRemovendum(input)
t.writeOutput(t.subrange(input, 0, this.output)) // retain Inserts in my range
}
Skip.prototype.merge = function(op2) {
return this
}
Skip.prototype.invert = function() {
return new Insert(this.length)
}
Skip.unpack = function(data) {
return new Skip(parseInt(data, 36))
}
},{"../Changeset":2,"../Operator":4,"./Insert":7,"./Retain":8}]},{},[6])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment