├── docs ├── example-image.png └── collaboration-image.gif ├── package.json ├── README.md ├── .gitignore ├── index.js ├── sharedb-dist ├── ot-text.min.js ├── ot-text-tp2.min.js ├── ot-json0.min.js ├── rich-text.min.js └── sharedb-client.min.js └── clients ├── ace-editor.html └── code-mirror.html /docs/example-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkjohnson/collaborative-code-editor/HEAD/docs/example-image.png -------------------------------------------------------------------------------- /docs/collaboration-image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkjohnson/collaborative-code-editor/HEAD/docs/collaboration-image.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "collaborative-code-editor", 3 | "main": "index.js", 4 | "scripts": { 5 | "start": "node index.js" 6 | }, 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/gkjohnson/collaborative-code-editor.git" 10 | }, 11 | "author": "Garrett Johnson ", 12 | "license": "ISC", 13 | "bugs": { 14 | "url": "https://github.com/gkjohnson/collaborative-code-editor/issues" 15 | }, 16 | "homepage": "https://github.com/gkjohnson/collaborative-code-editor#readme", 17 | "dependencies": { 18 | "ace-builds": "^1.2.8", 19 | "codemirror": "^5.28.0", 20 | "express": "^4.15.3", 21 | "ot-text": "^1.0.1", 22 | "socket.io": "^2.0.3", 23 | "websocket-json-stream": "0.0.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # collaborative-code-editor 2 | 3 | Collaborative Text Editor example with selection synchronization, active user display, and real time text collaboration using ShareDB. The purpose of this repo is to give a basic example setup for [ShareDB](https://github.com/share/sharedb) with an [Ace Editor](https://github.com/ajaxorg/ace) [client](/clients/ace-editor.html) and [Code-Mirror](https://codemirror.net/) [client](/clients/code-mirror.html). 4 | 5 | ![Editor Example](/docs/collaboration-image.gif) 6 | 7 | ## Details 8 | 9 | Uses `ot-text` as an example format, but could easily be updated to use `ot-text-tp2` or other OT formats. 10 | 11 | Because ShareDB requires precompiling source to load, a pre-built version of sharedb is copied from [this repo](https://github.com/gkjohnson/sharedb-builds). 12 | 13 | ## How To Run 14 | 15 | Run `npm install`. 16 | 17 | Then `npm start`. 18 | 19 | A static file server will start on port `80`, and the ShareDB connection on port `8080`. Load up `http://localhost/clients/ace-editor.html` or `http://localhost/clients/code-mirror.html` to get collaborating! 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const ShareDB = require('sharedb') 2 | ShareDB.types.register(require('ot-text').type) 3 | const WebSocket = require('ws') 4 | const WebSocketJSONStream = require('websocket-json-stream') 5 | const express = require('express') 6 | const socketio = require('socket.io') 7 | const http = require('http') 8 | 9 | // Static Server 10 | const app = express() 11 | const server = http.createServer(app); 12 | app.use((res, req, next) => { 13 | // Create the document if it hasn't been already 14 | // const sharedoc = shareconn.get('docs', res.query.doc || 'default') 15 | const sharedoc = shareconn.get('docs', 'default') 16 | if (sharedoc.data == null) sharedoc.create("", 'text'); 17 | 18 | next() 19 | }) 20 | app.use('/', express.static('./')) 21 | 22 | 23 | // Socket IO Server 24 | const io = socketio(server) 25 | const anchors = {} 26 | const names = {} 27 | io.on('connection', client => { 28 | 29 | const id = client.id 30 | names[id] = String.fromCharCode(Math.floor('A'.charCodeAt(0) + Math.random() * 26)) 31 | anchors[id] = [0, 0] 32 | 33 | // send client its id and anchor and names obj 34 | client.emit('initialize', { anchors, names }) 35 | 36 | client.on('anchor-update', msg => { 37 | // set anchors[id] 38 | anchors[id] = msg 39 | io.emit('anchor-update', { id, anchor: anchors[id] }) 40 | }) 41 | 42 | io.emit('id-join', { id, name: names[id], anchor: anchors[id] }) 43 | 44 | // Remove id info and update clients 45 | // TODO: This doesn't seem to always get called 46 | // Mashing resfresh on a page seems to leave lingering 47 | // connections that eventually close 48 | client.on('disconnect', () => { 49 | console.log('left', id) 50 | delete names[id] 51 | delete anchors[id] 52 | io.emit('id-left', { id }) 53 | }) 54 | }) 55 | 56 | // Start Server 57 | const port = 80 58 | server.listen(port) 59 | console.log(`listening on port ${port}`) 60 | 61 | // Share DB 62 | const share = new ShareDB() 63 | const shareconn = share.connect() 64 | const shareserver = http.createServer() 65 | const sharewss = new WebSocket.Server({ server: shareserver }) 66 | sharewss.on('connection', client => share.listen(new WebSocketJSONStream(client))) 67 | shareserver.listen(8080) 68 | 69 | console.log(`ShareDB listening on port 8080`) -------------------------------------------------------------------------------- /sharedb-dist/ot-text.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=16)}({16:function(e,t,r){window.ShareDB.types.register(r(17).type)},17:function(e,t,r){var n=r(18);n.api=r(19),e.exports={type:n}},18:function(e,t){t.name="text",t.uri="http://sharejs.org/types/textv1",t.create=function(e){if(null!=e&&"string"!=typeof e)throw Error("Initial data must be a string");return e||""};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},n=function(e){if(!r(e))throw Error("Op must be an array of components");for(var t=null,n=0;n0))throw Error("Object components must be deletes of size > 0");break;case"string":if(!(o.length>0))throw Error("Inserts cannot be empty");break;case"number":if(!(o>0))throw Error("Skip components must be >0");if("number"==typeof t)throw Error("Adjacent skip components should be combined")}t=o}if("number"==typeof t)throw Error("Op has a trailing skip")},o=function(e){return function(t){if(t&&0!==t.d)return 0===e.length?e.push(t):typeof t==typeof e[e.length-1]?"object"==typeof t?e[e.length-1].d+=t.d:e[e.length-1]+=t:e.push(t)}},i=function(e){var t=0,r=0;return[function(n,o){if(t===e.length)return-1===n?null:n;var i,s=e[t];return"number"==typeof s?-1===n||s-r<=n?(i=s-r,++t,r=0,i):(r+=n,n):"string"==typeof s?-1===n||"i"===o||s.length-r<=n?(i=s.slice(r),++t,r=0,i):(i=s.slice(r,r+n),r+=n,i):-1===n||"d"===o||s.d-r<=n?(i={d:s.d-r},++t,r=0,i):(r+=n,{d:n})},function(){return e[t]}]},s=function(e){return"number"==typeof e?e:e.length||e.d},c=function(e){return e.length>0&&"number"==typeof e[e.length-1]&&e.pop(),e};t.normalize=function(e){for(var t=[],r=o(t),n=0;ne.length)throw Error("The op is too long for this document");r.push(e.slice(0,i)),e=e.slice(i);break;case"string":r.push(i);break;case"object":e=e.slice(i.d)}}return r.join("")+e},t.transform=function(e,t,r){if("left"!=r&&"right"!=r)throw Error("side ("+r+") must be 'left' or 'right'");n(e),n(t);for(var u=[],a=o(u),f=i(e),l=f[0],p=f[1],h=0;h0;)g=l(b,"i"),a(g),"string"!=typeof g&&(b-=s(g));break;case"string":"left"===r&&"string"==typeof p()&&a(l(-1)),a(m.length);break;case"object":for(b=m.d;b>0;)switch(typeof(g=l(b,"i"))){case"number":b-=g;break;case"string":a(g);break;case"object":b-=g.d}}}for(;m=l(-1);)a(m);return c(u)},t.compose=function(e,t){n(e),n(t);for(var r=[],u=o(r),a=i(e)[0],f=0;f0;)p=a(l,"d"),u(p),"object"!=typeof p&&(l-=s(p));break;case"string":u(h);break;case"object":for(l=h.d;l>0;)switch(typeof(p=a(l,"d"))){case"number":u({d:p}),l-=p;break;case"string":l-=p.length;break;case"object":u(p)}}}for(;h=a(-1);)u(h);return c(r)};var u=function(e,t){for(var r=0,n=0;n0||"number"==typeof o.i&&o.i>0))throw new Error("Inserts must insert a string or a +ive number")}else{if(void 0===o.d)throw new Error("Operation component must define .i or .d");if(!("number"==typeof o.d&&o.d>0))throw new Error("Deletes must be a +ive number")}else{if("number"!=typeof o)throw new Error("Op components must be objects or numbers");if(o<=0)throw new Error("Skip components must be a positive number");if("number"==typeof t)throw new Error("Adjacent skip components should be combined")}t=o}},i=r._takeDoc=function(e,t,r,n){if(t.index>=e.data.length)throw new Error("Operation goes past the end of the document");var o,i=e.data[t.index];o="string"==typeof i?null!=r?i.slice(t.offset,t.offset+r):i.slice(t.offset):null==r||n?i-t.offset:Math.min(r,i-t.offset);var f=o.length||o;return(i.length||i)-t.offset>f?t.offset+=f:(t.index++,t.offset=0),o},f=r._appendDoc=function(e,t){if(0!==t&&""!==t){"string"==typeof t?(e.charLength+=t.length,e.totalLength+=t.length):e.totalLength+=t;var r=e.data;0===r.length?r.push(t):typeof r[r.length-1]==typeof t?r[r.length-1]+=t:r.push(t)}};r.apply=function(e,t){if(null==e.totalLength||null==e.charLength||!n(e.data))throw new Error("Snapshot is invalid");o(t);for(var l=r.create(),a={index:0,offset:0},u=0;u0;)h=i(e,a,s),f(l,h),s-=h.length||h;else if(void 0!==p.i)f(l,p.i);else if(void 0!==p.d){for(s=p.d;s>0;)h=i(e,a,s),s-=h.length||h;f(l,p.d)}}return l};var l=r._append=function(e,t){var r;0===t||""===t.i||0===t.i||0===t.d||(0===e.length?e.push(t):(r=e[e.length-1],"number"==typeof t&&"number"==typeof r?e[e.length-1]+=t:null!=t.i&&null!=r.i&&typeof r.i==typeof t.i?r.i+=t.i:null!=t.d&&null!=r.d?r.d+=t.d:e.push(t)))},a=function(e,t,r,n){if(t.index===e.length)return null;var o,i,f=e[t.index],l=t.offset;if("number"==typeof(o=f)||"number"==typeof(o=f.i)||null!=(o=f.d)){var a;return null==r||o-l<=r||n&&null!=f.i?(a=o-l,++t.index,t.offset=0):(t.offset+=r,a=r),null!=f.i?{i:a}:null!=f.d?{d:a}:a}return null==r||f.i.length-l<=r||n?(i={i:f.i.slice(l)},++t.index,t.offset=0):(i={i:f.i.slice(l,l+r)},t.offset+=r),i},u=function(e){return"number"==typeof e?e:"string"==typeof e.i?e.i.length:e.d||e.i};r.normalize=function(e){for(var t=[],r=0;r0;){if(null===(h=a(e,f,d,!0)))throw new Error("The transformed op is invalid");if(null!=h.d)throw new Error("The transformed op deletes locally inserted characters - it cannot be purged of the insert.");"number"==typeof h?d-=h:l(i,h)}else for(;d>0;){if(null===(h=a(e,f,d,!0)))throw new Error("The op traverses more elements than the document has");l(i,h),h.i||(d-=u(h))}}for(var p;p=a(e,f);){if(void 0===p.i)throw new Error("Remaining fragments in the op: "+p);l(i,p)}return i};r.transform=function(e,t,r){if("left"!=r&&"right"!=r)throw new Error("side ("+r+") should be 'left' or 'right'");return s(e,t,!0,r)},r.prune=function(e,t){return s(e,t,!1)},r.compose=function(e,t){if(null==e)return t;o(e),o(t);for(var r,n=[],i={index:0,offset:0},f=0;f0;){if(null===(h=a(e,i,s)))throw new Error("The op traverses more elements than the document has");l(n,h),s-=u(h)}else if(void 0!==r.i)l(n,{i:r.i});else for(s=r.d;s>0;){if(null===(h=a(e,i,s)))throw new Error("The op traverses more elements than the document has");var p=u(h);void 0!==h.i?l(n,{i:p}):l(n,{d:p}),s-=p}}for(;r=a(e,i);){if(void 0===r.i)throw new Error("Remaining fragments in op1: "+r);l(n,r)}return n}}}); -------------------------------------------------------------------------------- /sharedb-dist/ot-json0.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(t){if(i[t])return i[t].exports;var l=i[t]={i:t,l:!1,exports:{}};return e[t].call(l.exports,l,l.exports,n),l.l=!0,l.exports}var i={};n.m=e,n.c=i,n.d=function(e,i,t){n.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(i,"a",i),i},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=12)}([function(e,n){function i(e,n,i,t){var l=function(e,i,t,l){n(t,e,i,"left"),n(l,i,e,"right")},r=e.transformX=function(e,n){i(e),i(n);for(var o=[],p=0;p=t||r!==n.p[l])return null}return i},d.canOpAffectPath=function(e,n){return null!=d.commonLengthForOps({p:n},e)},d.transformComponent=function(e,n,i,r){n=p(n);var o=d.commonLengthForOps(i,n),u=d.commonLengthForOps(n,i),s=n.p.length,a=i.p.length;if((null!=n.na||n.t)&&s++,(null!=i.na||i.t)&&a++,null!=u&&a>s&&n.p[u]==i.p[u])if(void 0!==n.ld){var c=p(i);c.p=c.p.slice(s),n.ld=d.apply(p(n.ld),[c])}else if(void 0!==n.od){var c=p(i);c.p=c.p.slice(s),n.od=d.apply(p(n.od),[c])}if(null!=o){var v=s==a,c=i;if(null==n.si&&null==n.sd||null==i.si&&null==i.sd||(t(n),c=p(i),t(c)),c.t&&f[c.t]){if(n.t&&n.t===c.t){var h=f[n.t].transform(n.o,c.o,r);if(h.length>0)if(null!=n.si||null!=n.sd)for(var g=n.p,m=0;mO&&n.p[o]--,w>b?n.p[o]++:w===b&&O>b&&(n.p[o]++,w===y&&n.lm++),y>O?n.lm--:y===O&&y>w&&n.lm--,y>b?n.lm++:y===b&&(b>O&&y>w||bw?n.lm++:y===O&&n.lm--)}else if(void 0!==n.li&&void 0===n.ld&&v){var w=i.p[o],y=i.lm;g=n.p[o],g>w&&n.p[o]--,g>y&&n.p[o]++}else{var w=i.p[o],y=i.lm;g=n.p[o],g===w?n.p[o]=y:(g>w&&n.p[o]--,g>y?n.p[o]++:g===y&&w>y&&n.p[o]++)}else if(void 0!==i.oi&&void 0!==i.od){if(n.p[o]===i.p[o]){if(void 0===n.oi||!v)return e;if("right"===r)return e;n.od=i.oi}}else if(void 0!==i.oi){if(void 0!==n.oi&&n.p[o]===i.p[o]){if("left"!==r)return e;d.append(e,{p:n.p,od:i.oi})}}else if(void 0!==i.od&&n.p[o]==i.p[o]){if(!v)return e;if(void 0===n.oi)return e;delete n.od}}return d.append(e,n),e},i(0)(d,d.transformComponent,d.checkValidOp,d.append);var s=i(3);d.registerSubtype(s),e.exports=d},function(e,n,i){var t=e.exports={name:"text0",uri:"http://sharejs.org/types/textv0",create:function(e){if(null!=e&&"string"!=typeof e)throw new Error("Initial data must be a string");return e||""}},l=function(e,n,i){return e.slice(0,n)+i+e.slice(n)},r=function(e){if("number"!=typeof e.p)throw new Error("component missing position field");if("string"==typeof e.i==("string"==typeof e.d))throw new Error("component needs an i or d field");if(e.p<0)throw new Error("position cannot be negative")},o=function(e){for(var n=0;n=i.p+i.d.length)p(e,{d:n.d,p:n.p-i.d.length});else if(n.p+n.d.length<=i.p)p(e,n);else{var o={d:"",p:n.p};n.pi.p+i.d.length&&(o.d+=n.d.slice(i.p+i.d.length-n.p));var f=Math.max(n.p,i.p),u=Math.min(n.p+n.d.length,i.p+i.d.length),s=n.d.slice(f-n.p,u-n.p),a=i.d.slice(f-i.p,u-i.p);if(s!==a)throw new Error("Delete ops delete different text in the same region of the document");""!==o.d&&(o.p=d(o.p,i),p(e,o))}return e},u=function(e){return null!=e.i?{d:e.i,p:e.p}:{i:e.d,p:e.p}};t.invert=function(e){e=e.slice().reverse();for(var n=0;n 2 | 3 | 4 | Collaborative Ace Editor 5 | 6 | 7 | 8 | 9 | 10 | 11 | 67 | 68 | 69 |
70 |

