loose_leaf 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 [](https://travis-ci.org/rike422/loose-leaf) [](https://coveralls.io/github/rike422/loose-leaf?branch=master) [](https://codeclimate.com/github/rike422/loose-leaf) [](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
|
+
[](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
|
+
;
|