loose_leaf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +21 -0
- data/README.md +136 -0
- data/Rakefile +29 -0
- data/app/assets/javascripts/loose_leaf/index.js +3408 -0
- data/app/channels/loose_leaf/collaboration_channel.rb +56 -0
- data/app/controllers/loose_leaf/application_controller.rb +4 -0
- data/lib/loose_leaf.rb +8 -0
- data/lib/loose_leaf/document.rb +99 -0
- data/lib/loose_leaf/document_attribute.rb +90 -0
- data/lib/loose_leaf/engine.rb +16 -0
- data/lib/loose_leaf/version.rb +3 -0
- data/lib/tasks/loose_leaf_tasks.rake +4 -0
- metadata +342 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 37fd14f6f259b07014bf83e22f256491738d6a44
|
4
|
+
data.tar.gz: 908f93f8a735d94a554933f1f0ba4696e1486c7b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d7fded0b64c6e35b3942e76848f1f986d11e258455c33f0c2446911409a5ddb6bb54723bb6069a26e41272c5999b44cece90fdcb954b3aea9beae8f22f7dd8c
|
7
|
+
data.tar.gz: 039596f3ea7fbdc0cdaf948de57ed4c107c4dc2cc546c1ba915e3044702ee7fda9ae147f072f5706eedbfc37e8c603913f88af795d0d2e259753414deab89d57
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright 2015 Hayden Ball
|
2
|
+
Copyright 2016 Akira Takahashi
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# LooseLeaf [![Build Status](https://travis-ci.org/rike422/loose-leaf.svg?branch=master)](https://travis-ci.org/rike422/loose-leaf) [![Coverage Status](https://coveralls.io/repos/github/rike422/loose-leaf/badge.svg?branch=master)](https://coveralls.io/github/rike422/loose-leaf?branch=master) [![Code Climate](https://codeclimate.com/github/rike422/loose-leaf/badges/gpa.svg)](https://codeclimate.com/github/rike422/loose-leaf) [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
|
2
|
+
|
3
|
+
|
4
|
+
LooseLeaf is a Rails Engine that allows Real-Time collaboration between users.
|
5
|
+
|
6
|
+
LooseLeaf is still a work in progress, and currently only text attributes may
|
7
|
+
be collaboratively text editor.
|
8
|
+
|
9
|
+
This project inspired by [Collabrate](https://github.com/ball-hayden/Collaborate)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'together'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
## Getting Started
|
24
|
+
|
25
|
+
### Prerequisites
|
26
|
+
|
27
|
+
You will need to have ActionCable set up. In particular, you will need an
|
28
|
+
`ApplicationCable::Channel` and `ApplicationCable::Connection`.
|
29
|
+
|
30
|
+
More information about setting up ActionCable can be found in its README.
|
31
|
+
|
32
|
+
### Model Setup
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
class Document < ActiveRecord::Base
|
36
|
+
# Include the LooseLeaf::Document concern
|
37
|
+
include LooseLeaf::Document
|
38
|
+
|
39
|
+
# Choose which attributes may be edited collaboratively
|
40
|
+
collaborative_attributes :body, :title
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
Adding `collaborative_attributes` will define an extra attribute on the model
|
45
|
+
prefixed with `collaborative_` (e.g `collaborative_body`). We must use that
|
46
|
+
over `body` whenever we wish to allow realtime collaboration.
|
47
|
+
|
48
|
+
Bear in mind that the `collaborative_` attributes are stored only in the Rails
|
49
|
+
cache. You must save these attributes where appropriate:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
document = Document.first
|
53
|
+
document.body = document.collaborative_body
|
54
|
+
document.save!
|
55
|
+
|
56
|
+
# Or, using the commit_collaborative_attributes convenience method:
|
57
|
+
|
58
|
+
document.commit_collaborative_attributes(:body)
|
59
|
+
document.commit_collaborative_attributes(:body, :title)
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
### Channel Setup
|
64
|
+
|
65
|
+
You will need to set up a collaboration channel for each model that is being
|
66
|
+
collaboratively edited.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class DocumentChannel < LooseLeaf::CollaborationChannel
|
70
|
+
private
|
71
|
+
|
72
|
+
# Set the Model class that we are editing.
|
73
|
+
def collaborative_model
|
74
|
+
Document
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
### View
|
80
|
+
|
81
|
+
As mentioned in Model Setup, we must use `collaborative_` attributes over normal
|
82
|
+
attributes when getting the values of collaborative attributes:
|
83
|
+
|
84
|
+
```erb
|
85
|
+
<%# app/views/documents/show.html.erb %>
|
86
|
+
|
87
|
+
<input id="title" type="text" value="<%= @document.collaborative_title %>">
|
88
|
+
|
89
|
+
<textarea id="body" rows="8" cols="40"><%= @document.collaborative_body %></textarea>
|
90
|
+
|
91
|
+
<%= link_to 'Back', documents_path %>
|
92
|
+
|
93
|
+
<script>
|
94
|
+
var documentId = <%= @document.id %>
|
95
|
+
</script>
|
96
|
+
```
|
97
|
+
|
98
|
+
### JavaScript
|
99
|
+
|
100
|
+
[![Standard - JavaScript Style Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
|
101
|
+
|
102
|
+
#### Sprockets
|
103
|
+
|
104
|
+
Add `loose-leaf` to your `application.coffee` asset, after actionable:
|
105
|
+
|
106
|
+
```coffeescript
|
107
|
+
#= require cable
|
108
|
+
#= require loose-leaf
|
109
|
+
```
|
110
|
+
|
111
|
+
Then, wherever appropriate:
|
112
|
+
|
113
|
+
```js
|
114
|
+
|
115
|
+
// Create a new ActionCable consumer
|
116
|
+
const cable = Cable.createConsumer('ws://localhost:28080')
|
117
|
+
|
118
|
+
// Set up our collaboration object. `documentId` is (as you may expect) the ID
|
119
|
+
// of the document that is being edited.
|
120
|
+
const looseLeaf = new LooseLeaf(cable, 'DocumentChannel', documentId)
|
121
|
+
|
122
|
+
// We now specify the two attributes we are editing.
|
123
|
+
collaborativeTitle = looseLeaf.addAttribute('title')
|
124
|
+
collaborativeBody = looseLeaf.addAttribute('body')
|
125
|
+
|
126
|
+
new LooseLeaf.Adapters.TextAreaAdapter(collaborativeTitle, '#title')
|
127
|
+
new LooseLeaf.Adapters.TextAreaAdapter(collaborativeBody, '#body')
|
128
|
+
|
129
|
+
```
|
130
|
+
|
131
|
+
#### npm or yuan
|
132
|
+
|
133
|
+
```
|
134
|
+
npm install loose-leaf
|
135
|
+
```
|
136
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
8
|
+
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = 'LooseLeaf'
|
14
|
+
rdoc.options << '--line-numbers'
|
15
|
+
rdoc.rdoc_files.include('README.md')
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'bundler/gem_tasks'
|
20
|
+
|
21
|
+
require 'rspec/core/rake_task'
|
22
|
+
|
23
|
+
load 'rails/tasks/engine.rake'
|
24
|
+
load 'rails/tasks/statistics.rake'
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
RSpec::Core::RakeTask.new(:spec)
|
29
|
+
task default: [:spec, :lint]
|
@@ -0,0 +1,3408 @@
|
|
1
|
+
(function webpackUniversalModuleDefinition(root, factory) {
|
2
|
+
if(typeof exports === 'object' && typeof module === 'object')
|
3
|
+
module.exports = factory();
|
4
|
+
else if(typeof define === 'function' && define.amd)
|
5
|
+
define([], factory);
|
6
|
+
else if(typeof exports === 'object')
|
7
|
+
exports["LooseLeaf"] = factory();
|
8
|
+
else
|
9
|
+
root["LooseLeaf"] = factory();
|
10
|
+
})(this, function() {
|
11
|
+
return /******/ (function(modules) { // webpackBootstrap
|
12
|
+
/******/ // The module cache
|
13
|
+
/******/ var installedModules = {};
|
14
|
+
|
15
|
+
/******/ // The require function
|
16
|
+
/******/ function __webpack_require__(moduleId) {
|
17
|
+
|
18
|
+
/******/ // Check if module is in cache
|
19
|
+
/******/ if(installedModules[moduleId])
|
20
|
+
/******/ return installedModules[moduleId].exports;
|
21
|
+
|
22
|
+
/******/ // Create a new module (and put it into the cache)
|
23
|
+
/******/ var module = installedModules[moduleId] = {
|
24
|
+
/******/ exports: {},
|
25
|
+
/******/ id: moduleId,
|
26
|
+
/******/ loaded: false
|
27
|
+
/******/ };
|
28
|
+
|
29
|
+
/******/ // Execute the module function
|
30
|
+
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
31
|
+
|
32
|
+
/******/ // Flag the module as loaded
|
33
|
+
/******/ module.loaded = true;
|
34
|
+
|
35
|
+
/******/ // Return the exports of the module
|
36
|
+
/******/ return module.exports;
|
37
|
+
/******/ }
|
38
|
+
|
39
|
+
|
40
|
+
/******/ // expose the modules object (__webpack_modules__)
|
41
|
+
/******/ __webpack_require__.m = modules;
|
42
|
+
|
43
|
+
/******/ // expose the module cache
|
44
|
+
/******/ __webpack_require__.c = installedModules;
|
45
|
+
|
46
|
+
/******/ // __webpack_public_path__
|
47
|
+
/******/ __webpack_require__.p = "";
|
48
|
+
|
49
|
+
/******/ // Load entry module and return exports
|
50
|
+
/******/ return __webpack_require__(0);
|
51
|
+
/******/ })
|
52
|
+
/************************************************************************/
|
53
|
+
/******/ ([
|
54
|
+
/* 0 */
|
55
|
+
/***/ function(module, exports, __webpack_require__) {
|
56
|
+
|
57
|
+
'use strict';
|
58
|
+
|
59
|
+
var _looseLeaf = __webpack_require__(1);
|
60
|
+
|
61
|
+
var _looseLeaf2 = _interopRequireDefault(_looseLeaf);
|
62
|
+
|
63
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
64
|
+
|
65
|
+
module.exports = _looseLeaf2.default; // weak
|
66
|
+
|
67
|
+
/***/ },
|
68
|
+
/* 1 */
|
69
|
+
/***/ function(module, exports, __webpack_require__) {
|
70
|
+
|
71
|
+
'use strict';
|
72
|
+
|
73
|
+
Object.defineProperty(exports, "__esModule", {
|
74
|
+
value: true
|
75
|
+
});
|
76
|
+
exports.default = undefined;
|
77
|
+
|
78
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
79
|
+
|
80
|
+
var _class, _temp; // weak
|
81
|
+
|
82
|
+
var _attributeCable = __webpack_require__(2);
|
83
|
+
|
84
|
+
var _attributeCable2 = _interopRequireDefault(_attributeCable);
|
85
|
+
|
86
|
+
var _index = __webpack_require__(23);
|
87
|
+
|
88
|
+
var _index2 = _interopRequireDefault(_index);
|
89
|
+
|
90
|
+
var _cable = __webpack_require__(22);
|
91
|
+
|
92
|
+
var _cable2 = _interopRequireDefault(_cable);
|
93
|
+
|
94
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
95
|
+
|
96
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
97
|
+
|
98
|
+
var _events = __webpack_require__(25);
|
99
|
+
|
100
|
+
var _events2 = _interopRequireDefault(_events);
|
101
|
+
|
102
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
103
|
+
|
104
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
105
|
+
|
106
|
+
var LooseLeaf = (_temp = _class = function () {
|
107
|
+
function LooseLeaf(cable, channel, documentId) {
|
108
|
+
_classCallCheck(this, LooseLeaf);
|
109
|
+
|
110
|
+
this.documentId = documentId;
|
111
|
+
this.cable = new _cable2.default(this, cable, channel);
|
112
|
+
this.attributes = {};
|
113
|
+
}
|
114
|
+
|
115
|
+
_createClass(LooseLeaf, [{
|
116
|
+
key: 'addAttribute',
|
117
|
+
value: function addAttribute(attribute) {
|
118
|
+
this.attributes[attribute] = new _collaborativeAttribute2.default(this, attribute);
|
119
|
+
return this.attributes[attribute];
|
120
|
+
}
|
121
|
+
}, {
|
122
|
+
key: 'destroy',
|
123
|
+
value: function destroy() {
|
124
|
+
Object.values(this.attributes).forEach(function (attribute) {
|
125
|
+
return attribute.destroy();
|
126
|
+
});
|
127
|
+
this.cable.destroy();
|
128
|
+
}
|
129
|
+
}]);
|
130
|
+
|
131
|
+
return LooseLeaf;
|
132
|
+
}(), _class.AttributeCable = _attributeCable2.default, _class.Adapters = _index2.default, _class.Cable = _cable2.default, _class.CollaborativeAttribute = _collaborativeAttribute2.default, _class.Events = _events2.default, _temp);
|
133
|
+
exports.default = LooseLeaf;
|
134
|
+
;
|
135
|
+
|
136
|
+
/***/ },
|
137
|
+
/* 2 */
|
138
|
+
/***/ function(module, exports, __webpack_require__) {
|
139
|
+
|
140
|
+
'use strict';
|
141
|
+
|
142
|
+
Object.defineProperty(exports, "__esModule", {
|
143
|
+
value: true
|
144
|
+
});
|
145
|
+
exports.default = undefined;
|
146
|
+
|
147
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // weak
|
148
|
+
|
149
|
+
var _ot = __webpack_require__(3);
|
150
|
+
|
151
|
+
var _ot2 = _interopRequireDefault(_ot);
|
152
|
+
|
153
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
154
|
+
|
155
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
156
|
+
|
157
|
+
var _cable = __webpack_require__(22);
|
158
|
+
|
159
|
+
var _cable2 = _interopRequireDefault(_cable);
|
160
|
+
|
161
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
162
|
+
|
163
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
164
|
+
|
165
|
+
var AttributeCable = function () {
|
166
|
+
function AttributeCable(collaborativeAttribute, cable, attribute) {
|
167
|
+
_classCallCheck(this, AttributeCable);
|
168
|
+
|
169
|
+
this.collaborativeAttribute = collaborativeAttribute;
|
170
|
+
this.cable = cable;
|
171
|
+
this.attribute = attribute;
|
172
|
+
this.unackedOps = [];
|
173
|
+
this.version = 0;
|
174
|
+
this.cable.addAttribute(this.attribute, this);
|
175
|
+
}
|
176
|
+
|
177
|
+
_createClass(AttributeCable, [{
|
178
|
+
key: 'destroy',
|
179
|
+
value: function destroy() {
|
180
|
+
this.cable.removeAttribute(this.attribute);
|
181
|
+
true;
|
182
|
+
}
|
183
|
+
}, {
|
184
|
+
key: 'receiveAttribute',
|
185
|
+
value: function receiveAttribute(data) {
|
186
|
+
this.version = data.version;
|
187
|
+
}
|
188
|
+
}, {
|
189
|
+
key: 'sendOperation',
|
190
|
+
value: function sendOperation(data) {
|
191
|
+
this.version++;
|
192
|
+
data.attribute = this.attribute;
|
193
|
+
data.version = this.version;
|
194
|
+
|
195
|
+
console.log('Send ' + this.attribute + ' version ' + data.version + ': ' + data.operation.toString());
|
196
|
+
|
197
|
+
this.unackedOps.push(data.version);
|
198
|
+
return this.cable.sendOperation(data);
|
199
|
+
}
|
200
|
+
}, {
|
201
|
+
key: 'receiveOperation',
|
202
|
+
value: function receiveOperation(data) {
|
203
|
+
data.operation = _ot2.default.TextOperation.fromJSON(data.operation);
|
204
|
+
this.version = data.version;
|
205
|
+
|
206
|
+
console.log('Receive ' + this.attribute + ' version ' + data.version + ': ' + data.operation.toString() + ' from ' + data.client_id);
|
207
|
+
|
208
|
+
if (data.client_id === this.cable.clientId) {
|
209
|
+
return this.receiveAck(data);
|
210
|
+
} else {
|
211
|
+
return this.receiveRemoteOperation(data);
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}, {
|
215
|
+
key: 'receiveAck',
|
216
|
+
value: function receiveAck(data) {
|
217
|
+
var ackIndex = this.unackedOps.indexOf(data.sent_version);
|
218
|
+
|
219
|
+
if (ackIndex > -1) {
|
220
|
+
this.unackedOps.splice(ackIndex, 1);
|
221
|
+
this.collaborativeAttribute.receiveAck(data);
|
222
|
+
} else {
|
223
|
+
console.warn('Operation ' + data.sent_version + ' reAcked');
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}, {
|
227
|
+
key: 'receiveRemoteOperation',
|
228
|
+
value: function receiveRemoteOperation(data) {
|
229
|
+
return this.collaborativeAttribute.remoteOperation(data);
|
230
|
+
}
|
231
|
+
}]);
|
232
|
+
|
233
|
+
return AttributeCable;
|
234
|
+
}();
|
235
|
+
|
236
|
+
exports.default = AttributeCable;
|
237
|
+
;
|
238
|
+
|
239
|
+
/***/ },
|
240
|
+
/* 3 */
|
241
|
+
/***/ function(module, exports, __webpack_require__) {
|
242
|
+
|
243
|
+
exports.version = '0.0.15';
|
244
|
+
|
245
|
+
exports.TextOperation = __webpack_require__(4);
|
246
|
+
exports.SimpleTextOperation = __webpack_require__(5);
|
247
|
+
exports.Client = __webpack_require__(6);
|
248
|
+
exports.Server = __webpack_require__(7);
|
249
|
+
exports.Selection = __webpack_require__(8);
|
250
|
+
exports.EditorSocketIOServer = __webpack_require__(9);
|
251
|
+
|
252
|
+
|
253
|
+
/***/ },
|
254
|
+
/* 4 */
|
255
|
+
/***/ function(module, exports, __webpack_require__) {
|
256
|
+
|
257
|
+
if (typeof ot === 'undefined') {
|
258
|
+
// Export for browsers
|
259
|
+
var ot = {};
|
260
|
+
}
|
261
|
+
|
262
|
+
ot.TextOperation = (function () {
|
263
|
+
'use strict';
|
264
|
+
|
265
|
+
// Constructor for new operations.
|
266
|
+
function TextOperation () {
|
267
|
+
if (!this || this.constructor !== TextOperation) {
|
268
|
+
// => function was called without 'new'
|
269
|
+
return new TextOperation();
|
270
|
+
}
|
271
|
+
|
272
|
+
// When an operation is applied to an input string, you can think of this as
|
273
|
+
// if an imaginary cursor runs over the entire string and skips over some
|
274
|
+
// parts, deletes some parts and inserts characters at some positions. These
|
275
|
+
// actions (skip/delete/insert) are stored as an array in the "ops" property.
|
276
|
+
this.ops = [];
|
277
|
+
// An operation's baseLength is the length of every string the operation
|
278
|
+
// can be applied to.
|
279
|
+
this.baseLength = 0;
|
280
|
+
// The targetLength is the length of every string that results from applying
|
281
|
+
// the operation on a valid input string.
|
282
|
+
this.targetLength = 0;
|
283
|
+
}
|
284
|
+
|
285
|
+
TextOperation.prototype.equals = function (other) {
|
286
|
+
if (this.baseLength !== other.baseLength) { return false; }
|
287
|
+
if (this.targetLength !== other.targetLength) { return false; }
|
288
|
+
if (this.ops.length !== other.ops.length) { return false; }
|
289
|
+
for (var i = 0; i < this.ops.length; i++) {
|
290
|
+
if (this.ops[i] !== other.ops[i]) { return false; }
|
291
|
+
}
|
292
|
+
return true;
|
293
|
+
};
|
294
|
+
|
295
|
+
// Operation are essentially lists of ops. There are three types of ops:
|
296
|
+
//
|
297
|
+
// * Retain ops: Advance the cursor position by a given number of characters.
|
298
|
+
// Represented by positive ints.
|
299
|
+
// * Insert ops: Insert a given string at the current cursor position.
|
300
|
+
// Represented by strings.
|
301
|
+
// * Delete ops: Delete the next n characters. Represented by negative ints.
|
302
|
+
|
303
|
+
var isRetain = TextOperation.isRetain = function (op) {
|
304
|
+
return typeof op === 'number' && op > 0;
|
305
|
+
};
|
306
|
+
|
307
|
+
var isInsert = TextOperation.isInsert = function (op) {
|
308
|
+
return typeof op === 'string';
|
309
|
+
};
|
310
|
+
|
311
|
+
var isDelete = TextOperation.isDelete = function (op) {
|
312
|
+
return typeof op === 'number' && op < 0;
|
313
|
+
};
|
314
|
+
|
315
|
+
|
316
|
+
// After an operation is constructed, the user of the library can specify the
|
317
|
+
// actions of an operation (skip/insert/delete) with these three builder
|
318
|
+
// methods. They all return the operation for convenient chaining.
|
319
|
+
|
320
|
+
// Skip over a given number of characters.
|
321
|
+
TextOperation.prototype.retain = function (n) {
|
322
|
+
if (typeof n !== 'number') {
|
323
|
+
throw new Error("retain expects an integer");
|
324
|
+
}
|
325
|
+
if (n === 0) { return this; }
|
326
|
+
this.baseLength += n;
|
327
|
+
this.targetLength += n;
|
328
|
+
if (isRetain(this.ops[this.ops.length-1])) {
|
329
|
+
// The last op is a retain op => we can merge them into one op.
|
330
|
+
this.ops[this.ops.length-1] += n;
|
331
|
+
} else {
|
332
|
+
// Create a new op.
|
333
|
+
this.ops.push(n);
|
334
|
+
}
|
335
|
+
return this;
|
336
|
+
};
|
337
|
+
|
338
|
+
// Insert a string at the current position.
|
339
|
+
TextOperation.prototype.insert = function (str) {
|
340
|
+
if (typeof str !== 'string') {
|
341
|
+
throw new Error("insert expects a string");
|
342
|
+
}
|
343
|
+
if (str === '') { return this; }
|
344
|
+
this.targetLength += str.length;
|
345
|
+
var ops = this.ops;
|
346
|
+
if (isInsert(ops[ops.length-1])) {
|
347
|
+
// Merge insert op.
|
348
|
+
ops[ops.length-1] += str;
|
349
|
+
} else if (isDelete(ops[ops.length-1])) {
|
350
|
+
// It doesn't matter when an operation is applied whether the operation
|
351
|
+
// is delete(3), insert("something") or insert("something"), delete(3).
|
352
|
+
// Here we enforce that in this case, the insert op always comes first.
|
353
|
+
// This makes all operations that have the same effect when applied to
|
354
|
+
// a document of the right length equal in respect to the `equals` method.
|
355
|
+
if (isInsert(ops[ops.length-2])) {
|
356
|
+
ops[ops.length-2] += str;
|
357
|
+
} else {
|
358
|
+
ops[ops.length] = ops[ops.length-1];
|
359
|
+
ops[ops.length-2] = str;
|
360
|
+
}
|
361
|
+
} else {
|
362
|
+
ops.push(str);
|
363
|
+
}
|
364
|
+
return this;
|
365
|
+
};
|
366
|
+
|
367
|
+
// Delete a string at the current position.
|
368
|
+
TextOperation.prototype['delete'] = function (n) {
|
369
|
+
if (typeof n === 'string') { n = n.length; }
|
370
|
+
if (typeof n !== 'number') {
|
371
|
+
throw new Error("delete expects an integer or a string");
|
372
|
+
}
|
373
|
+
if (n === 0) { return this; }
|
374
|
+
if (n > 0) { n = -n; }
|
375
|
+
this.baseLength -= n;
|
376
|
+
if (isDelete(this.ops[this.ops.length-1])) {
|
377
|
+
this.ops[this.ops.length-1] += n;
|
378
|
+
} else {
|
379
|
+
this.ops.push(n);
|
380
|
+
}
|
381
|
+
return this;
|
382
|
+
};
|
383
|
+
|
384
|
+
// Tests whether this operation has no effect.
|
385
|
+
TextOperation.prototype.isNoop = function () {
|
386
|
+
return this.ops.length === 0 || (this.ops.length === 1 && isRetain(this.ops[0]));
|
387
|
+
};
|
388
|
+
|
389
|
+
// Pretty printing.
|
390
|
+
TextOperation.prototype.toString = function () {
|
391
|
+
// map: build a new array by applying a function to every element in an old
|
392
|
+
// array.
|
393
|
+
var map = Array.prototype.map || function (fn) {
|
394
|
+
var arr = this;
|
395
|
+
var newArr = [];
|
396
|
+
for (var i = 0, l = arr.length; i < l; i++) {
|
397
|
+
newArr[i] = fn(arr[i]);
|
398
|
+
}
|
399
|
+
return newArr;
|
400
|
+
};
|
401
|
+
return map.call(this.ops, function (op) {
|
402
|
+
if (isRetain(op)) {
|
403
|
+
return "retain " + op;
|
404
|
+
} else if (isInsert(op)) {
|
405
|
+
return "insert '" + op + "'";
|
406
|
+
} else {
|
407
|
+
return "delete " + (-op);
|
408
|
+
}
|
409
|
+
}).join(', ');
|
410
|
+
};
|
411
|
+
|
412
|
+
// Converts operation into a JSON value.
|
413
|
+
TextOperation.prototype.toJSON = function () {
|
414
|
+
return this.ops;
|
415
|
+
};
|
416
|
+
|
417
|
+
// Converts a plain JS object into an operation and validates it.
|
418
|
+
TextOperation.fromJSON = function (ops) {
|
419
|
+
var o = new TextOperation();
|
420
|
+
for (var i = 0, l = ops.length; i < l; i++) {
|
421
|
+
var op = ops[i];
|
422
|
+
if (isRetain(op)) {
|
423
|
+
o.retain(op);
|
424
|
+
} else if (isInsert(op)) {
|
425
|
+
o.insert(op);
|
426
|
+
} else if (isDelete(op)) {
|
427
|
+
o['delete'](op);
|
428
|
+
} else {
|
429
|
+
throw new Error("unknown operation: " + JSON.stringify(op));
|
430
|
+
}
|
431
|
+
}
|
432
|
+
return o;
|
433
|
+
};
|
434
|
+
|
435
|
+
// Apply an operation to a string, returning a new string. Throws an error if
|
436
|
+
// there's a mismatch between the input string and the operation.
|
437
|
+
TextOperation.prototype.apply = function (str) {
|
438
|
+
var operation = this;
|
439
|
+
if (str.length !== operation.baseLength) {
|
440
|
+
throw new Error("The operation's base length must be equal to the string's length.");
|
441
|
+
}
|
442
|
+
var newStr = [], j = 0;
|
443
|
+
var strIndex = 0;
|
444
|
+
var ops = this.ops;
|
445
|
+
for (var i = 0, l = ops.length; i < l; i++) {
|
446
|
+
var op = ops[i];
|
447
|
+
if (isRetain(op)) {
|
448
|
+
if (strIndex + op > str.length) {
|
449
|
+
throw new Error("Operation can't retain more characters than are left in the string.");
|
450
|
+
}
|
451
|
+
// Copy skipped part of the old string.
|
452
|
+
newStr[j++] = str.slice(strIndex, strIndex + op);
|
453
|
+
strIndex += op;
|
454
|
+
} else if (isInsert(op)) {
|
455
|
+
// Insert string.
|
456
|
+
newStr[j++] = op;
|
457
|
+
} else { // delete op
|
458
|
+
strIndex -= op;
|
459
|
+
}
|
460
|
+
}
|
461
|
+
if (strIndex !== str.length) {
|
462
|
+
throw new Error("The operation didn't operate on the whole string.");
|
463
|
+
}
|
464
|
+
return newStr.join('');
|
465
|
+
};
|
466
|
+
|
467
|
+
// Computes the inverse of an operation. The inverse of an operation is the
|
468
|
+
// operation that reverts the effects of the operation, e.g. when you have an
|
469
|
+
// operation 'insert("hello "); skip(6);' then the inverse is 'delete("hello ");
|
470
|
+
// skip(6);'. The inverse should be used for implementing undo.
|
471
|
+
TextOperation.prototype.invert = function (str) {
|
472
|
+
var strIndex = 0;
|
473
|
+
var inverse = new TextOperation();
|
474
|
+
var ops = this.ops;
|
475
|
+
for (var i = 0, l = ops.length; i < l; i++) {
|
476
|
+
var op = ops[i];
|
477
|
+
if (isRetain(op)) {
|
478
|
+
inverse.retain(op);
|
479
|
+
strIndex += op;
|
480
|
+
} else if (isInsert(op)) {
|
481
|
+
inverse['delete'](op.length);
|
482
|
+
} else { // delete op
|
483
|
+
inverse.insert(str.slice(strIndex, strIndex - op));
|
484
|
+
strIndex -= op;
|
485
|
+
}
|
486
|
+
}
|
487
|
+
return inverse;
|
488
|
+
};
|
489
|
+
|
490
|
+
// Compose merges two consecutive operations into one operation, that
|
491
|
+
// preserves the changes of both. Or, in other words, for each input string S
|
492
|
+
// and a pair of consecutive operations A and B,
|
493
|
+
// apply(apply(S, A), B) = apply(S, compose(A, B)) must hold.
|
494
|
+
TextOperation.prototype.compose = function (operation2) {
|
495
|
+
var operation1 = this;
|
496
|
+
if (operation1.targetLength !== operation2.baseLength) {
|
497
|
+
throw new Error("The base length of the second operation has to be the target length of the first operation");
|
498
|
+
}
|
499
|
+
|
500
|
+
var operation = new TextOperation(); // the combined operation
|
501
|
+
var ops1 = operation1.ops, ops2 = operation2.ops; // for fast access
|
502
|
+
var i1 = 0, i2 = 0; // current index into ops1 respectively ops2
|
503
|
+
var op1 = ops1[i1++], op2 = ops2[i2++]; // current ops
|
504
|
+
while (true) {
|
505
|
+
// Dispatch on the type of op1 and op2
|
506
|
+
if (typeof op1 === 'undefined' && typeof op2 === 'undefined') {
|
507
|
+
// end condition: both ops1 and ops2 have been processed
|
508
|
+
break;
|
509
|
+
}
|
510
|
+
|
511
|
+
if (isDelete(op1)) {
|
512
|
+
operation['delete'](op1);
|
513
|
+
op1 = ops1[i1++];
|
514
|
+
continue;
|
515
|
+
}
|
516
|
+
if (isInsert(op2)) {
|
517
|
+
operation.insert(op2);
|
518
|
+
op2 = ops2[i2++];
|
519
|
+
continue;
|
520
|
+
}
|
521
|
+
|
522
|
+
if (typeof op1 === 'undefined') {
|
523
|
+
throw new Error("Cannot compose operations: first operation is too short.");
|
524
|
+
}
|
525
|
+
if (typeof op2 === 'undefined') {
|
526
|
+
throw new Error("Cannot compose operations: first operation is too long.");
|
527
|
+
}
|
528
|
+
|
529
|
+
if (isRetain(op1) && isRetain(op2)) {
|
530
|
+
if (op1 > op2) {
|
531
|
+
operation.retain(op2);
|
532
|
+
op1 = op1 - op2;
|
533
|
+
op2 = ops2[i2++];
|
534
|
+
} else if (op1 === op2) {
|
535
|
+
operation.retain(op1);
|
536
|
+
op1 = ops1[i1++];
|
537
|
+
op2 = ops2[i2++];
|
538
|
+
} else {
|
539
|
+
operation.retain(op1);
|
540
|
+
op2 = op2 - op1;
|
541
|
+
op1 = ops1[i1++];
|
542
|
+
}
|
543
|
+
} else if (isInsert(op1) && isDelete(op2)) {
|
544
|
+
if (op1.length > -op2) {
|
545
|
+
op1 = op1.slice(-op2);
|
546
|
+
op2 = ops2[i2++];
|
547
|
+
} else if (op1.length === -op2) {
|
548
|
+
op1 = ops1[i1++];
|
549
|
+
op2 = ops2[i2++];
|
550
|
+
} else {
|
551
|
+
op2 = op2 + op1.length;
|
552
|
+
op1 = ops1[i1++];
|
553
|
+
}
|
554
|
+
} else if (isInsert(op1) && isRetain(op2)) {
|
555
|
+
if (op1.length > op2) {
|
556
|
+
operation.insert(op1.slice(0, op2));
|
557
|
+
op1 = op1.slice(op2);
|
558
|
+
op2 = ops2[i2++];
|
559
|
+
} else if (op1.length === op2) {
|
560
|
+
operation.insert(op1);
|
561
|
+
op1 = ops1[i1++];
|
562
|
+
op2 = ops2[i2++];
|
563
|
+
} else {
|
564
|
+
operation.insert(op1);
|
565
|
+
op2 = op2 - op1.length;
|
566
|
+
op1 = ops1[i1++];
|
567
|
+
}
|
568
|
+
} else if (isRetain(op1) && isDelete(op2)) {
|
569
|
+
if (op1 > -op2) {
|
570
|
+
operation['delete'](op2);
|
571
|
+
op1 = op1 + op2;
|
572
|
+
op2 = ops2[i2++];
|
573
|
+
} else if (op1 === -op2) {
|
574
|
+
operation['delete'](op2);
|
575
|
+
op1 = ops1[i1++];
|
576
|
+
op2 = ops2[i2++];
|
577
|
+
} else {
|
578
|
+
operation['delete'](op1);
|
579
|
+
op2 = op2 + op1;
|
580
|
+
op1 = ops1[i1++];
|
581
|
+
}
|
582
|
+
} else {
|
583
|
+
throw new Error(
|
584
|
+
"This shouldn't happen: op1: " +
|
585
|
+
JSON.stringify(op1) + ", op2: " +
|
586
|
+
JSON.stringify(op2)
|
587
|
+
);
|
588
|
+
}
|
589
|
+
}
|
590
|
+
return operation;
|
591
|
+
};
|
592
|
+
|
593
|
+
function getSimpleOp (operation, fn) {
|
594
|
+
var ops = operation.ops;
|
595
|
+
var isRetain = TextOperation.isRetain;
|
596
|
+
switch (ops.length) {
|
597
|
+
case 1:
|
598
|
+
return ops[0];
|
599
|
+
case 2:
|
600
|
+
return isRetain(ops[0]) ? ops[1] : (isRetain(ops[1]) ? ops[0] : null);
|
601
|
+
case 3:
|
602
|
+
if (isRetain(ops[0]) && isRetain(ops[2])) { return ops[1]; }
|
603
|
+
}
|
604
|
+
return null;
|
605
|
+
}
|
606
|
+
|
607
|
+
function getStartIndex (operation) {
|
608
|
+
if (isRetain(operation.ops[0])) { return operation.ops[0]; }
|
609
|
+
return 0;
|
610
|
+
}
|
611
|
+
|
612
|
+
// When you use ctrl-z to undo your latest changes, you expect the program not
|
613
|
+
// to undo every single keystroke but to undo your last sentence you wrote at
|
614
|
+
// a stretch or the deletion you did by holding the backspace key down. This
|
615
|
+
// This can be implemented by composing operations on the undo stack. This
|
616
|
+
// method can help decide whether two operations should be composed. It
|
617
|
+
// returns true if the operations are consecutive insert operations or both
|
618
|
+
// operations delete text at the same position. You may want to include other
|
619
|
+
// factors like the time since the last change in your decision.
|
620
|
+
TextOperation.prototype.shouldBeComposedWith = function (other) {
|
621
|
+
if (this.isNoop() || other.isNoop()) { return true; }
|
622
|
+
|
623
|
+
var startA = getStartIndex(this), startB = getStartIndex(other);
|
624
|
+
var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other);
|
625
|
+
if (!simpleA || !simpleB) { return false; }
|
626
|
+
|
627
|
+
if (isInsert(simpleA) && isInsert(simpleB)) {
|
628
|
+
return startA + simpleA.length === startB;
|
629
|
+
}
|
630
|
+
|
631
|
+
if (isDelete(simpleA) && isDelete(simpleB)) {
|
632
|
+
// there are two possibilities to delete: with backspace and with the
|
633
|
+
// delete key.
|
634
|
+
return (startB - simpleB === startA) || startA === startB;
|
635
|
+
}
|
636
|
+
|
637
|
+
return false;
|
638
|
+
};
|
639
|
+
|
640
|
+
// Decides whether two operations should be composed with each other
|
641
|
+
// if they were inverted, that is
|
642
|
+
// `shouldBeComposedWith(a, b) = shouldBeComposedWithInverted(b^{-1}, a^{-1})`.
|
643
|
+
TextOperation.prototype.shouldBeComposedWithInverted = function (other) {
|
644
|
+
if (this.isNoop() || other.isNoop()) { return true; }
|
645
|
+
|
646
|
+
var startA = getStartIndex(this), startB = getStartIndex(other);
|
647
|
+
var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other);
|
648
|
+
if (!simpleA || !simpleB) { return false; }
|
649
|
+
|
650
|
+
if (isInsert(simpleA) && isInsert(simpleB)) {
|
651
|
+
return startA + simpleA.length === startB || startA === startB;
|
652
|
+
}
|
653
|
+
|
654
|
+
if (isDelete(simpleA) && isDelete(simpleB)) {
|
655
|
+
return startB - simpleB === startA;
|
656
|
+
}
|
657
|
+
|
658
|
+
return false;
|
659
|
+
};
|
660
|
+
|
661
|
+
// Transform takes two operations A and B that happened concurrently and
|
662
|
+
// produces two operations A' and B' (in an array) such that
|
663
|
+
// `apply(apply(S, A), B') = apply(apply(S, B), A')`. This function is the
|
664
|
+
// heart of OT.
|
665
|
+
TextOperation.transform = function (operation1, operation2) {
|
666
|
+
if (operation1.baseLength !== operation2.baseLength) {
|
667
|
+
throw new Error("Both operations have to have the same base length");
|
668
|
+
}
|
669
|
+
|
670
|
+
var operation1prime = new TextOperation();
|
671
|
+
var operation2prime = new TextOperation();
|
672
|
+
var ops1 = operation1.ops, ops2 = operation2.ops;
|
673
|
+
var i1 = 0, i2 = 0;
|
674
|
+
var op1 = ops1[i1++], op2 = ops2[i2++];
|
675
|
+
while (true) {
|
676
|
+
// At every iteration of the loop, the imaginary cursor that both
|
677
|
+
// operation1 and operation2 have that operates on the input string must
|
678
|
+
// have the same position in the input string.
|
679
|
+
|
680
|
+
if (typeof op1 === 'undefined' && typeof op2 === 'undefined') {
|
681
|
+
// end condition: both ops1 and ops2 have been processed
|
682
|
+
break;
|
683
|
+
}
|
684
|
+
|
685
|
+
// next two cases: one or both ops are insert ops
|
686
|
+
// => insert the string in the corresponding prime operation, skip it in
|
687
|
+
// the other one. If both op1 and op2 are insert ops, prefer op1.
|
688
|
+
if (isInsert(op1)) {
|
689
|
+
operation1prime.insert(op1);
|
690
|
+
operation2prime.retain(op1.length);
|
691
|
+
op1 = ops1[i1++];
|
692
|
+
continue;
|
693
|
+
}
|
694
|
+
if (isInsert(op2)) {
|
695
|
+
operation1prime.retain(op2.length);
|
696
|
+
operation2prime.insert(op2);
|
697
|
+
op2 = ops2[i2++];
|
698
|
+
continue;
|
699
|
+
}
|
700
|
+
|
701
|
+
if (typeof op1 === 'undefined') {
|
702
|
+
throw new Error("Cannot compose operations: first operation is too short.");
|
703
|
+
}
|
704
|
+
if (typeof op2 === 'undefined') {
|
705
|
+
throw new Error("Cannot compose operations: first operation is too long.");
|
706
|
+
}
|
707
|
+
|
708
|
+
var minl;
|
709
|
+
if (isRetain(op1) && isRetain(op2)) {
|
710
|
+
// Simple case: retain/retain
|
711
|
+
if (op1 > op2) {
|
712
|
+
minl = op2;
|
713
|
+
op1 = op1 - op2;
|
714
|
+
op2 = ops2[i2++];
|
715
|
+
} else if (op1 === op2) {
|
716
|
+
minl = op2;
|
717
|
+
op1 = ops1[i1++];
|
718
|
+
op2 = ops2[i2++];
|
719
|
+
} else {
|
720
|
+
minl = op1;
|
721
|
+
op2 = op2 - op1;
|
722
|
+
op1 = ops1[i1++];
|
723
|
+
}
|
724
|
+
operation1prime.retain(minl);
|
725
|
+
operation2prime.retain(minl);
|
726
|
+
} else if (isDelete(op1) && isDelete(op2)) {
|
727
|
+
// Both operations delete the same string at the same position. We don't
|
728
|
+
// need to produce any operations, we just skip over the delete ops and
|
729
|
+
// handle the case that one operation deletes more than the other.
|
730
|
+
if (-op1 > -op2) {
|
731
|
+
op1 = op1 - op2;
|
732
|
+
op2 = ops2[i2++];
|
733
|
+
} else if (op1 === op2) {
|
734
|
+
op1 = ops1[i1++];
|
735
|
+
op2 = ops2[i2++];
|
736
|
+
} else {
|
737
|
+
op2 = op2 - op1;
|
738
|
+
op1 = ops1[i1++];
|
739
|
+
}
|
740
|
+
// next two cases: delete/retain and retain/delete
|
741
|
+
} else if (isDelete(op1) && isRetain(op2)) {
|
742
|
+
if (-op1 > op2) {
|
743
|
+
minl = op2;
|
744
|
+
op1 = op1 + op2;
|
745
|
+
op2 = ops2[i2++];
|
746
|
+
} else if (-op1 === op2) {
|
747
|
+
minl = op2;
|
748
|
+
op1 = ops1[i1++];
|
749
|
+
op2 = ops2[i2++];
|
750
|
+
} else {
|
751
|
+
minl = -op1;
|
752
|
+
op2 = op2 + op1;
|
753
|
+
op1 = ops1[i1++];
|
754
|
+
}
|
755
|
+
operation1prime['delete'](minl);
|
756
|
+
} else if (isRetain(op1) && isDelete(op2)) {
|
757
|
+
if (op1 > -op2) {
|
758
|
+
minl = -op2;
|
759
|
+
op1 = op1 + op2;
|
760
|
+
op2 = ops2[i2++];
|
761
|
+
} else if (op1 === -op2) {
|
762
|
+
minl = op1;
|
763
|
+
op1 = ops1[i1++];
|
764
|
+
op2 = ops2[i2++];
|
765
|
+
} else {
|
766
|
+
minl = op1;
|
767
|
+
op2 = op2 + op1;
|
768
|
+
op1 = ops1[i1++];
|
769
|
+
}
|
770
|
+
operation2prime['delete'](minl);
|
771
|
+
} else {
|
772
|
+
throw new Error("The two operations aren't compatible");
|
773
|
+
}
|
774
|
+
}
|
775
|
+
|
776
|
+
return [operation1prime, operation2prime];
|
777
|
+
};
|
778
|
+
|
779
|
+
return TextOperation;
|
780
|
+
|
781
|
+
}());
|
782
|
+
|
783
|
+
// Export for CommonJS
|
784
|
+
if (true) {
|
785
|
+
module.exports = ot.TextOperation;
|
786
|
+
}
|
787
|
+
|
788
|
+
/***/ },
|
789
|
+
/* 5 */
|
790
|
+
/***/ function(module, exports, __webpack_require__) {
|
791
|
+
|
792
|
+
if (typeof ot === 'undefined') {
|
793
|
+
// Export for browsers
|
794
|
+
var ot = {};
|
795
|
+
}
|
796
|
+
|
797
|
+
ot.SimpleTextOperation = (function (global) {
|
798
|
+
|
799
|
+
var TextOperation = global.ot ? global.ot.TextOperation : __webpack_require__(4);
|
800
|
+
|
801
|
+
function SimpleTextOperation () {}
|
802
|
+
|
803
|
+
|
804
|
+
// Insert the string `str` at the zero-based `position` in the document.
|
805
|
+
function Insert (str, position) {
|
806
|
+
if (!this || this.constructor !== SimpleTextOperation) {
|
807
|
+
// => function was called without 'new'
|
808
|
+
return new Insert(str, position);
|
809
|
+
}
|
810
|
+
this.str = str;
|
811
|
+
this.position = position;
|
812
|
+
}
|
813
|
+
|
814
|
+
Insert.prototype = new SimpleTextOperation();
|
815
|
+
SimpleTextOperation.Insert = Insert;
|
816
|
+
|
817
|
+
Insert.prototype.toString = function () {
|
818
|
+
return 'Insert(' + JSON.stringify(this.str) + ', ' + this.position + ')';
|
819
|
+
};
|
820
|
+
|
821
|
+
Insert.prototype.equals = function (other) {
|
822
|
+
return other instanceof Insert &&
|
823
|
+
this.str === other.str &&
|
824
|
+
this.position === other.position;
|
825
|
+
};
|
826
|
+
|
827
|
+
Insert.prototype.apply = function (doc) {
|
828
|
+
return doc.slice(0, this.position) + this.str + doc.slice(this.position);
|
829
|
+
};
|
830
|
+
|
831
|
+
|
832
|
+
// Delete `count` many characters at the zero-based `position` in the document.
|
833
|
+
function Delete (count, position) {
|
834
|
+
if (!this || this.constructor !== SimpleTextOperation) {
|
835
|
+
return new Delete(count, position);
|
836
|
+
}
|
837
|
+
this.count = count;
|
838
|
+
this.position = position;
|
839
|
+
}
|
840
|
+
|
841
|
+
Delete.prototype = new SimpleTextOperation();
|
842
|
+
SimpleTextOperation.Delete = Delete;
|
843
|
+
|
844
|
+
Delete.prototype.toString = function () {
|
845
|
+
return 'Delete(' + this.count + ', ' + this.position + ')';
|
846
|
+
};
|
847
|
+
|
848
|
+
Delete.prototype.equals = function (other) {
|
849
|
+
return other instanceof Delete &&
|
850
|
+
this.count === other.count &&
|
851
|
+
this.position === other.position;
|
852
|
+
};
|
853
|
+
|
854
|
+
Delete.prototype.apply = function (doc) {
|
855
|
+
return doc.slice(0, this.position) + doc.slice(this.position + this.count);
|
856
|
+
};
|
857
|
+
|
858
|
+
|
859
|
+
// An operation that does nothing. This is needed for the result of the
|
860
|
+
// transformation of two deletions of the same character.
|
861
|
+
function Noop () {
|
862
|
+
if (!this || this.constructor !== SimpleTextOperation) { return new Noop(); }
|
863
|
+
}
|
864
|
+
|
865
|
+
Noop.prototype = new SimpleTextOperation();
|
866
|
+
SimpleTextOperation.Noop = Noop;
|
867
|
+
|
868
|
+
Noop.prototype.toString = function () {
|
869
|
+
return 'Noop()';
|
870
|
+
};
|
871
|
+
|
872
|
+
Noop.prototype.equals = function (other) { return other instanceof Noop; };
|
873
|
+
|
874
|
+
Noop.prototype.apply = function (doc) { return doc; };
|
875
|
+
|
876
|
+
var noop = new Noop();
|
877
|
+
|
878
|
+
|
879
|
+
SimpleTextOperation.transform = function (a, b) {
|
880
|
+
if (a instanceof Noop || b instanceof Noop) { return [a, b]; }
|
881
|
+
|
882
|
+
if (a instanceof Insert && b instanceof Insert) {
|
883
|
+
if (a.position < b.position || (a.position === b.position && a.str < b.str)) {
|
884
|
+
return [a, new Insert(b.str, b.position + a.str.length)];
|
885
|
+
}
|
886
|
+
if (a.position > b.position || (a.position === b.position && a.str > b.str)) {
|
887
|
+
return [new Insert(a.str, a.position + b.str.length), b];
|
888
|
+
}
|
889
|
+
return [noop, noop];
|
890
|
+
}
|
891
|
+
|
892
|
+
if (a instanceof Insert && b instanceof Delete) {
|
893
|
+
if (a.position <= b.position) {
|
894
|
+
return [a, new Delete(b.count, b.position + a.str.length)];
|
895
|
+
}
|
896
|
+
if (a.position >= b.position + b.count) {
|
897
|
+
return [new Insert(a.str, a.position - b.count), b];
|
898
|
+
}
|
899
|
+
// Here, we have to delete the inserted string of operation a.
|
900
|
+
// That doesn't preserve the intention of operation a, but it's the only
|
901
|
+
// thing we can do to get a valid transform function.
|
902
|
+
return [noop, new Delete(b.count + a.str.length, b.position)];
|
903
|
+
}
|
904
|
+
|
905
|
+
if (a instanceof Delete && b instanceof Insert) {
|
906
|
+
if (a.position >= b.position) {
|
907
|
+
return [new Delete(a.count, a.position + b.str.length), b];
|
908
|
+
}
|
909
|
+
if (a.position + a.count <= b.position) {
|
910
|
+
return [a, new Insert(b.str, b.position - a.count)];
|
911
|
+
}
|
912
|
+
// Same problem as above. We have to delete the string that was inserted
|
913
|
+
// in operation b.
|
914
|
+
return [new Delete(a.count + b.str.length, a.position), noop];
|
915
|
+
}
|
916
|
+
|
917
|
+
if (a instanceof Delete && b instanceof Delete) {
|
918
|
+
if (a.position === b.position) {
|
919
|
+
if (a.count === b.count) {
|
920
|
+
return [noop, noop];
|
921
|
+
} else if (a.count < b.count) {
|
922
|
+
return [noop, new Delete(b.count - a.count, b.position)];
|
923
|
+
}
|
924
|
+
return [new Delete(a.count - b.count, a.position), noop];
|
925
|
+
}
|
926
|
+
if (a.position < b.position) {
|
927
|
+
if (a.position + a.count <= b.position) {
|
928
|
+
return [a, new Delete(b.count, b.position - a.count)];
|
929
|
+
}
|
930
|
+
if (a.position + a.count >= b.position + b.count) {
|
931
|
+
return [new Delete(a.count - b.count, a.position), noop];
|
932
|
+
}
|
933
|
+
return [
|
934
|
+
new Delete(b.position - a.position, a.position),
|
935
|
+
new Delete(b.position + b.count - (a.position + a.count), a.position)
|
936
|
+
];
|
937
|
+
}
|
938
|
+
if (a.position > b.position) {
|
939
|
+
if (a.position >= b.position + b.count) {
|
940
|
+
return [new Delete(a.count, a.position - b.count), b];
|
941
|
+
}
|
942
|
+
if (a.position + a.count <= b.position + b.count) {
|
943
|
+
return [noop, new Delete(b.count - a.count, b.position)];
|
944
|
+
}
|
945
|
+
return [
|
946
|
+
new Delete(a.position + a.count - (b.position + b.count), b.position),
|
947
|
+
new Delete(a.position - b.position, b.position)
|
948
|
+
];
|
949
|
+
}
|
950
|
+
}
|
951
|
+
};
|
952
|
+
|
953
|
+
// Convert a normal, composable `TextOperation` into an array of
|
954
|
+
// `SimpleTextOperation`s.
|
955
|
+
SimpleTextOperation.fromTextOperation = function (operation) {
|
956
|
+
var simpleOperations = [];
|
957
|
+
var index = 0;
|
958
|
+
for (var i = 0; i < operation.ops.length; i++) {
|
959
|
+
var op = operation.ops[i];
|
960
|
+
if (TextOperation.isRetain(op)) {
|
961
|
+
index += op;
|
962
|
+
} else if (TextOperation.isInsert(op)) {
|
963
|
+
simpleOperations.push(new Insert(op, index));
|
964
|
+
index += op.length;
|
965
|
+
} else {
|
966
|
+
simpleOperations.push(new Delete(Math.abs(op), index));
|
967
|
+
}
|
968
|
+
}
|
969
|
+
return simpleOperations;
|
970
|
+
};
|
971
|
+
|
972
|
+
|
973
|
+
return SimpleTextOperation;
|
974
|
+
})(this);
|
975
|
+
|
976
|
+
// Export for CommonJS
|
977
|
+
if (true) {
|
978
|
+
module.exports = ot.SimpleTextOperation;
|
979
|
+
}
|
980
|
+
|
981
|
+
/***/ },
|
982
|
+
/* 6 */
|
983
|
+
/***/ function(module, exports, __webpack_require__) {
|
984
|
+
|
985
|
+
// translation of https://github.com/djspiewak/cccp/blob/master/agent/src/main/scala/com/codecommit/cccp/agent/state.scala
|
986
|
+
|
987
|
+
if (typeof ot === 'undefined') {
|
988
|
+
var ot = {};
|
989
|
+
}
|
990
|
+
|
991
|
+
ot.Client = (function (global) {
|
992
|
+
'use strict';
|
993
|
+
|
994
|
+
// Client constructor
|
995
|
+
function Client (revision) {
|
996
|
+
this.revision = revision; // the next expected revision number
|
997
|
+
this.state = synchronized_; // start state
|
998
|
+
}
|
999
|
+
|
1000
|
+
Client.prototype.setState = function (state) {
|
1001
|
+
this.state = state;
|
1002
|
+
};
|
1003
|
+
|
1004
|
+
// Call this method when the user changes the document.
|
1005
|
+
Client.prototype.applyClient = function (operation) {
|
1006
|
+
this.setState(this.state.applyClient(this, operation));
|
1007
|
+
};
|
1008
|
+
|
1009
|
+
// Call this method with a new operation from the server
|
1010
|
+
Client.prototype.applyServer = function (operation) {
|
1011
|
+
this.revision++;
|
1012
|
+
this.setState(this.state.applyServer(this, operation));
|
1013
|
+
};
|
1014
|
+
|
1015
|
+
Client.prototype.serverAck = function () {
|
1016
|
+
this.revision++;
|
1017
|
+
this.setState(this.state.serverAck(this));
|
1018
|
+
};
|
1019
|
+
|
1020
|
+
Client.prototype.serverReconnect = function () {
|
1021
|
+
if (typeof this.state.resend === 'function') { this.state.resend(this); }
|
1022
|
+
};
|
1023
|
+
|
1024
|
+
// Transforms a selection from the latest known server state to the current
|
1025
|
+
// client state. For example, if we get from the server the information that
|
1026
|
+
// another user's cursor is at position 3, but the server hasn't yet received
|
1027
|
+
// our newest operation, an insertion of 5 characters at the beginning of the
|
1028
|
+
// document, the correct position of the other user's cursor in our current
|
1029
|
+
// document is 8.
|
1030
|
+
Client.prototype.transformSelection = function (selection) {
|
1031
|
+
return this.state.transformSelection(selection);
|
1032
|
+
};
|
1033
|
+
|
1034
|
+
// Override this method.
|
1035
|
+
Client.prototype.sendOperation = function (revision, operation) {
|
1036
|
+
throw new Error("sendOperation must be defined in child class");
|
1037
|
+
};
|
1038
|
+
|
1039
|
+
// Override this method.
|
1040
|
+
Client.prototype.applyOperation = function (operation) {
|
1041
|
+
throw new Error("applyOperation must be defined in child class");
|
1042
|
+
};
|
1043
|
+
|
1044
|
+
|
1045
|
+
// In the 'Synchronized' state, there is no pending operation that the client
|
1046
|
+
// has sent to the server.
|
1047
|
+
function Synchronized () {}
|
1048
|
+
Client.Synchronized = Synchronized;
|
1049
|
+
|
1050
|
+
Synchronized.prototype.applyClient = function (client, operation) {
|
1051
|
+
// When the user makes an edit, send the operation to the server and
|
1052
|
+
// switch to the 'AwaitingConfirm' state
|
1053
|
+
client.sendOperation(client.revision, operation);
|
1054
|
+
return new AwaitingConfirm(operation);
|
1055
|
+
};
|
1056
|
+
|
1057
|
+
Synchronized.prototype.applyServer = function (client, operation) {
|
1058
|
+
// When we receive a new operation from the server, the operation can be
|
1059
|
+
// simply applied to the current document
|
1060
|
+
client.applyOperation(operation);
|
1061
|
+
return this;
|
1062
|
+
};
|
1063
|
+
|
1064
|
+
Synchronized.prototype.serverAck = function (client) {
|
1065
|
+
throw new Error("There is no pending operation.");
|
1066
|
+
};
|
1067
|
+
|
1068
|
+
// Nothing to do because the latest server state and client state are the same.
|
1069
|
+
Synchronized.prototype.transformSelection = function (x) { return x; };
|
1070
|
+
|
1071
|
+
// Singleton
|
1072
|
+
var synchronized_ = new Synchronized();
|
1073
|
+
|
1074
|
+
|
1075
|
+
// In the 'AwaitingConfirm' state, there's one operation the client has sent
|
1076
|
+
// to the server and is still waiting for an acknowledgement.
|
1077
|
+
function AwaitingConfirm (outstanding) {
|
1078
|
+
// Save the pending operation
|
1079
|
+
this.outstanding = outstanding;
|
1080
|
+
}
|
1081
|
+
Client.AwaitingConfirm = AwaitingConfirm;
|
1082
|
+
|
1083
|
+
AwaitingConfirm.prototype.applyClient = function (client, operation) {
|
1084
|
+
// When the user makes an edit, don't send the operation immediately,
|
1085
|
+
// instead switch to 'AwaitingWithBuffer' state
|
1086
|
+
return new AwaitingWithBuffer(this.outstanding, operation);
|
1087
|
+
};
|
1088
|
+
|
1089
|
+
AwaitingConfirm.prototype.applyServer = function (client, operation) {
|
1090
|
+
// This is another client's operation. Visualization:
|
1091
|
+
//
|
1092
|
+
// /\
|
1093
|
+
// this.outstanding / \ operation
|
1094
|
+
// / \
|
1095
|
+
// \ /
|
1096
|
+
// pair[1] \ / pair[0] (new outstanding)
|
1097
|
+
// (can be applied \/
|
1098
|
+
// to the client's
|
1099
|
+
// current document)
|
1100
|
+
var pair = operation.constructor.transform(this.outstanding, operation);
|
1101
|
+
client.applyOperation(pair[1]);
|
1102
|
+
return new AwaitingConfirm(pair[0]);
|
1103
|
+
};
|
1104
|
+
|
1105
|
+
AwaitingConfirm.prototype.serverAck = function (client) {
|
1106
|
+
// The client's operation has been acknowledged
|
1107
|
+
// => switch to synchronized state
|
1108
|
+
return synchronized_;
|
1109
|
+
};
|
1110
|
+
|
1111
|
+
AwaitingConfirm.prototype.transformSelection = function (selection) {
|
1112
|
+
return selection.transform(this.outstanding);
|
1113
|
+
};
|
1114
|
+
|
1115
|
+
AwaitingConfirm.prototype.resend = function (client) {
|
1116
|
+
// The confirm didn't come because the client was disconnected.
|
1117
|
+
// Now that it has reconnected, we resend the outstanding operation.
|
1118
|
+
client.sendOperation(client.revision, this.outstanding);
|
1119
|
+
};
|
1120
|
+
|
1121
|
+
|
1122
|
+
// In the 'AwaitingWithBuffer' state, the client is waiting for an operation
|
1123
|
+
// to be acknowledged by the server while buffering the edits the user makes
|
1124
|
+
function AwaitingWithBuffer (outstanding, buffer) {
|
1125
|
+
// Save the pending operation and the user's edits since then
|
1126
|
+
this.outstanding = outstanding;
|
1127
|
+
this.buffer = buffer;
|
1128
|
+
}
|
1129
|
+
Client.AwaitingWithBuffer = AwaitingWithBuffer;
|
1130
|
+
|
1131
|
+
AwaitingWithBuffer.prototype.applyClient = function (client, operation) {
|
1132
|
+
// Compose the user's changes onto the buffer
|
1133
|
+
var newBuffer = this.buffer.compose(operation);
|
1134
|
+
return new AwaitingWithBuffer(this.outstanding, newBuffer);
|
1135
|
+
};
|
1136
|
+
|
1137
|
+
AwaitingWithBuffer.prototype.applyServer = function (client, operation) {
|
1138
|
+
// Operation comes from another client
|
1139
|
+
//
|
1140
|
+
// /\
|
1141
|
+
// this.outstanding / \ operation
|
1142
|
+
// / \
|
1143
|
+
// /\ /
|
1144
|
+
// this.buffer / \* / pair1[0] (new outstanding)
|
1145
|
+
// / \/
|
1146
|
+
// \ /
|
1147
|
+
// pair2[1] \ / pair2[0] (new buffer)
|
1148
|
+
// the transformed \/
|
1149
|
+
// operation -- can
|
1150
|
+
// be applied to the
|
1151
|
+
// client's current
|
1152
|
+
// document
|
1153
|
+
//
|
1154
|
+
// * pair1[1]
|
1155
|
+
var transform = operation.constructor.transform;
|
1156
|
+
var pair1 = transform(this.outstanding, operation);
|
1157
|
+
var pair2 = transform(this.buffer, pair1[1]);
|
1158
|
+
client.applyOperation(pair2[1]);
|
1159
|
+
return new AwaitingWithBuffer(pair1[0], pair2[0]);
|
1160
|
+
};
|
1161
|
+
|
1162
|
+
AwaitingWithBuffer.prototype.serverAck = function (client) {
|
1163
|
+
// The pending operation has been acknowledged
|
1164
|
+
// => send buffer
|
1165
|
+
client.sendOperation(client.revision, this.buffer);
|
1166
|
+
return new AwaitingConfirm(this.buffer);
|
1167
|
+
};
|
1168
|
+
|
1169
|
+
AwaitingWithBuffer.prototype.transformSelection = function (selection) {
|
1170
|
+
return selection.transform(this.outstanding).transform(this.buffer);
|
1171
|
+
};
|
1172
|
+
|
1173
|
+
AwaitingWithBuffer.prototype.resend = function (client) {
|
1174
|
+
// The confirm didn't come because the client was disconnected.
|
1175
|
+
// Now that it has reconnected, we resend the outstanding operation.
|
1176
|
+
client.sendOperation(client.revision, this.outstanding);
|
1177
|
+
};
|
1178
|
+
|
1179
|
+
|
1180
|
+
return Client;
|
1181
|
+
|
1182
|
+
}(this));
|
1183
|
+
|
1184
|
+
if (true) {
|
1185
|
+
module.exports = ot.Client;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
|
1189
|
+
/***/ },
|
1190
|
+
/* 7 */
|
1191
|
+
/***/ function(module, exports, __webpack_require__) {
|
1192
|
+
|
1193
|
+
if (typeof ot === 'undefined') {
|
1194
|
+
var ot = {};
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
ot.Server = (function (global) {
|
1198
|
+
'use strict';
|
1199
|
+
|
1200
|
+
// Constructor. Takes the current document as a string and optionally the array
|
1201
|
+
// of all operations.
|
1202
|
+
function Server (document, operations) {
|
1203
|
+
this.document = document;
|
1204
|
+
this.operations = operations || [];
|
1205
|
+
}
|
1206
|
+
|
1207
|
+
// Call this method whenever you receive an operation from a client.
|
1208
|
+
Server.prototype.receiveOperation = function (revision, operation) {
|
1209
|
+
if (revision < 0 || this.operations.length < revision) {
|
1210
|
+
throw new Error("operation revision not in history");
|
1211
|
+
}
|
1212
|
+
// Find all operations that the client didn't know of when it sent the
|
1213
|
+
// operation ...
|
1214
|
+
var concurrentOperations = this.operations.slice(revision);
|
1215
|
+
|
1216
|
+
// ... and transform the operation against all these operations ...
|
1217
|
+
var transform = operation.constructor.transform;
|
1218
|
+
for (var i = 0; i < concurrentOperations.length; i++) {
|
1219
|
+
operation = transform(operation, concurrentOperations[i])[0];
|
1220
|
+
}
|
1221
|
+
|
1222
|
+
// ... and apply that on the document.
|
1223
|
+
this.document = operation.apply(this.document);
|
1224
|
+
// Store operation in history.
|
1225
|
+
this.operations.push(operation);
|
1226
|
+
|
1227
|
+
// It's the caller's responsibility to send the operation to all connected
|
1228
|
+
// clients and an acknowledgement to the creator.
|
1229
|
+
return operation;
|
1230
|
+
};
|
1231
|
+
|
1232
|
+
return Server;
|
1233
|
+
|
1234
|
+
}(this));
|
1235
|
+
|
1236
|
+
if (true) {
|
1237
|
+
module.exports = ot.Server;
|
1238
|
+
}
|
1239
|
+
|
1240
|
+
/***/ },
|
1241
|
+
/* 8 */
|
1242
|
+
/***/ function(module, exports, __webpack_require__) {
|
1243
|
+
|
1244
|
+
if (typeof ot === 'undefined') {
|
1245
|
+
// Export for browsers
|
1246
|
+
var ot = {};
|
1247
|
+
}
|
1248
|
+
|
1249
|
+
ot.Selection = (function (global) {
|
1250
|
+
'use strict';
|
1251
|
+
|
1252
|
+
var TextOperation = global.ot ? global.ot.TextOperation : __webpack_require__(4);
|
1253
|
+
|
1254
|
+
// Range has `anchor` and `head` properties, which are zero-based indices into
|
1255
|
+
// the document. The `anchor` is the side of the selection that stays fixed,
|
1256
|
+
// `head` is the side of the selection where the cursor is. When both are
|
1257
|
+
// equal, the range represents a cursor.
|
1258
|
+
function Range (anchor, head) {
|
1259
|
+
this.anchor = anchor;
|
1260
|
+
this.head = head;
|
1261
|
+
}
|
1262
|
+
|
1263
|
+
Range.fromJSON = function (obj) {
|
1264
|
+
return new Range(obj.anchor, obj.head);
|
1265
|
+
};
|
1266
|
+
|
1267
|
+
Range.prototype.equals = function (other) {
|
1268
|
+
return this.anchor === other.anchor && this.head === other.head;
|
1269
|
+
};
|
1270
|
+
|
1271
|
+
Range.prototype.isEmpty = function () {
|
1272
|
+
return this.anchor === this.head;
|
1273
|
+
};
|
1274
|
+
|
1275
|
+
Range.prototype.transform = function (other) {
|
1276
|
+
function transformIndex (index) {
|
1277
|
+
var newIndex = index;
|
1278
|
+
var ops = other.ops;
|
1279
|
+
for (var i = 0, l = other.ops.length; i < l; i++) {
|
1280
|
+
if (TextOperation.isRetain(ops[i])) {
|
1281
|
+
index -= ops[i];
|
1282
|
+
} else if (TextOperation.isInsert(ops[i])) {
|
1283
|
+
newIndex += ops[i].length;
|
1284
|
+
} else {
|
1285
|
+
newIndex -= Math.min(index, -ops[i]);
|
1286
|
+
index += ops[i];
|
1287
|
+
}
|
1288
|
+
if (index < 0) { break; }
|
1289
|
+
}
|
1290
|
+
return newIndex;
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
var newAnchor = transformIndex(this.anchor);
|
1294
|
+
if (this.anchor === this.head) {
|
1295
|
+
return new Range(newAnchor, newAnchor);
|
1296
|
+
}
|
1297
|
+
return new Range(newAnchor, transformIndex(this.head));
|
1298
|
+
};
|
1299
|
+
|
1300
|
+
// A selection is basically an array of ranges. Every range represents a real
|
1301
|
+
// selection or a cursor in the document (when the start position equals the
|
1302
|
+
// end position of the range). The array must not be empty.
|
1303
|
+
function Selection (ranges) {
|
1304
|
+
this.ranges = ranges || [];
|
1305
|
+
}
|
1306
|
+
|
1307
|
+
Selection.Range = Range;
|
1308
|
+
|
1309
|
+
// Convenience method for creating selections only containing a single cursor
|
1310
|
+
// and no real selection range.
|
1311
|
+
Selection.createCursor = function (position) {
|
1312
|
+
return new Selection([new Range(position, position)]);
|
1313
|
+
};
|
1314
|
+
|
1315
|
+
Selection.fromJSON = function (obj) {
|
1316
|
+
var objRanges = obj.ranges || obj;
|
1317
|
+
for (var i = 0, ranges = []; i < objRanges.length; i++) {
|
1318
|
+
ranges[i] = Range.fromJSON(objRanges[i]);
|
1319
|
+
}
|
1320
|
+
return new Selection(ranges);
|
1321
|
+
};
|
1322
|
+
|
1323
|
+
Selection.prototype.equals = function (other) {
|
1324
|
+
if (this.position !== other.position) { return false; }
|
1325
|
+
if (this.ranges.length !== other.ranges.length) { return false; }
|
1326
|
+
// FIXME: Sort ranges before comparing them?
|
1327
|
+
for (var i = 0; i < this.ranges.length; i++) {
|
1328
|
+
if (!this.ranges[i].equals(other.ranges[i])) { return false; }
|
1329
|
+
}
|
1330
|
+
return true;
|
1331
|
+
};
|
1332
|
+
|
1333
|
+
Selection.prototype.somethingSelected = function () {
|
1334
|
+
for (var i = 0; i < this.ranges.length; i++) {
|
1335
|
+
if (!this.ranges[i].isEmpty()) { return true; }
|
1336
|
+
}
|
1337
|
+
return false;
|
1338
|
+
};
|
1339
|
+
|
1340
|
+
// Return the more current selection information.
|
1341
|
+
Selection.prototype.compose = function (other) {
|
1342
|
+
return other;
|
1343
|
+
};
|
1344
|
+
|
1345
|
+
// Update the selection with respect to an operation.
|
1346
|
+
Selection.prototype.transform = function (other) {
|
1347
|
+
for (var i = 0, newRanges = []; i < this.ranges.length; i++) {
|
1348
|
+
newRanges[i] = this.ranges[i].transform(other);
|
1349
|
+
}
|
1350
|
+
return new Selection(newRanges);
|
1351
|
+
};
|
1352
|
+
|
1353
|
+
return Selection;
|
1354
|
+
|
1355
|
+
}(this));
|
1356
|
+
|
1357
|
+
// Export for CommonJS
|
1358
|
+
if (true) {
|
1359
|
+
module.exports = ot.Selection;
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
|
1363
|
+
/***/ },
|
1364
|
+
/* 9 */
|
1365
|
+
/***/ function(module, exports, __webpack_require__) {
|
1366
|
+
|
1367
|
+
'use strict';
|
1368
|
+
|
1369
|
+
var EventEmitter = __webpack_require__(10).EventEmitter;
|
1370
|
+
var TextOperation = __webpack_require__(4);
|
1371
|
+
var WrappedOperation = __webpack_require__(11);
|
1372
|
+
var Server = __webpack_require__(7);
|
1373
|
+
var Selection = __webpack_require__(8);
|
1374
|
+
var util = __webpack_require__(12);
|
1375
|
+
|
1376
|
+
function EditorSocketIOServer (document, operations, docId, mayWrite) {
|
1377
|
+
EventEmitter.call(this);
|
1378
|
+
Server.call(this, document, operations);
|
1379
|
+
this.users = {};
|
1380
|
+
this.docId = docId;
|
1381
|
+
this.mayWrite = mayWrite || function (_, cb) { cb(true); };
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
util.inherits(EditorSocketIOServer, Server);
|
1385
|
+
extend(EditorSocketIOServer.prototype, EventEmitter.prototype);
|
1386
|
+
|
1387
|
+
function extend (target, source) {
|
1388
|
+
for (var key in source) {
|
1389
|
+
if (source.hasOwnProperty(key)) {
|
1390
|
+
target[key] = source[key];
|
1391
|
+
}
|
1392
|
+
}
|
1393
|
+
}
|
1394
|
+
|
1395
|
+
EditorSocketIOServer.prototype.addClient = function (socket) {
|
1396
|
+
var self = this;
|
1397
|
+
socket
|
1398
|
+
.join(this.docId)
|
1399
|
+
.emit('doc', {
|
1400
|
+
str: this.document,
|
1401
|
+
revision: this.operations.length,
|
1402
|
+
clients: this.users
|
1403
|
+
})
|
1404
|
+
.on('operation', function (revision, operation, selection) {
|
1405
|
+
self.mayWrite(socket, function (mayWrite) {
|
1406
|
+
if (!mayWrite) {
|
1407
|
+
console.log("User doesn't have the right to edit.");
|
1408
|
+
return;
|
1409
|
+
}
|
1410
|
+
self.onOperation(socket, revision, operation, selection);
|
1411
|
+
});
|
1412
|
+
})
|
1413
|
+
.on('selection', function (obj) {
|
1414
|
+
self.mayWrite(socket, function (mayWrite) {
|
1415
|
+
if (!mayWrite) {
|
1416
|
+
console.log("User doesn't have the right to edit.");
|
1417
|
+
return;
|
1418
|
+
}
|
1419
|
+
self.updateSelection(socket, obj && Selection.fromJSON(obj));
|
1420
|
+
});
|
1421
|
+
})
|
1422
|
+
.on('disconnect', function () {
|
1423
|
+
console.log("Disconnect");
|
1424
|
+
socket.leave(self.docId);
|
1425
|
+
self.onDisconnect(socket);
|
1426
|
+
if (socket.manager.sockets.clients(self.docId).length === 0) {
|
1427
|
+
self.emit('empty-room');
|
1428
|
+
}
|
1429
|
+
});
|
1430
|
+
};
|
1431
|
+
|
1432
|
+
EditorSocketIOServer.prototype.onOperation = function (socket, revision, operation, selection) {
|
1433
|
+
var wrapped;
|
1434
|
+
try {
|
1435
|
+
wrapped = new WrappedOperation(
|
1436
|
+
TextOperation.fromJSON(operation),
|
1437
|
+
selection && Selection.fromJSON(selection)
|
1438
|
+
);
|
1439
|
+
} catch (exc) {
|
1440
|
+
console.error("Invalid operation received: " + exc);
|
1441
|
+
return;
|
1442
|
+
}
|
1443
|
+
|
1444
|
+
try {
|
1445
|
+
var clientId = socket.id;
|
1446
|
+
var wrappedPrime = this.receiveOperation(revision, wrapped);
|
1447
|
+
console.log("new operation: " + wrapped);
|
1448
|
+
this.getClient(clientId).selection = wrappedPrime.meta;
|
1449
|
+
socket.emit('ack');
|
1450
|
+
socket.broadcast['in'](this.docId).emit(
|
1451
|
+
'operation', clientId,
|
1452
|
+
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
|
1453
|
+
);
|
1454
|
+
} catch (exc) {
|
1455
|
+
console.error(exc);
|
1456
|
+
}
|
1457
|
+
};
|
1458
|
+
|
1459
|
+
EditorSocketIOServer.prototype.updateSelection = function (socket, selection) {
|
1460
|
+
var clientId = socket.id;
|
1461
|
+
if (selection) {
|
1462
|
+
this.getClient(clientId).selection = selection;
|
1463
|
+
} else {
|
1464
|
+
delete this.getClient(clientId).selection;
|
1465
|
+
}
|
1466
|
+
socket.broadcast['in'](this.docId).emit('selection', clientId, selection);
|
1467
|
+
};
|
1468
|
+
|
1469
|
+
EditorSocketIOServer.prototype.setName = function (socket, name) {
|
1470
|
+
var clientId = socket.id;
|
1471
|
+
this.getClient(clientId).name = name;
|
1472
|
+
socket.broadcast['in'](this.docId).emit('set_name', clientId, name);
|
1473
|
+
};
|
1474
|
+
|
1475
|
+
EditorSocketIOServer.prototype.getClient = function (clientId) {
|
1476
|
+
return this.users[clientId] || (this.users[clientId] = {});
|
1477
|
+
};
|
1478
|
+
|
1479
|
+
EditorSocketIOServer.prototype.onDisconnect = function (socket) {
|
1480
|
+
var clientId = socket.id;
|
1481
|
+
delete this.users[clientId];
|
1482
|
+
socket.broadcast['in'](this.docId).emit('client_left', clientId);
|
1483
|
+
};
|
1484
|
+
|
1485
|
+
module.exports = EditorSocketIOServer;
|
1486
|
+
|
1487
|
+
/***/ },
|
1488
|
+
/* 10 */
|
1489
|
+
/***/ function(module, exports) {
|
1490
|
+
|
1491
|
+
// Copyright Joyent, Inc. and other Node contributors.
|
1492
|
+
//
|
1493
|
+
// Permission is hereby granted, free of charge, to any person obtaining a
|
1494
|
+
// copy of this software and associated documentation files (the
|
1495
|
+
// "Software"), to deal in the Software without restriction, including
|
1496
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
1497
|
+
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
1498
|
+
// persons to whom the Software is furnished to do so, subject to the
|
1499
|
+
// following conditions:
|
1500
|
+
//
|
1501
|
+
// The above copyright notice and this permission notice shall be included
|
1502
|
+
// in all copies or substantial portions of the Software.
|
1503
|
+
//
|
1504
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
1505
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
1506
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
1507
|
+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
1508
|
+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
1509
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
1510
|
+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1511
|
+
|
1512
|
+
function EventEmitter() {
|
1513
|
+
this._events = this._events || {};
|
1514
|
+
this._maxListeners = this._maxListeners || undefined;
|
1515
|
+
}
|
1516
|
+
module.exports = EventEmitter;
|
1517
|
+
|
1518
|
+
// Backwards-compat with node 0.10.x
|
1519
|
+
EventEmitter.EventEmitter = EventEmitter;
|
1520
|
+
|
1521
|
+
EventEmitter.prototype._events = undefined;
|
1522
|
+
EventEmitter.prototype._maxListeners = undefined;
|
1523
|
+
|
1524
|
+
// By default EventEmitters will print a warning if more than 10 listeners are
|
1525
|
+
// added to it. This is a useful default which helps finding memory leaks.
|
1526
|
+
EventEmitter.defaultMaxListeners = 10;
|
1527
|
+
|
1528
|
+
// Obviously not all Emitters should be limited to 10. This function allows
|
1529
|
+
// that to be increased. Set to zero for unlimited.
|
1530
|
+
EventEmitter.prototype.setMaxListeners = function(n) {
|
1531
|
+
if (!isNumber(n) || n < 0 || isNaN(n))
|
1532
|
+
throw TypeError('n must be a positive number');
|
1533
|
+
this._maxListeners = n;
|
1534
|
+
return this;
|
1535
|
+
};
|
1536
|
+
|
1537
|
+
EventEmitter.prototype.emit = function(type) {
|
1538
|
+
var er, handler, len, args, i, listeners;
|
1539
|
+
|
1540
|
+
if (!this._events)
|
1541
|
+
this._events = {};
|
1542
|
+
|
1543
|
+
// If there is no 'error' event listener then throw.
|
1544
|
+
if (type === 'error') {
|
1545
|
+
if (!this._events.error ||
|
1546
|
+
(isObject(this._events.error) && !this._events.error.length)) {
|
1547
|
+
er = arguments[1];
|
1548
|
+
if (er instanceof Error) {
|
1549
|
+
throw er; // Unhandled 'error' event
|
1550
|
+
} else {
|
1551
|
+
// At least give some kind of context to the user
|
1552
|
+
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
|
1553
|
+
err.context = er;
|
1554
|
+
throw err;
|
1555
|
+
}
|
1556
|
+
}
|
1557
|
+
}
|
1558
|
+
|
1559
|
+
handler = this._events[type];
|
1560
|
+
|
1561
|
+
if (isUndefined(handler))
|
1562
|
+
return false;
|
1563
|
+
|
1564
|
+
if (isFunction(handler)) {
|
1565
|
+
switch (arguments.length) {
|
1566
|
+
// fast cases
|
1567
|
+
case 1:
|
1568
|
+
handler.call(this);
|
1569
|
+
break;
|
1570
|
+
case 2:
|
1571
|
+
handler.call(this, arguments[1]);
|
1572
|
+
break;
|
1573
|
+
case 3:
|
1574
|
+
handler.call(this, arguments[1], arguments[2]);
|
1575
|
+
break;
|
1576
|
+
// slower
|
1577
|
+
default:
|
1578
|
+
args = Array.prototype.slice.call(arguments, 1);
|
1579
|
+
handler.apply(this, args);
|
1580
|
+
}
|
1581
|
+
} else if (isObject(handler)) {
|
1582
|
+
args = Array.prototype.slice.call(arguments, 1);
|
1583
|
+
listeners = handler.slice();
|
1584
|
+
len = listeners.length;
|
1585
|
+
for (i = 0; i < len; i++)
|
1586
|
+
listeners[i].apply(this, args);
|
1587
|
+
}
|
1588
|
+
|
1589
|
+
return true;
|
1590
|
+
};
|
1591
|
+
|
1592
|
+
EventEmitter.prototype.addListener = function(type, listener) {
|
1593
|
+
var m;
|
1594
|
+
|
1595
|
+
if (!isFunction(listener))
|
1596
|
+
throw TypeError('listener must be a function');
|
1597
|
+
|
1598
|
+
if (!this._events)
|
1599
|
+
this._events = {};
|
1600
|
+
|
1601
|
+
// To avoid recursion in the case that type === "newListener"! Before
|
1602
|
+
// adding it to the listeners, first emit "newListener".
|
1603
|
+
if (this._events.newListener)
|
1604
|
+
this.emit('newListener', type,
|
1605
|
+
isFunction(listener.listener) ?
|
1606
|
+
listener.listener : listener);
|
1607
|
+
|
1608
|
+
if (!this._events[type])
|
1609
|
+
// Optimize the case of one listener. Don't need the extra array object.
|
1610
|
+
this._events[type] = listener;
|
1611
|
+
else if (isObject(this._events[type]))
|
1612
|
+
// If we've already got an array, just append.
|
1613
|
+
this._events[type].push(listener);
|
1614
|
+
else
|
1615
|
+
// Adding the second element, need to change to array.
|
1616
|
+
this._events[type] = [this._events[type], listener];
|
1617
|
+
|
1618
|
+
// Check for listener leak
|
1619
|
+
if (isObject(this._events[type]) && !this._events[type].warned) {
|
1620
|
+
if (!isUndefined(this._maxListeners)) {
|
1621
|
+
m = this._maxListeners;
|
1622
|
+
} else {
|
1623
|
+
m = EventEmitter.defaultMaxListeners;
|
1624
|
+
}
|
1625
|
+
|
1626
|
+
if (m && m > 0 && this._events[type].length > m) {
|
1627
|
+
this._events[type].warned = true;
|
1628
|
+
console.error('(node) warning: possible EventEmitter memory ' +
|
1629
|
+
'leak detected. %d listeners added. ' +
|
1630
|
+
'Use emitter.setMaxListeners() to increase limit.',
|
1631
|
+
this._events[type].length);
|
1632
|
+
if (typeof console.trace === 'function') {
|
1633
|
+
// not supported in IE 10
|
1634
|
+
console.trace();
|
1635
|
+
}
|
1636
|
+
}
|
1637
|
+
}
|
1638
|
+
|
1639
|
+
return this;
|
1640
|
+
};
|
1641
|
+
|
1642
|
+
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
1643
|
+
|
1644
|
+
EventEmitter.prototype.once = function(type, listener) {
|
1645
|
+
if (!isFunction(listener))
|
1646
|
+
throw TypeError('listener must be a function');
|
1647
|
+
|
1648
|
+
var fired = false;
|
1649
|
+
|
1650
|
+
function g() {
|
1651
|
+
this.removeListener(type, g);
|
1652
|
+
|
1653
|
+
if (!fired) {
|
1654
|
+
fired = true;
|
1655
|
+
listener.apply(this, arguments);
|
1656
|
+
}
|
1657
|
+
}
|
1658
|
+
|
1659
|
+
g.listener = listener;
|
1660
|
+
this.on(type, g);
|
1661
|
+
|
1662
|
+
return this;
|
1663
|
+
};
|
1664
|
+
|
1665
|
+
// emits a 'removeListener' event iff the listener was removed
|
1666
|
+
EventEmitter.prototype.removeListener = function(type, listener) {
|
1667
|
+
var list, position, length, i;
|
1668
|
+
|
1669
|
+
if (!isFunction(listener))
|
1670
|
+
throw TypeError('listener must be a function');
|
1671
|
+
|
1672
|
+
if (!this._events || !this._events[type])
|
1673
|
+
return this;
|
1674
|
+
|
1675
|
+
list = this._events[type];
|
1676
|
+
length = list.length;
|
1677
|
+
position = -1;
|
1678
|
+
|
1679
|
+
if (list === listener ||
|
1680
|
+
(isFunction(list.listener) && list.listener === listener)) {
|
1681
|
+
delete this._events[type];
|
1682
|
+
if (this._events.removeListener)
|
1683
|
+
this.emit('removeListener', type, listener);
|
1684
|
+
|
1685
|
+
} else if (isObject(list)) {
|
1686
|
+
for (i = length; i-- > 0;) {
|
1687
|
+
if (list[i] === listener ||
|
1688
|
+
(list[i].listener && list[i].listener === listener)) {
|
1689
|
+
position = i;
|
1690
|
+
break;
|
1691
|
+
}
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
if (position < 0)
|
1695
|
+
return this;
|
1696
|
+
|
1697
|
+
if (list.length === 1) {
|
1698
|
+
list.length = 0;
|
1699
|
+
delete this._events[type];
|
1700
|
+
} else {
|
1701
|
+
list.splice(position, 1);
|
1702
|
+
}
|
1703
|
+
|
1704
|
+
if (this._events.removeListener)
|
1705
|
+
this.emit('removeListener', type, listener);
|
1706
|
+
}
|
1707
|
+
|
1708
|
+
return this;
|
1709
|
+
};
|
1710
|
+
|
1711
|
+
EventEmitter.prototype.removeAllListeners = function(type) {
|
1712
|
+
var key, listeners;
|
1713
|
+
|
1714
|
+
if (!this._events)
|
1715
|
+
return this;
|
1716
|
+
|
1717
|
+
// not listening for removeListener, no need to emit
|
1718
|
+
if (!this._events.removeListener) {
|
1719
|
+
if (arguments.length === 0)
|
1720
|
+
this._events = {};
|
1721
|
+
else if (this._events[type])
|
1722
|
+
delete this._events[type];
|
1723
|
+
return this;
|
1724
|
+
}
|
1725
|
+
|
1726
|
+
// emit removeListener for all listeners on all events
|
1727
|
+
if (arguments.length === 0) {
|
1728
|
+
for (key in this._events) {
|
1729
|
+
if (key === 'removeListener') continue;
|
1730
|
+
this.removeAllListeners(key);
|
1731
|
+
}
|
1732
|
+
this.removeAllListeners('removeListener');
|
1733
|
+
this._events = {};
|
1734
|
+
return this;
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
listeners = this._events[type];
|
1738
|
+
|
1739
|
+
if (isFunction(listeners)) {
|
1740
|
+
this.removeListener(type, listeners);
|
1741
|
+
} else if (listeners) {
|
1742
|
+
// LIFO order
|
1743
|
+
while (listeners.length)
|
1744
|
+
this.removeListener(type, listeners[listeners.length - 1]);
|
1745
|
+
}
|
1746
|
+
delete this._events[type];
|
1747
|
+
|
1748
|
+
return this;
|
1749
|
+
};
|
1750
|
+
|
1751
|
+
EventEmitter.prototype.listeners = function(type) {
|
1752
|
+
var ret;
|
1753
|
+
if (!this._events || !this._events[type])
|
1754
|
+
ret = [];
|
1755
|
+
else if (isFunction(this._events[type]))
|
1756
|
+
ret = [this._events[type]];
|
1757
|
+
else
|
1758
|
+
ret = this._events[type].slice();
|
1759
|
+
return ret;
|
1760
|
+
};
|
1761
|
+
|
1762
|
+
EventEmitter.prototype.listenerCount = function(type) {
|
1763
|
+
if (this._events) {
|
1764
|
+
var evlistener = this._events[type];
|
1765
|
+
|
1766
|
+
if (isFunction(evlistener))
|
1767
|
+
return 1;
|
1768
|
+
else if (evlistener)
|
1769
|
+
return evlistener.length;
|
1770
|
+
}
|
1771
|
+
return 0;
|
1772
|
+
};
|
1773
|
+
|
1774
|
+
EventEmitter.listenerCount = function(emitter, type) {
|
1775
|
+
return emitter.listenerCount(type);
|
1776
|
+
};
|
1777
|
+
|
1778
|
+
function isFunction(arg) {
|
1779
|
+
return typeof arg === 'function';
|
1780
|
+
}
|
1781
|
+
|
1782
|
+
function isNumber(arg) {
|
1783
|
+
return typeof arg === 'number';
|
1784
|
+
}
|
1785
|
+
|
1786
|
+
function isObject(arg) {
|
1787
|
+
return typeof arg === 'object' && arg !== null;
|
1788
|
+
}
|
1789
|
+
|
1790
|
+
function isUndefined(arg) {
|
1791
|
+
return arg === void 0;
|
1792
|
+
}
|
1793
|
+
|
1794
|
+
|
1795
|
+
/***/ },
|
1796
|
+
/* 11 */
|
1797
|
+
/***/ function(module, exports, __webpack_require__) {
|
1798
|
+
|
1799
|
+
if (typeof ot === 'undefined') {
|
1800
|
+
// Export for browsers
|
1801
|
+
var ot = {};
|
1802
|
+
}
|
1803
|
+
|
1804
|
+
ot.WrappedOperation = (function (global) {
|
1805
|
+
'use strict';
|
1806
|
+
|
1807
|
+
// A WrappedOperation contains an operation and corresponing metadata.
|
1808
|
+
function WrappedOperation (operation, meta) {
|
1809
|
+
this.wrapped = operation;
|
1810
|
+
this.meta = meta;
|
1811
|
+
}
|
1812
|
+
|
1813
|
+
WrappedOperation.prototype.apply = function () {
|
1814
|
+
return this.wrapped.apply.apply(this.wrapped, arguments);
|
1815
|
+
};
|
1816
|
+
|
1817
|
+
WrappedOperation.prototype.invert = function () {
|
1818
|
+
var meta = this.meta;
|
1819
|
+
return new WrappedOperation(
|
1820
|
+
this.wrapped.invert.apply(this.wrapped, arguments),
|
1821
|
+
meta && typeof meta === 'object' && typeof meta.invert === 'function' ?
|
1822
|
+
meta.invert.apply(meta, arguments) : meta
|
1823
|
+
);
|
1824
|
+
};
|
1825
|
+
|
1826
|
+
// Copy all properties from source to target.
|
1827
|
+
function copy (source, target) {
|
1828
|
+
for (var key in source) {
|
1829
|
+
if (source.hasOwnProperty(key)) {
|
1830
|
+
target[key] = source[key];
|
1831
|
+
}
|
1832
|
+
}
|
1833
|
+
}
|
1834
|
+
|
1835
|
+
function composeMeta (a, b) {
|
1836
|
+
if (a && typeof a === 'object') {
|
1837
|
+
if (typeof a.compose === 'function') { return a.compose(b); }
|
1838
|
+
var meta = {};
|
1839
|
+
copy(a, meta);
|
1840
|
+
copy(b, meta);
|
1841
|
+
return meta;
|
1842
|
+
}
|
1843
|
+
return b;
|
1844
|
+
}
|
1845
|
+
|
1846
|
+
WrappedOperation.prototype.compose = function (other) {
|
1847
|
+
return new WrappedOperation(
|
1848
|
+
this.wrapped.compose(other.wrapped),
|
1849
|
+
composeMeta(this.meta, other.meta)
|
1850
|
+
);
|
1851
|
+
};
|
1852
|
+
|
1853
|
+
function transformMeta (meta, operation) {
|
1854
|
+
if (meta && typeof meta === 'object') {
|
1855
|
+
if (typeof meta.transform === 'function') {
|
1856
|
+
return meta.transform(operation);
|
1857
|
+
}
|
1858
|
+
}
|
1859
|
+
return meta;
|
1860
|
+
}
|
1861
|
+
|
1862
|
+
WrappedOperation.transform = function (a, b) {
|
1863
|
+
var transform = a.wrapped.constructor.transform;
|
1864
|
+
var pair = transform(a.wrapped, b.wrapped);
|
1865
|
+
return [
|
1866
|
+
new WrappedOperation(pair[0], transformMeta(a.meta, b.wrapped)),
|
1867
|
+
new WrappedOperation(pair[1], transformMeta(b.meta, a.wrapped))
|
1868
|
+
];
|
1869
|
+
};
|
1870
|
+
|
1871
|
+
return WrappedOperation;
|
1872
|
+
|
1873
|
+
}(this));
|
1874
|
+
|
1875
|
+
// Export for CommonJS
|
1876
|
+
if (true) {
|
1877
|
+
module.exports = ot.WrappedOperation;
|
1878
|
+
}
|
1879
|
+
|
1880
|
+
/***/ },
|
1881
|
+
/* 12 */
|
1882
|
+
/***/ function(module, exports, __webpack_require__) {
|
1883
|
+
|
1884
|
+
/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors.
|
1885
|
+
//
|
1886
|
+
// Permission is hereby granted, free of charge, to any person obtaining a
|
1887
|
+
// copy of this software and associated documentation files (the
|
1888
|
+
// "Software"), to deal in the Software without restriction, including
|
1889
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
1890
|
+
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
1891
|
+
// persons to whom the Software is furnished to do so, subject to the
|
1892
|
+
// following conditions:
|
1893
|
+
//
|
1894
|
+
// The above copyright notice and this permission notice shall be included
|
1895
|
+
// in all copies or substantial portions of the Software.
|
1896
|
+
//
|
1897
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
1898
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
1899
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
1900
|
+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
1901
|
+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
1902
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
1903
|
+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1904
|
+
|
1905
|
+
var formatRegExp = /%[sdj%]/g;
|
1906
|
+
exports.format = function(f) {
|
1907
|
+
if (!isString(f)) {
|
1908
|
+
var objects = [];
|
1909
|
+
for (var i = 0; i < arguments.length; i++) {
|
1910
|
+
objects.push(inspect(arguments[i]));
|
1911
|
+
}
|
1912
|
+
return objects.join(' ');
|
1913
|
+
}
|
1914
|
+
|
1915
|
+
var i = 1;
|
1916
|
+
var args = arguments;
|
1917
|
+
var len = args.length;
|
1918
|
+
var str = String(f).replace(formatRegExp, function(x) {
|
1919
|
+
if (x === '%%') return '%';
|
1920
|
+
if (i >= len) return x;
|
1921
|
+
switch (x) {
|
1922
|
+
case '%s': return String(args[i++]);
|
1923
|
+
case '%d': return Number(args[i++]);
|
1924
|
+
case '%j':
|
1925
|
+
try {
|
1926
|
+
return JSON.stringify(args[i++]);
|
1927
|
+
} catch (_) {
|
1928
|
+
return '[Circular]';
|
1929
|
+
}
|
1930
|
+
default:
|
1931
|
+
return x;
|
1932
|
+
}
|
1933
|
+
});
|
1934
|
+
for (var x = args[i]; i < len; x = args[++i]) {
|
1935
|
+
if (isNull(x) || !isObject(x)) {
|
1936
|
+
str += ' ' + x;
|
1937
|
+
} else {
|
1938
|
+
str += ' ' + inspect(x);
|
1939
|
+
}
|
1940
|
+
}
|
1941
|
+
return str;
|
1942
|
+
};
|
1943
|
+
|
1944
|
+
|
1945
|
+
// Mark that a method should not be used.
|
1946
|
+
// Returns a modified function which warns once by default.
|
1947
|
+
// If --no-deprecation is set, then it is a no-op.
|
1948
|
+
exports.deprecate = function(fn, msg) {
|
1949
|
+
// Allow for deprecating things in the process of starting up.
|
1950
|
+
if (isUndefined(global.process)) {
|
1951
|
+
return function() {
|
1952
|
+
return exports.deprecate(fn, msg).apply(this, arguments);
|
1953
|
+
};
|
1954
|
+
}
|
1955
|
+
|
1956
|
+
if (process.noDeprecation === true) {
|
1957
|
+
return fn;
|
1958
|
+
}
|
1959
|
+
|
1960
|
+
var warned = false;
|
1961
|
+
function deprecated() {
|
1962
|
+
if (!warned) {
|
1963
|
+
if (process.throwDeprecation) {
|
1964
|
+
throw new Error(msg);
|
1965
|
+
} else if (process.traceDeprecation) {
|
1966
|
+
console.trace(msg);
|
1967
|
+
} else {
|
1968
|
+
console.error(msg);
|
1969
|
+
}
|
1970
|
+
warned = true;
|
1971
|
+
}
|
1972
|
+
return fn.apply(this, arguments);
|
1973
|
+
}
|
1974
|
+
|
1975
|
+
return deprecated;
|
1976
|
+
};
|
1977
|
+
|
1978
|
+
|
1979
|
+
var debugs = {};
|
1980
|
+
var debugEnviron;
|
1981
|
+
exports.debuglog = function(set) {
|
1982
|
+
if (isUndefined(debugEnviron))
|
1983
|
+
debugEnviron = process.env.NODE_DEBUG || '';
|
1984
|
+
set = set.toUpperCase();
|
1985
|
+
if (!debugs[set]) {
|
1986
|
+
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
|
1987
|
+
var pid = process.pid;
|
1988
|
+
debugs[set] = function() {
|
1989
|
+
var msg = exports.format.apply(exports, arguments);
|
1990
|
+
console.error('%s %d: %s', set, pid, msg);
|
1991
|
+
};
|
1992
|
+
} else {
|
1993
|
+
debugs[set] = function() {};
|
1994
|
+
}
|
1995
|
+
}
|
1996
|
+
return debugs[set];
|
1997
|
+
};
|
1998
|
+
|
1999
|
+
|
2000
|
+
/**
|
2001
|
+
* Echos the value of a value. Trys to print the value out
|
2002
|
+
* in the best way possible given the different types.
|
2003
|
+
*
|
2004
|
+
* @param {Object} obj The object to print out.
|
2005
|
+
* @param {Object} opts Optional options object that alters the output.
|
2006
|
+
*/
|
2007
|
+
/* legacy: obj, showHidden, depth, colors*/
|
2008
|
+
function inspect(obj, opts) {
|
2009
|
+
// default options
|
2010
|
+
var ctx = {
|
2011
|
+
seen: [],
|
2012
|
+
stylize: stylizeNoColor
|
2013
|
+
};
|
2014
|
+
// legacy...
|
2015
|
+
if (arguments.length >= 3) ctx.depth = arguments[2];
|
2016
|
+
if (arguments.length >= 4) ctx.colors = arguments[3];
|
2017
|
+
if (isBoolean(opts)) {
|
2018
|
+
// legacy...
|
2019
|
+
ctx.showHidden = opts;
|
2020
|
+
} else if (opts) {
|
2021
|
+
// got an "options" object
|
2022
|
+
exports._extend(ctx, opts);
|
2023
|
+
}
|
2024
|
+
// set default options
|
2025
|
+
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
|
2026
|
+
if (isUndefined(ctx.depth)) ctx.depth = 2;
|
2027
|
+
if (isUndefined(ctx.colors)) ctx.colors = false;
|
2028
|
+
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
|
2029
|
+
if (ctx.colors) ctx.stylize = stylizeWithColor;
|
2030
|
+
return formatValue(ctx, obj, ctx.depth);
|
2031
|
+
}
|
2032
|
+
exports.inspect = inspect;
|
2033
|
+
|
2034
|
+
|
2035
|
+
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
2036
|
+
inspect.colors = {
|
2037
|
+
'bold' : [1, 22],
|
2038
|
+
'italic' : [3, 23],
|
2039
|
+
'underline' : [4, 24],
|
2040
|
+
'inverse' : [7, 27],
|
2041
|
+
'white' : [37, 39],
|
2042
|
+
'grey' : [90, 39],
|
2043
|
+
'black' : [30, 39],
|
2044
|
+
'blue' : [34, 39],
|
2045
|
+
'cyan' : [36, 39],
|
2046
|
+
'green' : [32, 39],
|
2047
|
+
'magenta' : [35, 39],
|
2048
|
+
'red' : [31, 39],
|
2049
|
+
'yellow' : [33, 39]
|
2050
|
+
};
|
2051
|
+
|
2052
|
+
// Don't use 'blue' not visible on cmd.exe
|
2053
|
+
inspect.styles = {
|
2054
|
+
'special': 'cyan',
|
2055
|
+
'number': 'yellow',
|
2056
|
+
'boolean': 'yellow',
|
2057
|
+
'undefined': 'grey',
|
2058
|
+
'null': 'bold',
|
2059
|
+
'string': 'green',
|
2060
|
+
'date': 'magenta',
|
2061
|
+
// "name": intentionally not styling
|
2062
|
+
'regexp': 'red'
|
2063
|
+
};
|
2064
|
+
|
2065
|
+
|
2066
|
+
function stylizeWithColor(str, styleType) {
|
2067
|
+
var style = inspect.styles[styleType];
|
2068
|
+
|
2069
|
+
if (style) {
|
2070
|
+
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
|
2071
|
+
'\u001b[' + inspect.colors[style][1] + 'm';
|
2072
|
+
} else {
|
2073
|
+
return str;
|
2074
|
+
}
|
2075
|
+
}
|
2076
|
+
|
2077
|
+
|
2078
|
+
function stylizeNoColor(str, styleType) {
|
2079
|
+
return str;
|
2080
|
+
}
|
2081
|
+
|
2082
|
+
|
2083
|
+
function arrayToHash(array) {
|
2084
|
+
var hash = {};
|
2085
|
+
|
2086
|
+
array.forEach(function(val, idx) {
|
2087
|
+
hash[val] = true;
|
2088
|
+
});
|
2089
|
+
|
2090
|
+
return hash;
|
2091
|
+
}
|
2092
|
+
|
2093
|
+
|
2094
|
+
function formatValue(ctx, value, recurseTimes) {
|
2095
|
+
// Provide a hook for user-specified inspect functions.
|
2096
|
+
// Check that value is an object with an inspect function on it
|
2097
|
+
if (ctx.customInspect &&
|
2098
|
+
value &&
|
2099
|
+
isFunction(value.inspect) &&
|
2100
|
+
// Filter out the util module, it's inspect function is special
|
2101
|
+
value.inspect !== exports.inspect &&
|
2102
|
+
// Also filter out any prototype objects using the circular check.
|
2103
|
+
!(value.constructor && value.constructor.prototype === value)) {
|
2104
|
+
var ret = value.inspect(recurseTimes, ctx);
|
2105
|
+
if (!isString(ret)) {
|
2106
|
+
ret = formatValue(ctx, ret, recurseTimes);
|
2107
|
+
}
|
2108
|
+
return ret;
|
2109
|
+
}
|
2110
|
+
|
2111
|
+
// Primitive types cannot have properties
|
2112
|
+
var primitive = formatPrimitive(ctx, value);
|
2113
|
+
if (primitive) {
|
2114
|
+
return primitive;
|
2115
|
+
}
|
2116
|
+
|
2117
|
+
// Look up the keys of the object.
|
2118
|
+
var keys = Object.keys(value);
|
2119
|
+
var visibleKeys = arrayToHash(keys);
|
2120
|
+
|
2121
|
+
if (ctx.showHidden) {
|
2122
|
+
keys = Object.getOwnPropertyNames(value);
|
2123
|
+
}
|
2124
|
+
|
2125
|
+
// IE doesn't make error fields non-enumerable
|
2126
|
+
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
|
2127
|
+
if (isError(value)
|
2128
|
+
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
|
2129
|
+
return formatError(value);
|
2130
|
+
}
|
2131
|
+
|
2132
|
+
// Some type of object without properties can be shortcutted.
|
2133
|
+
if (keys.length === 0) {
|
2134
|
+
if (isFunction(value)) {
|
2135
|
+
var name = value.name ? ': ' + value.name : '';
|
2136
|
+
return ctx.stylize('[Function' + name + ']', 'special');
|
2137
|
+
}
|
2138
|
+
if (isRegExp(value)) {
|
2139
|
+
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
2140
|
+
}
|
2141
|
+
if (isDate(value)) {
|
2142
|
+
return ctx.stylize(Date.prototype.toString.call(value), 'date');
|
2143
|
+
}
|
2144
|
+
if (isError(value)) {
|
2145
|
+
return formatError(value);
|
2146
|
+
}
|
2147
|
+
}
|
2148
|
+
|
2149
|
+
var base = '', array = false, braces = ['{', '}'];
|
2150
|
+
|
2151
|
+
// Make Array say that they are Array
|
2152
|
+
if (isArray(value)) {
|
2153
|
+
array = true;
|
2154
|
+
braces = ['[', ']'];
|
2155
|
+
}
|
2156
|
+
|
2157
|
+
// Make functions say that they are functions
|
2158
|
+
if (isFunction(value)) {
|
2159
|
+
var n = value.name ? ': ' + value.name : '';
|
2160
|
+
base = ' [Function' + n + ']';
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
// Make RegExps say that they are RegExps
|
2164
|
+
if (isRegExp(value)) {
|
2165
|
+
base = ' ' + RegExp.prototype.toString.call(value);
|
2166
|
+
}
|
2167
|
+
|
2168
|
+
// Make dates with properties first say the date
|
2169
|
+
if (isDate(value)) {
|
2170
|
+
base = ' ' + Date.prototype.toUTCString.call(value);
|
2171
|
+
}
|
2172
|
+
|
2173
|
+
// Make error with message first say the error
|
2174
|
+
if (isError(value)) {
|
2175
|
+
base = ' ' + formatError(value);
|
2176
|
+
}
|
2177
|
+
|
2178
|
+
if (keys.length === 0 && (!array || value.length == 0)) {
|
2179
|
+
return braces[0] + base + braces[1];
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
if (recurseTimes < 0) {
|
2183
|
+
if (isRegExp(value)) {
|
2184
|
+
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
2185
|
+
} else {
|
2186
|
+
return ctx.stylize('[Object]', 'special');
|
2187
|
+
}
|
2188
|
+
}
|
2189
|
+
|
2190
|
+
ctx.seen.push(value);
|
2191
|
+
|
2192
|
+
var output;
|
2193
|
+
if (array) {
|
2194
|
+
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
|
2195
|
+
} else {
|
2196
|
+
output = keys.map(function(key) {
|
2197
|
+
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
|
2198
|
+
});
|
2199
|
+
}
|
2200
|
+
|
2201
|
+
ctx.seen.pop();
|
2202
|
+
|
2203
|
+
return reduceToSingleString(output, base, braces);
|
2204
|
+
}
|
2205
|
+
|
2206
|
+
|
2207
|
+
function formatPrimitive(ctx, value) {
|
2208
|
+
if (isUndefined(value))
|
2209
|
+
return ctx.stylize('undefined', 'undefined');
|
2210
|
+
if (isString(value)) {
|
2211
|
+
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
|
2212
|
+
.replace(/'/g, "\\'")
|
2213
|
+
.replace(/\\"/g, '"') + '\'';
|
2214
|
+
return ctx.stylize(simple, 'string');
|
2215
|
+
}
|
2216
|
+
if (isNumber(value))
|
2217
|
+
return ctx.stylize('' + value, 'number');
|
2218
|
+
if (isBoolean(value))
|
2219
|
+
return ctx.stylize('' + value, 'boolean');
|
2220
|
+
// For some reason typeof null is "object", so special case here.
|
2221
|
+
if (isNull(value))
|
2222
|
+
return ctx.stylize('null', 'null');
|
2223
|
+
}
|
2224
|
+
|
2225
|
+
|
2226
|
+
function formatError(value) {
|
2227
|
+
return '[' + Error.prototype.toString.call(value) + ']';
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
|
2231
|
+
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
2232
|
+
var output = [];
|
2233
|
+
for (var i = 0, l = value.length; i < l; ++i) {
|
2234
|
+
if (hasOwnProperty(value, String(i))) {
|
2235
|
+
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
2236
|
+
String(i), true));
|
2237
|
+
} else {
|
2238
|
+
output.push('');
|
2239
|
+
}
|
2240
|
+
}
|
2241
|
+
keys.forEach(function(key) {
|
2242
|
+
if (!key.match(/^\d+$/)) {
|
2243
|
+
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
2244
|
+
key, true));
|
2245
|
+
}
|
2246
|
+
});
|
2247
|
+
return output;
|
2248
|
+
}
|
2249
|
+
|
2250
|
+
|
2251
|
+
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
|
2252
|
+
var name, str, desc;
|
2253
|
+
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
|
2254
|
+
if (desc.get) {
|
2255
|
+
if (desc.set) {
|
2256
|
+
str = ctx.stylize('[Getter/Setter]', 'special');
|
2257
|
+
} else {
|
2258
|
+
str = ctx.stylize('[Getter]', 'special');
|
2259
|
+
}
|
2260
|
+
} else {
|
2261
|
+
if (desc.set) {
|
2262
|
+
str = ctx.stylize('[Setter]', 'special');
|
2263
|
+
}
|
2264
|
+
}
|
2265
|
+
if (!hasOwnProperty(visibleKeys, key)) {
|
2266
|
+
name = '[' + key + ']';
|
2267
|
+
}
|
2268
|
+
if (!str) {
|
2269
|
+
if (ctx.seen.indexOf(desc.value) < 0) {
|
2270
|
+
if (isNull(recurseTimes)) {
|
2271
|
+
str = formatValue(ctx, desc.value, null);
|
2272
|
+
} else {
|
2273
|
+
str = formatValue(ctx, desc.value, recurseTimes - 1);
|
2274
|
+
}
|
2275
|
+
if (str.indexOf('\n') > -1) {
|
2276
|
+
if (array) {
|
2277
|
+
str = str.split('\n').map(function(line) {
|
2278
|
+
return ' ' + line;
|
2279
|
+
}).join('\n').substr(2);
|
2280
|
+
} else {
|
2281
|
+
str = '\n' + str.split('\n').map(function(line) {
|
2282
|
+
return ' ' + line;
|
2283
|
+
}).join('\n');
|
2284
|
+
}
|
2285
|
+
}
|
2286
|
+
} else {
|
2287
|
+
str = ctx.stylize('[Circular]', 'special');
|
2288
|
+
}
|
2289
|
+
}
|
2290
|
+
if (isUndefined(name)) {
|
2291
|
+
if (array && key.match(/^\d+$/)) {
|
2292
|
+
return str;
|
2293
|
+
}
|
2294
|
+
name = JSON.stringify('' + key);
|
2295
|
+
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
|
2296
|
+
name = name.substr(1, name.length - 2);
|
2297
|
+
name = ctx.stylize(name, 'name');
|
2298
|
+
} else {
|
2299
|
+
name = name.replace(/'/g, "\\'")
|
2300
|
+
.replace(/\\"/g, '"')
|
2301
|
+
.replace(/(^"|"$)/g, "'");
|
2302
|
+
name = ctx.stylize(name, 'string');
|
2303
|
+
}
|
2304
|
+
}
|
2305
|
+
|
2306
|
+
return name + ': ' + str;
|
2307
|
+
}
|
2308
|
+
|
2309
|
+
|
2310
|
+
function reduceToSingleString(output, base, braces) {
|
2311
|
+
var numLinesEst = 0;
|
2312
|
+
var length = output.reduce(function(prev, cur) {
|
2313
|
+
numLinesEst++;
|
2314
|
+
if (cur.indexOf('\n') >= 0) numLinesEst++;
|
2315
|
+
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
|
2316
|
+
}, 0);
|
2317
|
+
|
2318
|
+
if (length > 60) {
|
2319
|
+
return braces[0] +
|
2320
|
+
(base === '' ? '' : base + '\n ') +
|
2321
|
+
' ' +
|
2322
|
+
output.join(',\n ') +
|
2323
|
+
' ' +
|
2324
|
+
braces[1];
|
2325
|
+
}
|
2326
|
+
|
2327
|
+
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
|
2328
|
+
}
|
2329
|
+
|
2330
|
+
|
2331
|
+
// NOTE: These type checking functions intentionally don't use `instanceof`
|
2332
|
+
// because it is fragile and can be easily faked with `Object.create()`.
|
2333
|
+
function isArray(ar) {
|
2334
|
+
return Array.isArray(ar);
|
2335
|
+
}
|
2336
|
+
exports.isArray = isArray;
|
2337
|
+
|
2338
|
+
function isBoolean(arg) {
|
2339
|
+
return typeof arg === 'boolean';
|
2340
|
+
}
|
2341
|
+
exports.isBoolean = isBoolean;
|
2342
|
+
|
2343
|
+
function isNull(arg) {
|
2344
|
+
return arg === null;
|
2345
|
+
}
|
2346
|
+
exports.isNull = isNull;
|
2347
|
+
|
2348
|
+
function isNullOrUndefined(arg) {
|
2349
|
+
return arg == null;
|
2350
|
+
}
|
2351
|
+
exports.isNullOrUndefined = isNullOrUndefined;
|
2352
|
+
|
2353
|
+
function isNumber(arg) {
|
2354
|
+
return typeof arg === 'number';
|
2355
|
+
}
|
2356
|
+
exports.isNumber = isNumber;
|
2357
|
+
|
2358
|
+
function isString(arg) {
|
2359
|
+
return typeof arg === 'string';
|
2360
|
+
}
|
2361
|
+
exports.isString = isString;
|
2362
|
+
|
2363
|
+
function isSymbol(arg) {
|
2364
|
+
return typeof arg === 'symbol';
|
2365
|
+
}
|
2366
|
+
exports.isSymbol = isSymbol;
|
2367
|
+
|
2368
|
+
function isUndefined(arg) {
|
2369
|
+
return arg === void 0;
|
2370
|
+
}
|
2371
|
+
exports.isUndefined = isUndefined;
|
2372
|
+
|
2373
|
+
function isRegExp(re) {
|
2374
|
+
return isObject(re) && objectToString(re) === '[object RegExp]';
|
2375
|
+
}
|
2376
|
+
exports.isRegExp = isRegExp;
|
2377
|
+
|
2378
|
+
function isObject(arg) {
|
2379
|
+
return typeof arg === 'object' && arg !== null;
|
2380
|
+
}
|
2381
|
+
exports.isObject = isObject;
|
2382
|
+
|
2383
|
+
function isDate(d) {
|
2384
|
+
return isObject(d) && objectToString(d) === '[object Date]';
|
2385
|
+
}
|
2386
|
+
exports.isDate = isDate;
|
2387
|
+
|
2388
|
+
function isError(e) {
|
2389
|
+
return isObject(e) &&
|
2390
|
+
(objectToString(e) === '[object Error]' || e instanceof Error);
|
2391
|
+
}
|
2392
|
+
exports.isError = isError;
|
2393
|
+
|
2394
|
+
function isFunction(arg) {
|
2395
|
+
return typeof arg === 'function';
|
2396
|
+
}
|
2397
|
+
exports.isFunction = isFunction;
|
2398
|
+
|
2399
|
+
function isPrimitive(arg) {
|
2400
|
+
return arg === null ||
|
2401
|
+
typeof arg === 'boolean' ||
|
2402
|
+
typeof arg === 'number' ||
|
2403
|
+
typeof arg === 'string' ||
|
2404
|
+
typeof arg === 'symbol' || // ES6 symbol
|
2405
|
+
typeof arg === 'undefined';
|
2406
|
+
}
|
2407
|
+
exports.isPrimitive = isPrimitive;
|
2408
|
+
|
2409
|
+
exports.isBuffer = __webpack_require__(14);
|
2410
|
+
|
2411
|
+
function objectToString(o) {
|
2412
|
+
return Object.prototype.toString.call(o);
|
2413
|
+
}
|
2414
|
+
|
2415
|
+
|
2416
|
+
function pad(n) {
|
2417
|
+
return n < 10 ? '0' + n.toString(10) : n.toString(10);
|
2418
|
+
}
|
2419
|
+
|
2420
|
+
|
2421
|
+
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
|
2422
|
+
'Oct', 'Nov', 'Dec'];
|
2423
|
+
|
2424
|
+
// 26 Feb 16:19:34
|
2425
|
+
function timestamp() {
|
2426
|
+
var d = new Date();
|
2427
|
+
var time = [pad(d.getHours()),
|
2428
|
+
pad(d.getMinutes()),
|
2429
|
+
pad(d.getSeconds())].join(':');
|
2430
|
+
return [d.getDate(), months[d.getMonth()], time].join(' ');
|
2431
|
+
}
|
2432
|
+
|
2433
|
+
|
2434
|
+
// log is just a thin wrapper to console.log that prepends a timestamp
|
2435
|
+
exports.log = function() {
|
2436
|
+
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
|
2437
|
+
};
|
2438
|
+
|
2439
|
+
|
2440
|
+
/**
|
2441
|
+
* Inherit the prototype methods from one constructor into another.
|
2442
|
+
*
|
2443
|
+
* The Function.prototype.inherits from lang.js rewritten as a standalone
|
2444
|
+
* function (not on Function.prototype). NOTE: If this file is to be loaded
|
2445
|
+
* during bootstrapping this function needs to be rewritten using some native
|
2446
|
+
* functions as prototype setup using normal JavaScript does not work as
|
2447
|
+
* expected during bootstrapping (see mirror.js in r114903).
|
2448
|
+
*
|
2449
|
+
* @param {function} ctor Constructor function which needs to inherit the
|
2450
|
+
* prototype.
|
2451
|
+
* @param {function} superCtor Constructor function to inherit prototype from.
|
2452
|
+
*/
|
2453
|
+
exports.inherits = __webpack_require__(15);
|
2454
|
+
|
2455
|
+
exports._extend = function(origin, add) {
|
2456
|
+
// Don't do anything if add isn't an object
|
2457
|
+
if (!add || !isObject(add)) return origin;
|
2458
|
+
|
2459
|
+
var keys = Object.keys(add);
|
2460
|
+
var i = keys.length;
|
2461
|
+
while (i--) {
|
2462
|
+
origin[keys[i]] = add[keys[i]];
|
2463
|
+
}
|
2464
|
+
return origin;
|
2465
|
+
};
|
2466
|
+
|
2467
|
+
function hasOwnProperty(obj, prop) {
|
2468
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
2469
|
+
}
|
2470
|
+
|
2471
|
+
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(13)))
|
2472
|
+
|
2473
|
+
/***/ },
|
2474
|
+
/* 13 */
|
2475
|
+
/***/ function(module, exports) {
|
2476
|
+
|
2477
|
+
// shim for using process in browser
|
2478
|
+
var process = module.exports = {};
|
2479
|
+
|
2480
|
+
// cached from whatever global is present so that test runners that stub it
|
2481
|
+
// don't break things. But we need to wrap it in a try catch in case it is
|
2482
|
+
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
2483
|
+
// function because try/catches deoptimize in certain engines.
|
2484
|
+
|
2485
|
+
var cachedSetTimeout;
|
2486
|
+
var cachedClearTimeout;
|
2487
|
+
|
2488
|
+
function defaultSetTimout() {
|
2489
|
+
throw new Error('setTimeout has not been defined');
|
2490
|
+
}
|
2491
|
+
function defaultClearTimeout () {
|
2492
|
+
throw new Error('clearTimeout has not been defined');
|
2493
|
+
}
|
2494
|
+
(function () {
|
2495
|
+
try {
|
2496
|
+
if (typeof setTimeout === 'function') {
|
2497
|
+
cachedSetTimeout = setTimeout;
|
2498
|
+
} else {
|
2499
|
+
cachedSetTimeout = defaultSetTimout;
|
2500
|
+
}
|
2501
|
+
} catch (e) {
|
2502
|
+
cachedSetTimeout = defaultSetTimout;
|
2503
|
+
}
|
2504
|
+
try {
|
2505
|
+
if (typeof clearTimeout === 'function') {
|
2506
|
+
cachedClearTimeout = clearTimeout;
|
2507
|
+
} else {
|
2508
|
+
cachedClearTimeout = defaultClearTimeout;
|
2509
|
+
}
|
2510
|
+
} catch (e) {
|
2511
|
+
cachedClearTimeout = defaultClearTimeout;
|
2512
|
+
}
|
2513
|
+
} ())
|
2514
|
+
function runTimeout(fun) {
|
2515
|
+
if (cachedSetTimeout === setTimeout) {
|
2516
|
+
//normal enviroments in sane situations
|
2517
|
+
return setTimeout(fun, 0);
|
2518
|
+
}
|
2519
|
+
// if setTimeout wasn't available but was latter defined
|
2520
|
+
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
2521
|
+
cachedSetTimeout = setTimeout;
|
2522
|
+
return setTimeout(fun, 0);
|
2523
|
+
}
|
2524
|
+
try {
|
2525
|
+
// when when somebody has screwed with setTimeout but no I.E. maddness
|
2526
|
+
return cachedSetTimeout(fun, 0);
|
2527
|
+
} catch(e){
|
2528
|
+
try {
|
2529
|
+
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
2530
|
+
return cachedSetTimeout.call(null, fun, 0);
|
2531
|
+
} catch(e){
|
2532
|
+
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
2533
|
+
return cachedSetTimeout.call(this, fun, 0);
|
2534
|
+
}
|
2535
|
+
}
|
2536
|
+
|
2537
|
+
|
2538
|
+
}
|
2539
|
+
function runClearTimeout(marker) {
|
2540
|
+
if (cachedClearTimeout === clearTimeout) {
|
2541
|
+
//normal enviroments in sane situations
|
2542
|
+
return clearTimeout(marker);
|
2543
|
+
}
|
2544
|
+
// if clearTimeout wasn't available but was latter defined
|
2545
|
+
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
2546
|
+
cachedClearTimeout = clearTimeout;
|
2547
|
+
return clearTimeout(marker);
|
2548
|
+
}
|
2549
|
+
try {
|
2550
|
+
// when when somebody has screwed with setTimeout but no I.E. maddness
|
2551
|
+
return cachedClearTimeout(marker);
|
2552
|
+
} catch (e){
|
2553
|
+
try {
|
2554
|
+
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
2555
|
+
return cachedClearTimeout.call(null, marker);
|
2556
|
+
} catch (e){
|
2557
|
+
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
2558
|
+
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
2559
|
+
return cachedClearTimeout.call(this, marker);
|
2560
|
+
}
|
2561
|
+
}
|
2562
|
+
|
2563
|
+
|
2564
|
+
|
2565
|
+
}
|
2566
|
+
var queue = [];
|
2567
|
+
var draining = false;
|
2568
|
+
var currentQueue;
|
2569
|
+
var queueIndex = -1;
|
2570
|
+
|
2571
|
+
function cleanUpNextTick() {
|
2572
|
+
if (!draining || !currentQueue) {
|
2573
|
+
return;
|
2574
|
+
}
|
2575
|
+
draining = false;
|
2576
|
+
if (currentQueue.length) {
|
2577
|
+
queue = currentQueue.concat(queue);
|
2578
|
+
} else {
|
2579
|
+
queueIndex = -1;
|
2580
|
+
}
|
2581
|
+
if (queue.length) {
|
2582
|
+
drainQueue();
|
2583
|
+
}
|
2584
|
+
}
|
2585
|
+
|
2586
|
+
function drainQueue() {
|
2587
|
+
if (draining) {
|
2588
|
+
return;
|
2589
|
+
}
|
2590
|
+
var timeout = runTimeout(cleanUpNextTick);
|
2591
|
+
draining = true;
|
2592
|
+
|
2593
|
+
var len = queue.length;
|
2594
|
+
while(len) {
|
2595
|
+
currentQueue = queue;
|
2596
|
+
queue = [];
|
2597
|
+
while (++queueIndex < len) {
|
2598
|
+
if (currentQueue) {
|
2599
|
+
currentQueue[queueIndex].run();
|
2600
|
+
}
|
2601
|
+
}
|
2602
|
+
queueIndex = -1;
|
2603
|
+
len = queue.length;
|
2604
|
+
}
|
2605
|
+
currentQueue = null;
|
2606
|
+
draining = false;
|
2607
|
+
runClearTimeout(timeout);
|
2608
|
+
}
|
2609
|
+
|
2610
|
+
process.nextTick = function (fun) {
|
2611
|
+
var args = new Array(arguments.length - 1);
|
2612
|
+
if (arguments.length > 1) {
|
2613
|
+
for (var i = 1; i < arguments.length; i++) {
|
2614
|
+
args[i - 1] = arguments[i];
|
2615
|
+
}
|
2616
|
+
}
|
2617
|
+
queue.push(new Item(fun, args));
|
2618
|
+
if (queue.length === 1 && !draining) {
|
2619
|
+
runTimeout(drainQueue);
|
2620
|
+
}
|
2621
|
+
};
|
2622
|
+
|
2623
|
+
// v8 likes predictible objects
|
2624
|
+
function Item(fun, array) {
|
2625
|
+
this.fun = fun;
|
2626
|
+
this.array = array;
|
2627
|
+
}
|
2628
|
+
Item.prototype.run = function () {
|
2629
|
+
this.fun.apply(null, this.array);
|
2630
|
+
};
|
2631
|
+
process.title = 'browser';
|
2632
|
+
process.browser = true;
|
2633
|
+
process.env = {};
|
2634
|
+
process.argv = [];
|
2635
|
+
process.version = ''; // empty string to avoid regexp issues
|
2636
|
+
process.versions = {};
|
2637
|
+
|
2638
|
+
function noop() {}
|
2639
|
+
|
2640
|
+
process.on = noop;
|
2641
|
+
process.addListener = noop;
|
2642
|
+
process.once = noop;
|
2643
|
+
process.off = noop;
|
2644
|
+
process.removeListener = noop;
|
2645
|
+
process.removeAllListeners = noop;
|
2646
|
+
process.emit = noop;
|
2647
|
+
|
2648
|
+
process.binding = function (name) {
|
2649
|
+
throw new Error('process.binding is not supported');
|
2650
|
+
};
|
2651
|
+
|
2652
|
+
process.cwd = function () { return '/' };
|
2653
|
+
process.chdir = function (dir) {
|
2654
|
+
throw new Error('process.chdir is not supported');
|
2655
|
+
};
|
2656
|
+
process.umask = function() { return 0; };
|
2657
|
+
|
2658
|
+
|
2659
|
+
/***/ },
|
2660
|
+
/* 14 */
|
2661
|
+
/***/ function(module, exports) {
|
2662
|
+
|
2663
|
+
module.exports = function isBuffer(arg) {
|
2664
|
+
return arg && typeof arg === 'object'
|
2665
|
+
&& typeof arg.copy === 'function'
|
2666
|
+
&& typeof arg.fill === 'function'
|
2667
|
+
&& typeof arg.readUInt8 === 'function';
|
2668
|
+
}
|
2669
|
+
|
2670
|
+
/***/ },
|
2671
|
+
/* 15 */
|
2672
|
+
/***/ function(module, exports) {
|
2673
|
+
|
2674
|
+
if (typeof Object.create === 'function') {
|
2675
|
+
// implementation from standard node.js 'util' module
|
2676
|
+
module.exports = function inherits(ctor, superCtor) {
|
2677
|
+
ctor.super_ = superCtor
|
2678
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
2679
|
+
constructor: {
|
2680
|
+
value: ctor,
|
2681
|
+
enumerable: false,
|
2682
|
+
writable: true,
|
2683
|
+
configurable: true
|
2684
|
+
}
|
2685
|
+
});
|
2686
|
+
};
|
2687
|
+
} else {
|
2688
|
+
// old school shim for old browsers
|
2689
|
+
module.exports = function inherits(ctor, superCtor) {
|
2690
|
+
ctor.super_ = superCtor
|
2691
|
+
var TempCtor = function () {}
|
2692
|
+
TempCtor.prototype = superCtor.prototype
|
2693
|
+
ctor.prototype = new TempCtor()
|
2694
|
+
ctor.prototype.constructor = ctor
|
2695
|
+
}
|
2696
|
+
}
|
2697
|
+
|
2698
|
+
|
2699
|
+
/***/ },
|
2700
|
+
/* 16 */
|
2701
|
+
/***/ function(module, exports, __webpack_require__) {
|
2702
|
+
|
2703
|
+
'use strict';
|
2704
|
+
|
2705
|
+
Object.defineProperty(exports, "__esModule", {
|
2706
|
+
value: true
|
2707
|
+
});
|
2708
|
+
|
2709
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
2710
|
+
|
2711
|
+
var _class, _temp; // weak
|
2712
|
+
|
2713
|
+
var _looseLeaf = __webpack_require__(1);
|
2714
|
+
|
2715
|
+
var _looseLeaf2 = _interopRequireDefault(_looseLeaf);
|
2716
|
+
|
2717
|
+
var _attributeCable = __webpack_require__(2);
|
2718
|
+
|
2719
|
+
var _attributeCable2 = _interopRequireDefault(_attributeCable);
|
2720
|
+
|
2721
|
+
var _awaitingAck = __webpack_require__(17);
|
2722
|
+
|
2723
|
+
var _awaitingAck2 = _interopRequireDefault(_awaitingAck);
|
2724
|
+
|
2725
|
+
var _awaitingWithBuffer = __webpack_require__(19);
|
2726
|
+
|
2727
|
+
var _awaitingWithBuffer2 = _interopRequireDefault(_awaitingWithBuffer);
|
2728
|
+
|
2729
|
+
var _synchronized = __webpack_require__(20);
|
2730
|
+
|
2731
|
+
var _synchronized2 = _interopRequireDefault(_synchronized);
|
2732
|
+
|
2733
|
+
var _types = __webpack_require__(21);
|
2734
|
+
|
2735
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
2736
|
+
|
2737
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
2738
|
+
|
2739
|
+
var CollaborativeAttribute = (_temp = _class = function () {
|
2740
|
+
function CollaborativeAttribute(collaborate, attribute) {
|
2741
|
+
_classCallCheck(this, CollaborativeAttribute);
|
2742
|
+
|
2743
|
+
this.collaborate = collaborate;
|
2744
|
+
this.attribute = attribute;
|
2745
|
+
|
2746
|
+
if (!this.attribute) {
|
2747
|
+
throw new Error('You must specify an attribute to collaboratively edit');
|
2748
|
+
}
|
2749
|
+
|
2750
|
+
var event = new _looseLeaf2.default.Events();
|
2751
|
+
event.attachHandlers(this);
|
2752
|
+
this.documentId = this.collaborate.documentId;
|
2753
|
+
this.cable = new _attributeCable2.default(this, this.collaborate.cable, this.attribute);
|
2754
|
+
this.state = new _synchronized2.default(this);
|
2755
|
+
}
|
2756
|
+
|
2757
|
+
_createClass(CollaborativeAttribute, [{
|
2758
|
+
key: 'destroy',
|
2759
|
+
value: function destroy() {
|
2760
|
+
return this.cable.destroy();
|
2761
|
+
}
|
2762
|
+
}, {
|
2763
|
+
key: 'localOperation',
|
2764
|
+
value: function localOperation(operation) {
|
2765
|
+
if (operation.isNoop()) {
|
2766
|
+
return;
|
2767
|
+
}
|
2768
|
+
|
2769
|
+
return this.state.localOperation(operation);
|
2770
|
+
}
|
2771
|
+
}, {
|
2772
|
+
key: 'remoteOperation',
|
2773
|
+
value: function remoteOperation(data) {
|
2774
|
+
this.state.transformRemoteOperation(data);
|
2775
|
+
return this.trigger('remoteOperation', data.operation);
|
2776
|
+
}
|
2777
|
+
}, {
|
2778
|
+
key: 'receiveAck',
|
2779
|
+
value: function receiveAck(data) {
|
2780
|
+
return this.state.receiveAck(data);
|
2781
|
+
}
|
2782
|
+
}]);
|
2783
|
+
|
2784
|
+
return CollaborativeAttribute;
|
2785
|
+
}(), _class.AwaitingAck = _awaitingAck2.default, _class.AwaitingWithBuffer = _awaitingWithBuffer2.default, _class.Synchronized = _synchronized2.default, _temp);
|
2786
|
+
exports.default = CollaborativeAttribute;
|
2787
|
+
|
2788
|
+
/***/ },
|
2789
|
+
/* 17 */
|
2790
|
+
/***/ function(module, exports, __webpack_require__) {
|
2791
|
+
|
2792
|
+
'use strict';
|
2793
|
+
|
2794
|
+
Object.defineProperty(exports, "__esModule", {
|
2795
|
+
value: true
|
2796
|
+
});
|
2797
|
+
exports.default = undefined;
|
2798
|
+
|
2799
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
2800
|
+
|
2801
|
+
var _ot = __webpack_require__(3);
|
2802
|
+
|
2803
|
+
var _ot2 = _interopRequireDefault(_ot);
|
2804
|
+
|
2805
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
2806
|
+
|
2807
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
2808
|
+
|
2809
|
+
var _state = __webpack_require__(18);
|
2810
|
+
|
2811
|
+
var _state2 = _interopRequireDefault(_state);
|
2812
|
+
|
2813
|
+
var _awaitingWithBuffer = __webpack_require__(19);
|
2814
|
+
|
2815
|
+
var _awaitingWithBuffer2 = _interopRequireDefault(_awaitingWithBuffer);
|
2816
|
+
|
2817
|
+
var _synchronized = __webpack_require__(20);
|
2818
|
+
|
2819
|
+
var _synchronized2 = _interopRequireDefault(_synchronized);
|
2820
|
+
|
2821
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
2822
|
+
|
2823
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
2824
|
+
|
2825
|
+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
2826
|
+
|
2827
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // weak
|
2828
|
+
|
2829
|
+
|
2830
|
+
var AwaitingAck = function (_State) {
|
2831
|
+
_inherits(AwaitingAck, _State);
|
2832
|
+
|
2833
|
+
function AwaitingAck(collaborativeAttribute, operation) {
|
2834
|
+
_classCallCheck(this, AwaitingAck);
|
2835
|
+
|
2836
|
+
var _this = _possibleConstructorReturn(this, (AwaitingAck.__proto__ || Object.getPrototypeOf(AwaitingAck)).apply(this, arguments));
|
2837
|
+
|
2838
|
+
_this.operation = operation;
|
2839
|
+
return _this;
|
2840
|
+
}
|
2841
|
+
|
2842
|
+
_createClass(AwaitingAck, [{
|
2843
|
+
key: 'localOperation',
|
2844
|
+
value: function localOperation(operation) {
|
2845
|
+
this.collaborativeAttribute.state = new _awaitingWithBuffer2.default(this.collaborativeAttribute, this.operation, operation);
|
2846
|
+
}
|
2847
|
+
}, {
|
2848
|
+
key: 'receiveAck',
|
2849
|
+
value: function receiveAck(data) {
|
2850
|
+
this.collaborativeAttribute.state = new _synchronized2.default(this.collaborativeAttribute);
|
2851
|
+
}
|
2852
|
+
}, {
|
2853
|
+
key: 'transformRemoteOperation',
|
2854
|
+
value: function transformRemoteOperation(data) {
|
2855
|
+
var pair = _ot2.default.TextOperation.transform(this.operation, data.operation);
|
2856
|
+
this.operation = pair[0];
|
2857
|
+
data.operation = pair[1];
|
2858
|
+
}
|
2859
|
+
}]);
|
2860
|
+
|
2861
|
+
return AwaitingAck;
|
2862
|
+
}(_state2.default);
|
2863
|
+
|
2864
|
+
exports.default = AwaitingAck;
|
2865
|
+
;
|
2866
|
+
|
2867
|
+
/***/ },
|
2868
|
+
/* 18 */
|
2869
|
+
/***/ function(module, exports, __webpack_require__) {
|
2870
|
+
|
2871
|
+
'use strict';
|
2872
|
+
|
2873
|
+
Object.defineProperty(exports, "__esModule", {
|
2874
|
+
value: true
|
2875
|
+
});
|
2876
|
+
exports.default = undefined;
|
2877
|
+
|
2878
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
2879
|
+
|
2880
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
2881
|
+
|
2882
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
2883
|
+
|
2884
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // weak
|
2885
|
+
|
2886
|
+
var State = function State(collaborativeAttribute) {
|
2887
|
+
_classCallCheck(this, State);
|
2888
|
+
|
2889
|
+
this.collaborativeAttribute = collaborativeAttribute;
|
2890
|
+
};
|
2891
|
+
|
2892
|
+
exports.default = State;
|
2893
|
+
|
2894
|
+
/***/ },
|
2895
|
+
/* 19 */
|
2896
|
+
/***/ function(module, exports, __webpack_require__) {
|
2897
|
+
|
2898
|
+
'use strict';
|
2899
|
+
|
2900
|
+
Object.defineProperty(exports, "__esModule", {
|
2901
|
+
value: true
|
2902
|
+
});
|
2903
|
+
exports.default = undefined;
|
2904
|
+
|
2905
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
2906
|
+
|
2907
|
+
var _ot = __webpack_require__(3);
|
2908
|
+
|
2909
|
+
var _ot2 = _interopRequireDefault(_ot);
|
2910
|
+
|
2911
|
+
var _state = __webpack_require__(18);
|
2912
|
+
|
2913
|
+
var _state2 = _interopRequireDefault(_state);
|
2914
|
+
|
2915
|
+
var _awaitingAck = __webpack_require__(17);
|
2916
|
+
|
2917
|
+
var _awaitingAck2 = _interopRequireDefault(_awaitingAck);
|
2918
|
+
|
2919
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
2920
|
+
|
2921
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
2922
|
+
|
2923
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
2924
|
+
|
2925
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
2926
|
+
|
2927
|
+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
2928
|
+
|
2929
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // weak
|
2930
|
+
|
2931
|
+
var AwaitingWithBuffer = function (_State) {
|
2932
|
+
_inherits(AwaitingWithBuffer, _State);
|
2933
|
+
|
2934
|
+
function AwaitingWithBuffer(collaborativeAttribute, operation, buffer) {
|
2935
|
+
_classCallCheck(this, AwaitingWithBuffer);
|
2936
|
+
|
2937
|
+
var _this = _possibleConstructorReturn(this, (AwaitingWithBuffer.__proto__ || Object.getPrototypeOf(AwaitingWithBuffer)).apply(this, arguments));
|
2938
|
+
|
2939
|
+
_this.operation = operation;
|
2940
|
+
_this.buffer = buffer;
|
2941
|
+
return _this;
|
2942
|
+
}
|
2943
|
+
|
2944
|
+
_createClass(AwaitingWithBuffer, [{
|
2945
|
+
key: 'localOperation',
|
2946
|
+
value: function localOperation(operation) {
|
2947
|
+
this.buffer = this.buffer.compose(operation);
|
2948
|
+
}
|
2949
|
+
}, {
|
2950
|
+
key: 'receiveAck',
|
2951
|
+
value: function receiveAck(data) {
|
2952
|
+
this.collaborativeAttribute.cable.sendOperation({
|
2953
|
+
operation: this.buffer
|
2954
|
+
});
|
2955
|
+
|
2956
|
+
this.collaborativeAttribute.state = new _awaitingAck2.default(this.collaborativeAttribute, this.buffer);
|
2957
|
+
}
|
2958
|
+
}, {
|
2959
|
+
key: 'transformRemoteOperation',
|
2960
|
+
value: function transformRemoteOperation(data) {
|
2961
|
+
var pair = _ot2.default.TextOperation.transform(this.operation, data.operation);
|
2962
|
+
this.operation = pair[0];
|
2963
|
+
data.operation = pair[1];
|
2964
|
+
pair = _ot2.default.TextOperation.transform(this.buffer, data.operation);
|
2965
|
+
this.buffer = pair[0];
|
2966
|
+
data.operation = pair[1];
|
2967
|
+
}
|
2968
|
+
}]);
|
2969
|
+
|
2970
|
+
return AwaitingWithBuffer;
|
2971
|
+
}(_state2.default);
|
2972
|
+
|
2973
|
+
exports.default = AwaitingWithBuffer;
|
2974
|
+
|
2975
|
+
/***/ },
|
2976
|
+
/* 20 */
|
2977
|
+
/***/ function(module, exports, __webpack_require__) {
|
2978
|
+
|
2979
|
+
'use strict';
|
2980
|
+
|
2981
|
+
Object.defineProperty(exports, "__esModule", {
|
2982
|
+
value: true
|
2983
|
+
});
|
2984
|
+
exports.default = undefined;
|
2985
|
+
|
2986
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
2987
|
+
|
2988
|
+
var _state = __webpack_require__(18);
|
2989
|
+
|
2990
|
+
var _state2 = _interopRequireDefault(_state);
|
2991
|
+
|
2992
|
+
var _awaitingAck = __webpack_require__(17);
|
2993
|
+
|
2994
|
+
var _awaitingAck2 = _interopRequireDefault(_awaitingAck);
|
2995
|
+
|
2996
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
2997
|
+
|
2998
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
2999
|
+
|
3000
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3001
|
+
|
3002
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3003
|
+
|
3004
|
+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
3005
|
+
|
3006
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // weak
|
3007
|
+
|
3008
|
+
var Synchronized = function (_State) {
|
3009
|
+
_inherits(Synchronized, _State);
|
3010
|
+
|
3011
|
+
function Synchronized() {
|
3012
|
+
_classCallCheck(this, Synchronized);
|
3013
|
+
|
3014
|
+
return _possibleConstructorReturn(this, (Synchronized.__proto__ || Object.getPrototypeOf(Synchronized)).apply(this, arguments));
|
3015
|
+
}
|
3016
|
+
|
3017
|
+
_createClass(Synchronized, [{
|
3018
|
+
key: 'localOperation',
|
3019
|
+
value: function localOperation(operation) {
|
3020
|
+
this.collaborativeAttribute.cable.sendOperation({
|
3021
|
+
operation: operation
|
3022
|
+
});
|
3023
|
+
|
3024
|
+
this.collaborativeAttribute.state = new _awaitingAck2.default(this.collaborativeAttribute, operation);
|
3025
|
+
}
|
3026
|
+
}, {
|
3027
|
+
key: 'receiveAck',
|
3028
|
+
value: function receiveAck(data) {
|
3029
|
+
console.error('Received an ack for version ' + data.version + ' whilst in Synchronized state.');
|
3030
|
+
}
|
3031
|
+
}, {
|
3032
|
+
key: 'transformRemoteOperation',
|
3033
|
+
value: function transformRemoteOperation(data) {}
|
3034
|
+
}]);
|
3035
|
+
|
3036
|
+
return Synchronized;
|
3037
|
+
}(_state2.default);
|
3038
|
+
|
3039
|
+
exports.default = Synchronized;
|
3040
|
+
|
3041
|
+
/***/ },
|
3042
|
+
/* 21 */
|
3043
|
+
/***/ function(module, exports) {
|
3044
|
+
|
3045
|
+
"use strict";
|
3046
|
+
|
3047
|
+
/***/ },
|
3048
|
+
/* 22 */
|
3049
|
+
/***/ function(module, exports, __webpack_require__) {
|
3050
|
+
|
3051
|
+
'use strict';
|
3052
|
+
|
3053
|
+
Object.defineProperty(exports, "__esModule", {
|
3054
|
+
value: true
|
3055
|
+
});
|
3056
|
+
exports.default = undefined;
|
3057
|
+
|
3058
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // weak
|
3059
|
+
|
3060
|
+
|
3061
|
+
var _looseLeaf = __webpack_require__(1);
|
3062
|
+
|
3063
|
+
var _looseLeaf2 = _interopRequireDefault(_looseLeaf);
|
3064
|
+
|
3065
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3066
|
+
|
3067
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3068
|
+
|
3069
|
+
var Cable = function () {
|
3070
|
+
function Cable(collaborate, cable, channel) {
|
3071
|
+
_classCallCheck(this, Cable);
|
3072
|
+
|
3073
|
+
this.collaborate = collaborate;
|
3074
|
+
this.unackedOps = [];
|
3075
|
+
this.attributeCables = {};
|
3076
|
+
this.documentId = this.collaborate.documentId;
|
3077
|
+
|
3078
|
+
this.subscription = cable.subscriptions.create({
|
3079
|
+
channel: channel,
|
3080
|
+
documentId: this.documentId
|
3081
|
+
}, {
|
3082
|
+
connected: this.connected.bind(this),
|
3083
|
+
received: this.received.bind(this)
|
3084
|
+
});
|
3085
|
+
}
|
3086
|
+
|
3087
|
+
_createClass(Cable, [{
|
3088
|
+
key: 'destroy',
|
3089
|
+
value: function destroy() {
|
3090
|
+
this.subscription.unsubscribe();
|
3091
|
+
}
|
3092
|
+
}, {
|
3093
|
+
key: 'addAttribute',
|
3094
|
+
value: function addAttribute(attribute, attributeCable) {
|
3095
|
+
this.attributeCables[attribute] = attributeCable;
|
3096
|
+
}
|
3097
|
+
}, {
|
3098
|
+
key: 'removeAttribute',
|
3099
|
+
value: function removeAttribute(attribute) {
|
3100
|
+
delete this.attributeCables[attribute];
|
3101
|
+
}
|
3102
|
+
}, {
|
3103
|
+
key: 'connected',
|
3104
|
+
value: function connected() {
|
3105
|
+
var _this = this;
|
3106
|
+
|
3107
|
+
return setTimeout(function () {
|
3108
|
+
_this.subscription.perform('document', {
|
3109
|
+
id: _this.documentId
|
3110
|
+
});
|
3111
|
+
|
3112
|
+
return console.info('Document Setup Complete');
|
3113
|
+
}, 200);
|
3114
|
+
}
|
3115
|
+
}, {
|
3116
|
+
key: 'received',
|
3117
|
+
value: function received(data) {
|
3118
|
+
switch (data.action) {
|
3119
|
+
case 'subscribed':
|
3120
|
+
return this.subscribed(data);
|
3121
|
+
case 'attribute':
|
3122
|
+
return this.receiveAttribute(data);
|
3123
|
+
case 'operation':
|
3124
|
+
return this.receiveOperation(data);
|
3125
|
+
default:
|
3126
|
+
console.warn(data.action + ' unhandled');
|
3127
|
+
return console.info(data);
|
3128
|
+
}
|
3129
|
+
}
|
3130
|
+
}, {
|
3131
|
+
key: 'sendOperation',
|
3132
|
+
value: function sendOperation(data) {
|
3133
|
+
data.client_id = this.clientId;
|
3134
|
+
data.document_id = this.documentId;
|
3135
|
+
return this.subscription.perform('operation', data);
|
3136
|
+
}
|
3137
|
+
}, {
|
3138
|
+
key: 'subscribed',
|
3139
|
+
value: function subscribed(data) {
|
3140
|
+
this.clientId = data.client_id;
|
3141
|
+
console.log('Set client ID as ' + this.clientId);
|
3142
|
+
}
|
3143
|
+
}, {
|
3144
|
+
key: 'receiveAttribute',
|
3145
|
+
value: function receiveAttribute(data) {
|
3146
|
+
if (data.document_id !== this.documentId) {
|
3147
|
+
return;
|
3148
|
+
}
|
3149
|
+
|
3150
|
+
var attributeCable = this.attributeCables[data.attribute];
|
3151
|
+
|
3152
|
+
if (!attributeCable) {
|
3153
|
+
console.warn('Received collaboration message for ' + data.attribute + ', but it has not been registered');
|
3154
|
+
|
3155
|
+
return;
|
3156
|
+
}
|
3157
|
+
|
3158
|
+
return attributeCable.receiveAttribute(data);
|
3159
|
+
}
|
3160
|
+
}, {
|
3161
|
+
key: 'receiveOperation',
|
3162
|
+
value: function receiveOperation(data) {
|
3163
|
+
if (data.document_id !== this.documentId) {
|
3164
|
+
return;
|
3165
|
+
}
|
3166
|
+
|
3167
|
+
var attributeCable = this.attributeCables[data.attribute];
|
3168
|
+
|
3169
|
+
if (!attributeCable) {
|
3170
|
+
console.warn('Received collaboration message for ' + data.attribute + ', but it has not been registered');
|
3171
|
+
|
3172
|
+
return;
|
3173
|
+
}
|
3174
|
+
|
3175
|
+
return attributeCable.receiveOperation(data);
|
3176
|
+
}
|
3177
|
+
}]);
|
3178
|
+
|
3179
|
+
return Cable;
|
3180
|
+
}();
|
3181
|
+
|
3182
|
+
exports.default = Cable;
|
3183
|
+
;
|
3184
|
+
|
3185
|
+
/***/ },
|
3186
|
+
/* 23 */
|
3187
|
+
/***/ function(module, exports, __webpack_require__) {
|
3188
|
+
|
3189
|
+
'use strict';
|
3190
|
+
|
3191
|
+
Object.defineProperty(exports, "__esModule", {
|
3192
|
+
value: true
|
3193
|
+
});
|
3194
|
+
|
3195
|
+
var _textareaAdapter = __webpack_require__(24);
|
3196
|
+
|
3197
|
+
var _textareaAdapter2 = _interopRequireDefault(_textareaAdapter);
|
3198
|
+
|
3199
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3200
|
+
|
3201
|
+
exports.default = {
|
3202
|
+
TextAreaAdapter: _textareaAdapter2.default
|
3203
|
+
};
|
3204
|
+
|
3205
|
+
/***/ },
|
3206
|
+
/* 24 */
|
3207
|
+
/***/ function(module, exports, __webpack_require__) {
|
3208
|
+
|
3209
|
+
'use strict';
|
3210
|
+
|
3211
|
+
Object.defineProperty(exports, "__esModule", {
|
3212
|
+
value: true
|
3213
|
+
});
|
3214
|
+
exports.default = undefined;
|
3215
|
+
|
3216
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // weak
|
3217
|
+
|
3218
|
+
var _ot = __webpack_require__(3);
|
3219
|
+
|
3220
|
+
var _ot2 = _interopRequireDefault(_ot);
|
3221
|
+
|
3222
|
+
var _collaborativeAttribute = __webpack_require__(16);
|
3223
|
+
|
3224
|
+
var _collaborativeAttribute2 = _interopRequireDefault(_collaborativeAttribute);
|
3225
|
+
|
3226
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3227
|
+
|
3228
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3229
|
+
|
3230
|
+
var TextAreaAdapter = function () {
|
3231
|
+
function TextAreaAdapter(collaborativeAttribute, textarea) {
|
3232
|
+
_classCallCheck(this, TextAreaAdapter);
|
3233
|
+
|
3234
|
+
this.collaborativeAttribute = collaborativeAttribute;
|
3235
|
+
this.$textarea = textarea;
|
3236
|
+
this.oldContent = this.$textarea.value;
|
3237
|
+
|
3238
|
+
var _arr = ['keyup', 'cut', 'paste'];
|
3239
|
+
for (var _i = 0; _i < _arr.length; _i++) {
|
3240
|
+
var eventName = _arr[_i];
|
3241
|
+
this.$textarea.addEventListener(eventName, this.textChange.bind(this));
|
3242
|
+
}
|
3243
|
+
|
3244
|
+
this.collaborativeAttribute.on('remoteOperation', this.applyRemoteOperation.bind(this));
|
3245
|
+
}
|
3246
|
+
|
3247
|
+
_createClass(TextAreaAdapter, [{
|
3248
|
+
key: 'textChange',
|
3249
|
+
value: function textChange() {
|
3250
|
+
var newContent = this.$textarea.value;
|
3251
|
+
var operation = this.operationFromTextChange(this.oldContent, newContent);
|
3252
|
+
this.oldContent = newContent;
|
3253
|
+
return this.collaborativeAttribute.localOperation(operation);
|
3254
|
+
}
|
3255
|
+
}, {
|
3256
|
+
key: 'operationFromTextChange',
|
3257
|
+
value: function operationFromTextChange(oldContent, newContent) {
|
3258
|
+
var op = new _ot2.default.TextOperation();
|
3259
|
+
|
3260
|
+
if (oldContent === newContent) {
|
3261
|
+
return op;
|
3262
|
+
}
|
3263
|
+
|
3264
|
+
var commonStart = 0;
|
3265
|
+
|
3266
|
+
while (oldContent.charAt(commonStart) === newContent.charAt(commonStart)) {
|
3267
|
+
commonStart++;
|
3268
|
+
}
|
3269
|
+
|
3270
|
+
var commonEnd = 0;
|
3271
|
+
|
3272
|
+
while (oldContent.charAt(oldContent.length - 1 - commonEnd) === newContent.charAt(newContent.length - 1 - commonEnd)) {
|
3273
|
+
if (commonEnd + commonStart === oldContent.length) {
|
3274
|
+
break;
|
3275
|
+
}
|
3276
|
+
|
3277
|
+
if (commonEnd + commonStart === newContent.length) {
|
3278
|
+
break;
|
3279
|
+
}
|
3280
|
+
|
3281
|
+
commonEnd++;
|
3282
|
+
}
|
3283
|
+
|
3284
|
+
op.retain(commonStart);
|
3285
|
+
|
3286
|
+
if (oldContent.length !== commonStart + commonEnd) {
|
3287
|
+
op.delete(oldContent.length - commonStart - commonEnd);
|
3288
|
+
}
|
3289
|
+
|
3290
|
+
if (newContent.length !== commonStart + commonEnd) {
|
3291
|
+
op.insert(newContent.slice(commonStart, newContent.length - commonEnd));
|
3292
|
+
}
|
3293
|
+
|
3294
|
+
op.retain(commonEnd);
|
3295
|
+
return op;
|
3296
|
+
}
|
3297
|
+
}, {
|
3298
|
+
key: 'applyRemoteOperation',
|
3299
|
+
value: function applyRemoteOperation(operation) {
|
3300
|
+
var content = operation.apply(this.oldContent);
|
3301
|
+
var selectionStart = this.$textarea.selectionStart;
|
3302
|
+
var selectionEnd = this.$textarea.selectionEnd;
|
3303
|
+
this.$textarea.value = content;
|
3304
|
+
this.$textarea.setSelectionRange(selectionStart, selectionEnd);
|
3305
|
+
this.oldContent = content;
|
3306
|
+
}
|
3307
|
+
}]);
|
3308
|
+
|
3309
|
+
return TextAreaAdapter;
|
3310
|
+
}();
|
3311
|
+
|
3312
|
+
exports.default = TextAreaAdapter;
|
3313
|
+
|
3314
|
+
/***/ },
|
3315
|
+
/* 25 */
|
3316
|
+
/***/ function(module, exports) {
|
3317
|
+
|
3318
|
+
"use strict";
|
3319
|
+
|
3320
|
+
Object.defineProperty(exports, "__esModule", {
|
3321
|
+
value: true
|
3322
|
+
});
|
3323
|
+
|
3324
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
3325
|
+
|
3326
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3327
|
+
|
3328
|
+
// weak
|
3329
|
+
|
3330
|
+
var Events = function () {
|
3331
|
+
function Events(object) {
|
3332
|
+
_classCallCheck(this, Events);
|
3333
|
+
|
3334
|
+
this._listeners = [];
|
3335
|
+
}
|
3336
|
+
|
3337
|
+
_createClass(Events, [{
|
3338
|
+
key: "attachHandlers",
|
3339
|
+
value: function attachHandlers(object) {
|
3340
|
+
object.on = this.on.bind(this);
|
3341
|
+
object.off = this.off.bind(this);
|
3342
|
+
object.trigger = this.trigger.bind(this);
|
3343
|
+
}
|
3344
|
+
}, {
|
3345
|
+
key: "on",
|
3346
|
+
value: function on(eventName, callback) {
|
3347
|
+
this._listeners[eventName] || (this._listeners[eventName] = []);
|
3348
|
+
return this._listeners[eventName].push(callback);
|
3349
|
+
}
|
3350
|
+
}, {
|
3351
|
+
key: "off",
|
3352
|
+
value: function off(eventName, callback) {
|
3353
|
+
if (!callback) {
|
3354
|
+
delete this._listeners[eventName];
|
3355
|
+
return;
|
3356
|
+
}
|
3357
|
+
|
3358
|
+
var index = this._listeners[eventName].indexOf(callback);
|
3359
|
+
return this._listeners.splice(index, 1);
|
3360
|
+
}
|
3361
|
+
}, {
|
3362
|
+
key: "trigger",
|
3363
|
+
value: function trigger(eventName) {
|
3364
|
+
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
3365
|
+
args[_key - 1] = arguments[_key];
|
3366
|
+
}
|
3367
|
+
|
3368
|
+
var listeners = this._listeners[eventName] || [];
|
3369
|
+
|
3370
|
+
return function () {
|
3371
|
+
var _iteratorNormalCompletion = true;
|
3372
|
+
var _didIteratorError = false;
|
3373
|
+
var _iteratorError = undefined;
|
3374
|
+
|
3375
|
+
try {
|
3376
|
+
for (var _iterator = listeners[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
3377
|
+
var callback = _step.value;
|
3378
|
+
|
3379
|
+
callback.apply(undefined, args);
|
3380
|
+
}
|
3381
|
+
} catch (err) {
|
3382
|
+
_didIteratorError = true;
|
3383
|
+
_iteratorError = err;
|
3384
|
+
} finally {
|
3385
|
+
try {
|
3386
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
3387
|
+
_iterator.return();
|
3388
|
+
}
|
3389
|
+
} finally {
|
3390
|
+
if (_didIteratorError) {
|
3391
|
+
throw _iteratorError;
|
3392
|
+
}
|
3393
|
+
}
|
3394
|
+
}
|
3395
|
+
}();
|
3396
|
+
}
|
3397
|
+
}]);
|
3398
|
+
|
3399
|
+
return Events;
|
3400
|
+
}();
|
3401
|
+
|
3402
|
+
exports.default = Events;
|
3403
|
+
;
|
3404
|
+
|
3405
|
+
/***/ }
|
3406
|
+
/******/ ])
|
3407
|
+
});
|
3408
|
+
;
|