Ace Editor

71 |
    72 |
    
     73 |     
    74 | 75 | 281 | 282 | -------------------------------------------------------------------------------- /clients/code-mirror.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Collaborative Ace Editor 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 63 | 64 | 65 |
    66 |

    Code Mirror

    67 |
      68 |
      69 | 70 |
      71 |
      72 | 73 | 286 | 287 | -------------------------------------------------------------------------------- /sharedb-dist/rich-text.min.js: -------------------------------------------------------------------------------- 1 | !function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=20)}([,,,,,,,,function(t,e,r){function n(t){return null===t||void 0===t}function i(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&!(t.length>0&&"number"!=typeof t[0]))}function o(t,e,r){var o,a;if(n(t)||n(e))return!1;if(t.prototype!==e.prototype)return!1;if(f(t))return!!f(e)&&(t=s.call(t),e=s.call(e),p(t,e,r));if(i(t)){if(!i(e))return!1;if(t.length!==e.length)return!1;for(o=0;o=0;o--)if(h[o]!=c[o])return!1;for(o=h.length-1;o>=0;o--)if(a=h[o],!p(t[a],e[a],r))return!1;return typeof t==typeof e}var s=Array.prototype.slice,u=r(25),f=r(26),p=t.exports=function(t,e,r){return r||(r={}),t===e||(t instanceof Date&&e instanceof Date?t.getTime()===e.getTime():!t||!e||"object"!=typeof t&&"object"!=typeof e?r.strict?t===e:t==e:o(t,e,r))}},function(t,e,r){"use strict";var n=Object.prototype.hasOwnProperty,i=Object.prototype.toString,o=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===i.call(t)},s=function(t){if(!t||"[object Object]"!==i.call(t))return!1;var e=n.call(t,"constructor"),r=t.constructor&&t.constructor.prototype&&n.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!e&&!r)return!1;var o;for(o in t);return void 0===o||n.call(t,o)};t.exports=function t(){var e,r,n,i,u,f,p=arguments[0],a=1,h=arguments.length,c=!1;for("boolean"==typeof p&&(c=p,p=arguments[1]||{},a=2),(null==p||"object"!=typeof p&&"function"!=typeof p)&&(p={});a0&&(r.attributes=e),this.push(r))},f.prototype.delete=function(t){return t<=0?this:this.push({delete:t})},f.prototype.retain=function(t,e){if(t<=0)return this;var r={retain:t};return null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(r.attributes=e),this.push(r)},f.prototype.push=function(t){var e=this.ops.length,r=this.ops[e-1];if(t=o(!0,{},t),"object"==typeof r){if("number"==typeof t.delete&&"number"==typeof r.delete)return this.ops[e-1]={delete:r.delete+t.delete},this;if("number"==typeof r.delete&&null!=t.insert&&(e-=1,"object"!=typeof(r=this.ops[e-1])))return this.ops.unshift(t),this;if(i(t.attributes,r.attributes)){if("string"==typeof t.insert&&"string"==typeof r.insert)return this.ops[e-1]={insert:r.insert+t.insert},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this;if("number"==typeof t.retain&&"number"==typeof r.retain)return this.ops[e-1]={retain:r.retain+t.retain},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this}}return e===this.ops.length?this.ops.push(t):this.ops.splice(e,0,t),this},f.prototype.filter=function(t){return this.ops.filter(t)},f.prototype.forEach=function(t){this.ops.forEach(t)},f.prototype.map=function(t){return this.ops.map(t)},f.prototype.partition=function(t){var e=[],r=[];return this.forEach(function(n){(t(n)?e:r).push(n)}),[e,r]},f.prototype.reduce=function(t,e){return this.ops.reduce(t,e)},f.prototype.chop=function(){var t=this.ops[this.ops.length-1];return t&&t.retain&&!t.attributes&&this.ops.pop(),this},f.prototype.length=function(){return this.reduce(function(t,e){return t+s.length(e)},0)},f.prototype.slice=function(t,e){t=t||0,"number"!=typeof e&&(e=1/0);for(var r=[],n=s.iterator(this.ops),i=0;i0&&(e.push(t.ops[0]),e.ops=e.ops.concat(t.ops.slice(1))),e},f.prototype.diff=function(t,e){if(this.ops===t.ops)return new f;var r=[this,t].map(function(e){return e.map(function(r){if(null!=r.insert)return"string"==typeof r.insert?r.insert:u;var n=e===t?"on":"with";throw new Error("diff() called "+n+" non-document")}).join("")}),o=new f,p=n(r[0],r[1],e),a=s.iterator(this.ops),h=s.iterator(t.ops);return p.forEach(function(t){for(var e=t[1].length;e>0;){var r=0;switch(t[0]){case n.INSERT:r=Math.min(h.peekLength(),e),o.push(h.next(r));break;case n.DELETE:r=Math.min(e,a.peekLength()),a.next(r),o.delete(r);break;case n.EQUAL:r=Math.min(a.peekLength(),h.peekLength(),e);var u=a.next(r),f=h.next(r);i(u.insert,f.insert)?o.retain(r,s.attributes.diff(u.attributes,f.attributes)):o.push(f).delete(r)}e-=r}}),o.chop()},f.prototype.eachLine=function(t,e){e=e||"\n";for(var r=s.iterator(this.ops),n=new f,i=0;r.hasNext();){if("insert"!==r.peekType())return;var o=r.peek(),u=s.length(o)-r.peekLength(),p="string"==typeof o.insert?o.insert.indexOf(e,u)-u:-1;if(p<0)n.push(r.next());else if(p>0)n.push(r.next(p));else{if(!1===t(n,r.next(1).attributes||{},i))return;i+=1,n=new f}}n.length()>0&&t(n,{},i)},f.prototype.transform=function(t,e){if(e=!!e,"number"==typeof t)return this.transformPosition(t,e);for(var r=s.iterator(this.ops),n=s.iterator(t.ops),i=new f;r.hasNext()||n.hasNext();)if("insert"!==r.peekType()||!e&&"insert"===n.peekType())if("insert"===n.peekType())i.push(n.next());else{var o=Math.min(r.peekLength(),n.peekLength()),u=r.next(o),p=n.next(o);if(u.delete)continue;p.delete?i.push(p):i.retain(o,s.attributes.transform(u.attributes,p.attributes,e))}else i.retain(s.length(r.next()));return i.chop()},f.prototype.transformPosition=function(t,e){e=!!e;for(var r=s.iterator(this.ops),n=0;r.hasNext()&&n<=t;){var i=r.peekLength(),o=r.peekType();r.next(),"delete"!==o?("insert"===o&&(ne.length?t:e,s=t.length>e.length?e:t,u=o.indexOf(s);if(-1!=u)return n=[[g,o.substring(0,u)],[b,s],[g,o.substring(u+s.length)]],t.length>e.length&&(n[0][0]=n[2][0]=l),n;if(1==s.length)return[[l,t],[g,e]];var p=f(t,e);if(p){var a=p[0],h=p[1],c=p[2],y=p[3],v=p[4],d=r(a,c),m=r(h,y);return d.concat([[b,v]],m)}return i(t,e)}function i(t,e){for(var r=t.length,n=e.length,i=Math.ceil((r+n)/2),s=i,u=2*i,f=new Array(u),p=new Array(u),a=0;ar)y+=2;else if(w>n)b+=2;else if(c){var O=s+h-x;if(O>=0&&O=A)return o(t,e,j,w)}}}for(var E=-m+v;E<=m-d;E+=2){var A,O=s+E;A=E==-m||E!=m&&p[O-1]r)d+=2;else if(L>n)v+=2;else if(!c){var k=s+h-E;if(k>=0&&k=A)return o(t,e,j,w)}}}}return[[l,t],[g,e]]}function o(t,e,n,i){var o=t.substring(0,n),s=e.substring(0,i),u=t.substring(n),f=e.substring(i),p=r(o,s),a=r(u,f);return p.concat(a)}function s(t,e){if(!t||!e||t.charAt(0)!=e.charAt(0))return 0;for(var r=0,n=Math.min(t.length,e.length),i=n,o=0;r=t.length?[n,i,o,f,h]:null}var n=t.length>e.length?t:e,i=t.length>e.length?e:t;if(n.length<4||2*i.lengthp[4].length?f:p:f;var a,h,c,l;return t.length>e.length?(a=o[0],h=o[1],c=o[2],l=o[3]):(c=o[0],l=o[1],a=o[2],h=o[3]),[a,h,c,l,o[4]]}function p(t){t.push([b,""]);for(var e,r=0,n=0,i=0,o="",f="";r1?(0!==n&&0!==i&&(e=s(f,o),0!==e&&(r-n-i>0&&t[r-n-i-1][0]==b?t[r-n-i-1][1]+=f.substring(0,e):(t.splice(0,0,[b,f.substring(0,e)]),r++),f=f.substring(e),o=o.substring(e)),0!==(e=u(f,o))&&(t[r][1]=f.substring(f.length-e)+t[r][1],f=f.substring(0,f.length-e),o=o.substring(0,o.length-e))),0===n?t.splice(r-i,n+i,[g,f]):0===i?t.splice(r-n,n+i,[l,o]):t.splice(r-n-i,n+i,[l,o],[g,f]),r=r-n-i+(n?1:0)+(i?1:0)+1):0!==r&&t[r-1][0]==b?(t[r-1][1]+=t[r][1],t.splice(r,1)):r++,i=0,n=0,o="",f=""}""===t[t.length-1][1]&&t.pop();var a=!1;for(r=1;r0&&n.splice(i+2,0,[s[0],u]),c(n,i,3)}return t}function c(t,e,r){for(var n=e+r-1;n>=0&&n>=e-1;n--)if(n+10?n:void 0},diff:function(t,e){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var r=Object.keys(t).concat(Object.keys(e)).reduce(function(r,n){return i(t[n],e[n])||(r[n]=void 0===e[n]?null:e[n]),r},{});return Object.keys(r).length>0?r:void 0},transform:function(t,e,r){if("object"!=typeof t)return e;if("object"==typeof e){if(!r)return e;var n=Object.keys(e).reduce(function(r,n){return void 0===t[n]&&(r[n]=e[n]),r},{});return Object.keys(n).length>0?n:void 0}}},iterator:function(t){return new n(t)},length:function(t){return"number"==typeof t.delete?t.delete:"number"==typeof t.retain?t.retain:"string"==typeof t.insert?t.insert.length:1}};n.prototype.hasNext=function(){return this.peekLength()<1/0},n.prototype.next=function(t){t||(t=1/0);var e=this.ops[this.index];if(e){var r=this.offset,n=s.length(e);if(t>=n-r?(t=n-r,this.index+=1,this.offset=0):this.offset+=t,"number"==typeof e.delete)return{delete:t};var i={};return e.attributes&&(i.attributes=e.attributes),"number"==typeof e.retain?i.retain=t:"string"==typeof e.insert?i.insert=e.insert.substr(r,t):i.insert=e.insert,i}return{retain:1/0}},n.prototype.peek=function(){return this.ops[this.index]},n.prototype.peekLength=function(){return this.ops[this.index]?s.length(this.ops[this.index])-this.offset:1/0},n.prototype.peekType=function(){return this.ops[this.index]?"number"==typeof this.ops[this.index].delete?"delete":"number"==typeof this.ops[this.index].retain?"retain":"insert":"retain"},t.exports=s}]); -------------------------------------------------------------------------------- /sharedb-dist/sharedb-client.min.js: -------------------------------------------------------------------------------- 1 | !function(t){function e(n){if(i[n])return i[n].exports;var r=i[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var i={};e.m=t,e.c=i,e.d=function(t,i,n){e.o(t,i)||Object.defineProperty(t,i,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=28)}([function(t,e){function i(t,e,i,n){var r=function(t,i,n,r){e(n,t,i,"left"),e(r,i,t,"right")},s=t.transformX=function(t,e){i(t),i(e);for(var o=[],l=0;l=n||s!==e.p[r])return null}return i},h.canOpAffectPath=function(t,e){return null!=h.commonLengthForOps({p:e},t)},h.transformComponent=function(t,e,i,s){e=l(e);var o=h.commonLengthForOps(i,e),p=h.commonLengthForOps(e,i),a=e.p.length,u=i.p.length;if((null!=e.na||e.t)&&a++,(null!=i.na||i.t)&&u++,null!=p&&u>a&&e.p[p]==i.p[p])if(void 0!==e.ld){var f=l(i);f.p=f.p.slice(a),e.ld=h.apply(l(e.ld),[f])}else if(void 0!==e.od){var f=l(i);f.p=f.p.slice(a),e.od=h.apply(l(e.od),[f])}if(null!=o){var d=a==u,f=i;if(null==e.si&&null==e.sd||null==i.si&&null==i.sd||(n(e),f=l(i),n(f)),f.t&&c[f.t]){if(e.t&&e.t===f.t){var v=c[e.t].transform(e.o,f.o,s);if(v.length>0)if(null!=e.si||null!=e.sd)for(var g=e.p,y=0;y_&&e.p[o]--,m>w?e.p[o]++:m===w&&_>w&&(e.p[o]++,m===b&&e.lm++),b>_?e.lm--:b===_&&b>m&&e.lm--,b>w?e.lm++:b===w&&(w>_&&b>m||w<_&&bm?e.lm++:b===_&&e.lm--)}else if(void 0!==e.li&&void 0===e.ld&&d){var m=i.p[o],b=i.lm;g=e.p[o],g>m&&e.p[o]--,g>b&&e.p[o]++}else{var m=i.p[o],b=i.lm;g=e.p[o],g===m?e.p[o]=b:(g>m&&e.p[o]--,g>b?e.p[o]++:g===b&&m>b&&e.p[o]++)}else if(void 0!==i.oi&&void 0!==i.od){if(e.p[o]===i.p[o]){if(void 0===e.oi||!d)return t;if("right"===s)return t;e.od=i.oi}}else if(void 0!==i.oi){if(void 0!==e.oi&&e.p[o]===i.p[o]){if("left"!==s)return t;h.append(t,{p:e.p,od:i.oi})}}else if(void 0!==i.od&&e.p[o]==i.p[o]){if(!d)return t;if(void 0===e.oi)return t;delete e.od}}return h.append(t,e),t},i(0)(h,h.transformComponent,h.checkValidOp,h.append);var a=i(3);h.registerSubtype(a),t.exports=h},function(t,e,i){var n=t.exports={name:"text0",uri:"http://sharejs.org/types/textv0",create:function(t){if(null!=t&&"string"!=typeof t)throw new Error("Initial data must be a string");return t||""}},r=function(t,e,i){return t.slice(0,e)+i+t.slice(e)},s=function(t){if("number"!=typeof t.p)throw new Error("component missing position field");if("string"==typeof t.i==("string"==typeof t.d))throw new Error("component needs an i or d field");if(t.p<0)throw new Error("position cannot be negative")},o=function(t){for(var e=0;e=i.p+i.d.length)l(t,{d:e.d,p:e.p-i.d.length});else if(e.p+e.d.length<=i.p)l(t,e);else{var o={d:"",p:e.p};e.pi.p+i.d.length&&(o.d+=e.d.slice(i.p+i.d.length-e.p));var c=Math.max(e.p,i.p),p=Math.min(e.p+e.d.length,i.p+i.d.length),a=e.d.slice(c-e.p,p-e.p),u=i.d.slice(c-i.p,p-i.p);if(a!==u)throw new Error("Delete ops delete different text in the same region of the document");""!==o.d&&(o.p=h(o.p,i),l(t,o))}return t},p=function(t){return null!=t.i?{d:t.i,p:t.p}:{i:t.d,p:t.p}};n.invert=function(t){t=t.slice().reverse();for(var e=0;e1)for(var i=1;ithis.version?this.fetch(e):e&&e()}if(this.version>t.v)return e&&e();this.version=t.v;var n=void 0===t.type?p.defaultType:t.type;this._setType(n),this.data=this.type&&this.type.deserialize?this.type.deserialize(t.data):t.data,this.emit("load"),e&&e()},n.prototype.whenNothingPending=function(t){if(this.hasPending())return void this.once("nothing pending",t);t()},n.prototype.hasPending=function(){return!!(this.inflightOp||this.pendingOps.length||this.inflightFetch.length||this.inflightSubscribe.length||this.inflightUnsubscribe.length||this.pendingFetch.length)},n.prototype.hasWritePending=function(){return!(!this.inflightOp&&!this.pendingOps.length)},n.prototype._emitNothingPending=function(){this.hasWritePending()||(this.emit("no write pending"),this.hasPending()||this.emit("nothing pending"))},n.prototype._emitResponseError=function(t,e){if(e)return e(t),void this._emitNothingPending();this._emitNothingPending(),this.emit("error",t)},n.prototype._handleFetch=function(t,e){var i=this.inflightFetch.shift();if(t)return this._emitResponseError(t,i);this.ingestSnapshot(e,i),this._emitNothingPending()},n.prototype._handleSubscribe=function(t,e){var i=this.inflightSubscribe.shift();if(t)return this._emitResponseError(t,i);this.wantSubscribe&&(this.subscribed=!0),this.ingestSnapshot(e,i),this._emitNothingPending()},n.prototype._handleUnsubscribe=function(t){var e=this.inflightUnsubscribe.shift();if(t)return this._emitResponseError(t,e);e&&e(),this._emitNothingPending()},n.prototype._handleOp=function(t,e){if(t)return this.inflightOp?(4002===t.code&&(t=null),this._rollback(t)):this.emit("error",t);if(this.inflightOp&&e.src===this.inflightOp.src&&e.seq===this.inflightOp.seq)return void this._opAcknowledged(e);if(null==this.version||e.v>this.version)return void this.fetch();if(!(e.v1){this.applyStack||(this.applyStack=[]);for(var n=this.applyStack.length,r=0;r0)return void(this.applyStack.length=t);var e=this.applyStack[0];if(this.applyStack=null,e){var i=this.pendingOps.indexOf(e);if(-1!==i)for(var n=this.pendingOps.splice(i),i=0;i0&&this._events[t].length>r&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),"function"==typeof console.trace&&console.trace()),this},i.prototype.on=i.prototype.addListener,i.prototype.once=function(t,e){function i(){this.removeListener(t,i),r||(r=!0,e.apply(this,arguments))}if(!n(e))throw TypeError("listener must be a function");var r=!1;return i.listener=e,this.on(t,i),this},i.prototype.removeListener=function(t,e){var i,r,o,l;if(!n(e))throw TypeError("listener must be a function");if(!this._events||!this._events[t])return this;if(i=this._events[t],o=i.length,r=-1,i===e||n(i.listener)&&i.listener===e)delete this._events[t],this._events.removeListener&&this.emit("removeListener",t,e);else if(s(i)){for(l=o;l-- >0;)if(i[l]===e||i[l].listener&&i[l].listener===e){r=l;break}if(r<0)return this;1===i.length?(i.length=0,delete this._events[t]):i.splice(r,1),this._events.removeListener&&this.emit("removeListener",t,e)}return this},i.prototype.removeAllListeners=function(t){var e,i;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);return this.removeAllListeners("removeListener"),this._events={},this}if(i=this._events[t],n(i))this.removeListener(t,i);else if(i)for(;i.length;)this.removeListener(t,i[i.length-1]);return delete this._events[t],this},i.prototype.listeners=function(t){return this._events&&this._events[t]?n(this._events[t])?[this._events[t]]:this._events[t].slice():[]},i.prototype.listenerCount=function(t){if(this._events){var e=this._events[t];if(n(e))return 1;if(e)return e.length}return 0},i.listenerCount=function(t,e){return t.listenerCount(e)}},function(t,e,i){"use strict";function n(t){t&&s(this,"message",{configurable:!0,value:t,writable:!0});var e=this.constructor.name;e&&e!==this.name&&s(this,"name",{configurable:!0,value:e,writable:!0}),o(this,this.constructor)}function r(t,e){if(null==e||e===Error)e=n;else if("function"!=typeof e)throw new TypeError("super_ should be a function");var i;if("string"==typeof t)i=t,t=function(){e.apply(this,arguments)},l&&(l(t,i),i=null);else if("function"!=typeof t)throw new TypeError("constructor should be either a string or a function");t.super_=t.super=e;var r={constructor:{configurable:!0,value:t,writable:!0}};return null!=i&&(r.name={configurable:!0,value:i,writable:!0}),t.prototype=Object.create(e.prototype,r),t}var s=Object.defineProperty,o=Error.captureStackTrace;o||(o=function(t){var e=new Error;s(t,"stack",{configurable:!0,get:function(){var t=e.stack;return s(this,"stack",{value:t}),t},set:function(e){s(t,"stack",{configurable:!0,value:e,writable:!0})}})}),n.prototype=Object.create(Error.prototype,{constructor:{configurable:!0,value:n,writable:!0}});var l=function(){function t(t,e){return s(t,"name",{configurable:!0,value:e})}try{var e=function(){};if(t(e,"foo"),"foo"===e.name)return t}catch(t){}}();e=t.exports=r,e.BaseError=n},function(t,e){function i(){}e.doNothing=i,e.hasKeys=function(t){for(var e in t)return!0;return!1}}]); --------------------------------------------------------------------------------