├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── lib └── type.js ├── package-lock.json ├── package.json └── test ├── fuzzer.js └── presence.js /.gitignore: -------------------------------------------------------------------------------- 1 | /.coverage 2 | /node_modules 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: '6' 3 | script: 4 | - npm test 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v4.1.0 2 | 3 | #### Features 4 | 5 | - Add `transformPresence()` method 6 | 7 | ## v4.0.0 8 | 9 | #### Breaking Changes 10 | 11 | These were never documented as officially supported but to be safe we are doing a major version bump. 12 | 13 | - No longer works in IE8 as there is function called delete and IE8 treats that as a reserved identifier 14 | - The source structure has changed so those utilizing NPM's ability to import from arbitrary directories ex. import 15 | DeltaOp from 'quill-delta/lib/op' will have to update their imports 16 | 17 | ## v3.0.0 18 | 19 | #### Breaking Changes 20 | 21 | - Deep copy and compare attributes and deltas 22 | 23 | #### Features 24 | 25 | ## v2.1.0 26 | 27 | #### Features 28 | 29 | - Add `concat()` method for document Deltas 30 | 31 | ## v2.0.0 32 | 33 | #### Breaking Changes 34 | 35 | - `compose()` returns a new Delta instead of self-modifying 36 | 37 | #### Features 38 | 39 | - Support embed being any non-string type 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jason Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rich Text OT Type [![Build Status](https://travis-ci.org/ottypes/rich-text.svg?branch=master)](https://travis-ci.org/ottypes/rich-text) 2 | 3 | An OT Type for rich text documents. 4 | 5 | For documentation on the spec this type implements, see [ottypes/docs](https://github.com/ottypes/docs). Rich Text does not implement the optional `invert`, but does implement `normalize`, tranformCursor, `serialize`, and `deserialize`. Please refer to [ottypes/docs](https://github.com/ottypes/docs) for documentation. 6 | 7 | Rich Text uses [quill-delta](https://github.com/quilljs/delta) on the back end. 8 | 9 | 10 | ## Operations 11 | 12 | Operations are an Array of changes, each operation describing a singular change to a document. They can be an [`insert`](#insert-operation), [`delete`](#delete-operation) or [`retain`](#retain-operation). Note operations do not take an index. They always describe the change at the current index. Use retains to "keep" or "skip" certain parts of the document. 13 | 14 | 15 | ### Insert Operation 16 | 17 | Insert operations have an `insert` key defined. A String value represents inserting text. Any other type represents inserting an embed (however only one level of object comparison will be performed for equality). 18 | 19 | In both cases of text and embeds, an optional `attributes` key can be defined with an Object to describe additonal formatting information. Formats can be changed by the [retain](#retain) operation. 20 | 21 | ```js 22 | // Insert a bolded "Text" 23 | { insert: "Text", attributes: { bold: true } } 24 | 25 | // Insert a link 26 | { insert: "Google", attributes: { href: 'https://www.google.com' } } 27 | 28 | // Insert an embed 29 | { 30 | insert: { image: 'https://octodex.github.com/images/labtocat.png' }, 31 | attributes: { alt: "Lab Octocat" } 32 | } 33 | 34 | // Insert another embed 35 | { 36 | insert: { video: 'https://www.youtube.com/watch?v=dMH0bHeiRNg' }, 37 | attributes: { 38 | width: 420, 39 | height: 315 40 | } 41 | } 42 | ``` 43 | 44 | ### Delete Operation 45 | 46 | Delete operations have a Number `delete` key defined representing the number of characters to delete. All embeds have a length of 1. 47 | 48 | ```js 49 | // Delete the next 10 characters 50 | { delete: 10 } 51 | ``` 52 | 53 | ### Retain Operation 54 | 55 | Retain operations have a Number `retain` key defined representing the number of characters to keep (other libraries might use the name keep or skip). An optional `attributes` key can be defined with an Object to describe formatting changes to the character range. A value of `null` in the `attributes` Object represents removal of that key. 56 | 57 | *Note: It is not necessary to retain the last characters of a document as this is implied.* 58 | 59 | ```js 60 | // Keep the next 5 characters 61 | { retain: 5 } 62 | 63 | // Keep and bold the next 5 characters 64 | { retain: 5, attributes: { bold: true } } 65 | 66 | // Keep and unbold the next 5 characters 67 | // More specifically, remove the bold key in the attributes Object 68 | // in the next 5 characters 69 | { retain: 5, attributes: { bold: null } } 70 | ``` 71 | 72 | 73 | ## Commentary 74 | 75 | This library was originally implemented as part of a full fledged Google Docs like product called Stypi. Eventually, parts were open sourced--the editor became [Quill](https://github.com/quilljs/quill), the realtime engine became [tandem](https://github.com/tandem/tandem) and the document type became [tandem-core](https://github.com/tandem/tandem-core). 76 | 77 | [ShareJS](https://github.com/josephg/ShareJS) was a more established open source realtime collaboration engine, so `tandem` and `tandem-core` were deprecated to unify support under one project. `tandem-core` was rewritten as `rich-text`, to adhere to ShareJS's [OT Type specification](https://github.com/ottypes/docs). 78 | 79 | The needs of a realtime rich text document type was formerly a superset of a generalized rich text document type. As Quill has evolved, the reverse is becoming true. This `rich-text` library today provides the interface to use with ShareJS, but the underlying type and fuctionality is implemented in [`quill-delta`](https://github.com/quilljs/delta). 80 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/type'); 2 | -------------------------------------------------------------------------------- /lib/type.js: -------------------------------------------------------------------------------- 1 | var Delta = require('quill-delta'); 2 | 3 | 4 | module.exports = { 5 | Delta: Delta, 6 | type: { 7 | name: 'rich-text', 8 | uri: 'http://sharejs.org/types/rich-text/v1', 9 | 10 | create: function (initial) { 11 | return new Delta(initial); 12 | }, 13 | 14 | apply: function (snapshot, delta) { 15 | snapshot = new Delta(snapshot); 16 | delta = new Delta(delta); 17 | return snapshot.compose(delta); 18 | }, 19 | 20 | compose: function (delta1, delta2) { 21 | delta1 = new Delta(delta1); 22 | delta2 = new Delta(delta2); 23 | return delta1.compose(delta2); 24 | }, 25 | 26 | diff: function (delta1, delta2) { 27 | delta1 = new Delta(delta1); 28 | delta2 = new Delta(delta2); 29 | return delta1.diff(delta2); 30 | }, 31 | 32 | transform: function (delta1, delta2, side) { 33 | delta1 = new Delta(delta1); 34 | delta2 = new Delta(delta2); 35 | // Fuzzer specs is in opposite order of delta interface 36 | return delta2.transform(delta1, side === 'left'); 37 | }, 38 | 39 | transformCursor: function(cursor, delta, isOwnOp) { 40 | return delta.transformPosition(cursor, !isOwnOp); 41 | }, 42 | 43 | normalize: function(delta) { 44 | return delta; // quill-delta is already canonical 45 | }, 46 | 47 | serialize: function(delta) { 48 | return delta.ops; 49 | }, 50 | 51 | deserialize: function(ops) { 52 | return new Delta(ops); 53 | }, 54 | 55 | transformPresence: function(range, op, isOwnOp) { 56 | if (!range) { 57 | return null; 58 | } 59 | 60 | const delta = new Delta(op); 61 | const start = this.transformCursor(range.index, delta, isOwnOp); 62 | const end = this.transformCursor(range.index + range.length, delta, isOwnOp); 63 | 64 | return Object.assign({}, range, { 65 | index: start, 66 | length: end - start, 67 | }); 68 | }, 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rich-text", 3 | "version": "4.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-colors": { 8 | "version": "3.2.3", 9 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 10 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", 11 | "dev": true 12 | }, 13 | "ansi-regex": { 14 | "version": "3.0.0", 15 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 16 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 17 | "dev": true 18 | }, 19 | "ansi-styles": { 20 | "version": "3.2.1", 21 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 22 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 23 | "dev": true, 24 | "requires": { 25 | "color-convert": "^1.9.0" 26 | } 27 | }, 28 | "argparse": { 29 | "version": "1.0.10", 30 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 31 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 32 | "dev": true, 33 | "requires": { 34 | "sprintf-js": "~1.0.2" 35 | } 36 | }, 37 | "assertion-error": { 38 | "version": "1.1.0", 39 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 40 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 41 | "dev": true 42 | }, 43 | "balanced-match": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 46 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 47 | "dev": true 48 | }, 49 | "brace-expansion": { 50 | "version": "1.1.11", 51 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 52 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 53 | "dev": true, 54 | "requires": { 55 | "balanced-match": "^1.0.0", 56 | "concat-map": "0.0.1" 57 | } 58 | }, 59 | "browser-stdout": { 60 | "version": "1.3.1", 61 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 62 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 63 | "dev": true 64 | }, 65 | "camelcase": { 66 | "version": "5.3.1", 67 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 68 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 69 | "dev": true 70 | }, 71 | "chai": { 72 | "version": "4.2.0", 73 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 74 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 75 | "dev": true, 76 | "requires": { 77 | "assertion-error": "^1.1.0", 78 | "check-error": "^1.0.2", 79 | "deep-eql": "^3.0.1", 80 | "get-func-name": "^2.0.0", 81 | "pathval": "^1.1.0", 82 | "type-detect": "^4.0.5" 83 | } 84 | }, 85 | "chalk": { 86 | "version": "2.4.2", 87 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 88 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 89 | "dev": true, 90 | "requires": { 91 | "ansi-styles": "^3.2.1", 92 | "escape-string-regexp": "^1.0.5", 93 | "supports-color": "^5.3.0" 94 | }, 95 | "dependencies": { 96 | "supports-color": { 97 | "version": "5.5.0", 98 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 99 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 100 | "dev": true, 101 | "requires": { 102 | "has-flag": "^3.0.0" 103 | } 104 | } 105 | } 106 | }, 107 | "check-error": { 108 | "version": "1.0.2", 109 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 110 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 111 | "dev": true 112 | }, 113 | "cli-progress": { 114 | "version": "2.1.1", 115 | "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-2.1.1.tgz", 116 | "integrity": "sha512-TSJw3LY9ZRSis7yYzQ7flIdtQMbacd9oYoiFphJhI4SzgmqF0zErO+uNv0lbUjk1L4AGfHQJ4OVYYzW+JV66KA==", 117 | "dev": true, 118 | "requires": { 119 | "colors": "^1.1.2", 120 | "string-width": "^2.1.1" 121 | } 122 | }, 123 | "cliui": { 124 | "version": "4.1.0", 125 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", 126 | "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", 127 | "dev": true, 128 | "requires": { 129 | "string-width": "^2.1.1", 130 | "strip-ansi": "^4.0.0", 131 | "wrap-ansi": "^2.0.0" 132 | } 133 | }, 134 | "code-point-at": { 135 | "version": "1.1.0", 136 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 137 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 138 | "dev": true 139 | }, 140 | "color-convert": { 141 | "version": "1.9.3", 142 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 143 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 144 | "dev": true, 145 | "requires": { 146 | "color-name": "1.1.3" 147 | } 148 | }, 149 | "color-name": { 150 | "version": "1.1.3", 151 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 152 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 153 | "dev": true 154 | }, 155 | "colors": { 156 | "version": "1.3.3", 157 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", 158 | "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", 159 | "dev": true 160 | }, 161 | "concat-map": { 162 | "version": "0.0.1", 163 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 164 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 165 | "dev": true 166 | }, 167 | "cross-spawn": { 168 | "version": "6.0.5", 169 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 170 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 171 | "dev": true, 172 | "requires": { 173 | "nice-try": "^1.0.4", 174 | "path-key": "^2.0.1", 175 | "semver": "^5.5.0", 176 | "shebang-command": "^1.2.0", 177 | "which": "^1.2.9" 178 | } 179 | }, 180 | "debug": { 181 | "version": "3.2.6", 182 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 183 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 184 | "dev": true, 185 | "requires": { 186 | "ms": "^2.1.1" 187 | } 188 | }, 189 | "decamelize": { 190 | "version": "1.2.0", 191 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 192 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 193 | "dev": true 194 | }, 195 | "deep-eql": { 196 | "version": "3.0.1", 197 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 198 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 199 | "dev": true, 200 | "requires": { 201 | "type-detect": "^4.0.0" 202 | } 203 | }, 204 | "deep-equal": { 205 | "version": "1.0.1", 206 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 207 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" 208 | }, 209 | "define-properties": { 210 | "version": "1.1.3", 211 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 212 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 213 | "dev": true, 214 | "requires": { 215 | "object-keys": "^1.0.12" 216 | } 217 | }, 218 | "diff": { 219 | "version": "3.5.0", 220 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 221 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 222 | "dev": true 223 | }, 224 | "emoji-regex": { 225 | "version": "7.0.3", 226 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 227 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 228 | "dev": true 229 | }, 230 | "end-of-stream": { 231 | "version": "1.4.1", 232 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 233 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 234 | "dev": true, 235 | "requires": { 236 | "once": "^1.4.0" 237 | } 238 | }, 239 | "es-abstract": { 240 | "version": "1.13.0", 241 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", 242 | "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", 243 | "dev": true, 244 | "requires": { 245 | "es-to-primitive": "^1.2.0", 246 | "function-bind": "^1.1.1", 247 | "has": "^1.0.3", 248 | "is-callable": "^1.1.4", 249 | "is-regex": "^1.0.4", 250 | "object-keys": "^1.0.12" 251 | } 252 | }, 253 | "es-to-primitive": { 254 | "version": "1.2.0", 255 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 256 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 257 | "dev": true, 258 | "requires": { 259 | "is-callable": "^1.1.4", 260 | "is-date-object": "^1.0.1", 261 | "is-symbol": "^1.0.2" 262 | } 263 | }, 264 | "escape-string-regexp": { 265 | "version": "1.0.5", 266 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 267 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 268 | "dev": true 269 | }, 270 | "esprima": { 271 | "version": "4.0.1", 272 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 273 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 274 | "dev": true 275 | }, 276 | "execa": { 277 | "version": "1.0.0", 278 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 279 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 280 | "dev": true, 281 | "requires": { 282 | "cross-spawn": "^6.0.0", 283 | "get-stream": "^4.0.0", 284 | "is-stream": "^1.1.0", 285 | "npm-run-path": "^2.0.0", 286 | "p-finally": "^1.0.0", 287 | "signal-exit": "^3.0.0", 288 | "strip-eof": "^1.0.0" 289 | } 290 | }, 291 | "extend": { 292 | "version": "3.0.2", 293 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 294 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 295 | }, 296 | "fast-diff": { 297 | "version": "1.2.0", 298 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 299 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" 300 | }, 301 | "find-up": { 302 | "version": "3.0.0", 303 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 304 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 305 | "dev": true, 306 | "requires": { 307 | "locate-path": "^3.0.0" 308 | } 309 | }, 310 | "flat": { 311 | "version": "4.1.0", 312 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 313 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 314 | "dev": true, 315 | "requires": { 316 | "is-buffer": "~2.0.3" 317 | } 318 | }, 319 | "fs.realpath": { 320 | "version": "1.0.0", 321 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 322 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 323 | "dev": true 324 | }, 325 | "function-bind": { 326 | "version": "1.1.1", 327 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 328 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 329 | "dev": true 330 | }, 331 | "get-caller-file": { 332 | "version": "2.0.5", 333 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 334 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 335 | "dev": true 336 | }, 337 | "get-func-name": { 338 | "version": "2.0.0", 339 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 340 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 341 | "dev": true 342 | }, 343 | "get-stream": { 344 | "version": "4.1.0", 345 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 346 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 347 | "dev": true, 348 | "requires": { 349 | "pump": "^3.0.0" 350 | } 351 | }, 352 | "glob": { 353 | "version": "7.1.3", 354 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 355 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 356 | "dev": true, 357 | "requires": { 358 | "fs.realpath": "^1.0.0", 359 | "inflight": "^1.0.4", 360 | "inherits": "2", 361 | "minimatch": "^3.0.4", 362 | "once": "^1.3.0", 363 | "path-is-absolute": "^1.0.0" 364 | } 365 | }, 366 | "growl": { 367 | "version": "1.10.5", 368 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 369 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 370 | "dev": true 371 | }, 372 | "has": { 373 | "version": "1.0.3", 374 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 375 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 376 | "dev": true, 377 | "requires": { 378 | "function-bind": "^1.1.1" 379 | } 380 | }, 381 | "has-flag": { 382 | "version": "3.0.0", 383 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 384 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 385 | "dev": true 386 | }, 387 | "has-symbols": { 388 | "version": "1.0.0", 389 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 390 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 391 | "dev": true 392 | }, 393 | "he": { 394 | "version": "1.2.0", 395 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 396 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 397 | "dev": true 398 | }, 399 | "inflight": { 400 | "version": "1.0.6", 401 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 402 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 403 | "dev": true, 404 | "requires": { 405 | "once": "^1.3.0", 406 | "wrappy": "1" 407 | } 408 | }, 409 | "inherits": { 410 | "version": "2.0.3", 411 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 412 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 413 | "dev": true 414 | }, 415 | "invert-kv": { 416 | "version": "2.0.0", 417 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", 418 | "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", 419 | "dev": true 420 | }, 421 | "is-buffer": { 422 | "version": "2.0.3", 423 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", 424 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", 425 | "dev": true 426 | }, 427 | "is-callable": { 428 | "version": "1.1.4", 429 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 430 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", 431 | "dev": true 432 | }, 433 | "is-date-object": { 434 | "version": "1.0.1", 435 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 436 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 437 | "dev": true 438 | }, 439 | "is-fullwidth-code-point": { 440 | "version": "2.0.0", 441 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 442 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 443 | "dev": true 444 | }, 445 | "is-regex": { 446 | "version": "1.0.4", 447 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 448 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 449 | "dev": true, 450 | "requires": { 451 | "has": "^1.0.1" 452 | } 453 | }, 454 | "is-stream": { 455 | "version": "1.1.0", 456 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 457 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 458 | "dev": true 459 | }, 460 | "is-symbol": { 461 | "version": "1.0.2", 462 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 463 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 464 | "dev": true, 465 | "requires": { 466 | "has-symbols": "^1.0.0" 467 | } 468 | }, 469 | "isexe": { 470 | "version": "2.0.0", 471 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 472 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 473 | "dev": true 474 | }, 475 | "js-yaml": { 476 | "version": "3.13.1", 477 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 478 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 479 | "dev": true, 480 | "requires": { 481 | "argparse": "^1.0.7", 482 | "esprima": "^4.0.0" 483 | } 484 | }, 485 | "lcid": { 486 | "version": "2.0.0", 487 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", 488 | "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", 489 | "dev": true, 490 | "requires": { 491 | "invert-kv": "^2.0.0" 492 | } 493 | }, 494 | "locate-path": { 495 | "version": "3.0.0", 496 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 497 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 498 | "dev": true, 499 | "requires": { 500 | "p-locate": "^3.0.0", 501 | "path-exists": "^3.0.0" 502 | } 503 | }, 504 | "lodash": { 505 | "version": "4.17.15", 506 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 507 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 508 | "dev": true 509 | }, 510 | "log-symbols": { 511 | "version": "2.2.0", 512 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 513 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 514 | "dev": true, 515 | "requires": { 516 | "chalk": "^2.0.1" 517 | } 518 | }, 519 | "map-age-cleaner": { 520 | "version": "0.1.3", 521 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", 522 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", 523 | "dev": true, 524 | "requires": { 525 | "p-defer": "^1.0.0" 526 | } 527 | }, 528 | "mem": { 529 | "version": "4.3.0", 530 | "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", 531 | "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", 532 | "dev": true, 533 | "requires": { 534 | "map-age-cleaner": "^0.1.1", 535 | "mimic-fn": "^2.0.0", 536 | "p-is-promise": "^2.0.0" 537 | } 538 | }, 539 | "mimic-fn": { 540 | "version": "2.1.0", 541 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 542 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 543 | "dev": true 544 | }, 545 | "minimatch": { 546 | "version": "3.0.4", 547 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 548 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 549 | "dev": true, 550 | "requires": { 551 | "brace-expansion": "^1.1.7" 552 | } 553 | }, 554 | "minimist": { 555 | "version": "0.0.8", 556 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 557 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 558 | "dev": true 559 | }, 560 | "mkdirp": { 561 | "version": "0.5.1", 562 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 563 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 564 | "dev": true, 565 | "requires": { 566 | "minimist": "0.0.8" 567 | } 568 | }, 569 | "mocha": { 570 | "version": "6.1.4", 571 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", 572 | "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", 573 | "dev": true, 574 | "requires": { 575 | "ansi-colors": "3.2.3", 576 | "browser-stdout": "1.3.1", 577 | "debug": "3.2.6", 578 | "diff": "3.5.0", 579 | "escape-string-regexp": "1.0.5", 580 | "find-up": "3.0.0", 581 | "glob": "7.1.3", 582 | "growl": "1.10.5", 583 | "he": "1.2.0", 584 | "js-yaml": "3.13.1", 585 | "log-symbols": "2.2.0", 586 | "minimatch": "3.0.4", 587 | "mkdirp": "0.5.1", 588 | "ms": "2.1.1", 589 | "node-environment-flags": "1.0.5", 590 | "object.assign": "4.1.0", 591 | "strip-json-comments": "2.0.1", 592 | "supports-color": "6.0.0", 593 | "which": "1.3.1", 594 | "wide-align": "1.1.3", 595 | "yargs": "13.2.2", 596 | "yargs-parser": "13.0.0", 597 | "yargs-unparser": "1.5.0" 598 | } 599 | }, 600 | "ms": { 601 | "version": "2.1.1", 602 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 603 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 604 | "dev": true 605 | }, 606 | "nice-try": { 607 | "version": "1.0.5", 608 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 609 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 610 | "dev": true 611 | }, 612 | "node-environment-flags": { 613 | "version": "1.0.5", 614 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", 615 | "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", 616 | "dev": true, 617 | "requires": { 618 | "object.getownpropertydescriptors": "^2.0.3", 619 | "semver": "^5.7.0" 620 | } 621 | }, 622 | "npm-run-path": { 623 | "version": "2.0.2", 624 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 625 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 626 | "dev": true, 627 | "requires": { 628 | "path-key": "^2.0.0" 629 | } 630 | }, 631 | "number-is-nan": { 632 | "version": "1.0.1", 633 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 634 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 635 | "dev": true 636 | }, 637 | "object-keys": { 638 | "version": "1.1.1", 639 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 640 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 641 | "dev": true 642 | }, 643 | "object.assign": { 644 | "version": "4.1.0", 645 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 646 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 647 | "dev": true, 648 | "requires": { 649 | "define-properties": "^1.1.2", 650 | "function-bind": "^1.1.1", 651 | "has-symbols": "^1.0.0", 652 | "object-keys": "^1.0.11" 653 | } 654 | }, 655 | "object.getownpropertydescriptors": { 656 | "version": "2.0.3", 657 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", 658 | "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", 659 | "dev": true, 660 | "requires": { 661 | "define-properties": "^1.1.2", 662 | "es-abstract": "^1.5.1" 663 | } 664 | }, 665 | "once": { 666 | "version": "1.4.0", 667 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 668 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 669 | "dev": true, 670 | "requires": { 671 | "wrappy": "1" 672 | } 673 | }, 674 | "os-locale": { 675 | "version": "3.1.0", 676 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", 677 | "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", 678 | "dev": true, 679 | "requires": { 680 | "execa": "^1.0.0", 681 | "lcid": "^2.0.0", 682 | "mem": "^4.0.0" 683 | } 684 | }, 685 | "ot-fuzzer": { 686 | "version": "1.2.1", 687 | "resolved": "https://registry.npmjs.org/ot-fuzzer/-/ot-fuzzer-1.2.1.tgz", 688 | "integrity": "sha512-dOm+Wb1Mqrw8ql5ksiZFf3Bdsruj5r4mHaa3COqtbg0ClkGjxZXwMGgMLjWslq/b0maeiw5yvYwIdH6As4svHg==", 689 | "dev": true, 690 | "requires": { 691 | "cli-progress": "^2.1.1", 692 | "seedrandom": "^2.4.4" 693 | } 694 | }, 695 | "p-defer": { 696 | "version": "1.0.0", 697 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 698 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", 699 | "dev": true 700 | }, 701 | "p-finally": { 702 | "version": "1.0.0", 703 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 704 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 705 | "dev": true 706 | }, 707 | "p-is-promise": { 708 | "version": "2.1.0", 709 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", 710 | "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", 711 | "dev": true 712 | }, 713 | "p-limit": { 714 | "version": "2.2.0", 715 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", 716 | "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", 717 | "dev": true, 718 | "requires": { 719 | "p-try": "^2.0.0" 720 | } 721 | }, 722 | "p-locate": { 723 | "version": "3.0.0", 724 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 725 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 726 | "dev": true, 727 | "requires": { 728 | "p-limit": "^2.0.0" 729 | } 730 | }, 731 | "p-try": { 732 | "version": "2.2.0", 733 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 734 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 735 | "dev": true 736 | }, 737 | "path-exists": { 738 | "version": "3.0.0", 739 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 740 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 741 | "dev": true 742 | }, 743 | "path-is-absolute": { 744 | "version": "1.0.1", 745 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 746 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 747 | "dev": true 748 | }, 749 | "path-key": { 750 | "version": "2.0.1", 751 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 752 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 753 | "dev": true 754 | }, 755 | "pathval": { 756 | "version": "1.1.0", 757 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 758 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 759 | "dev": true 760 | }, 761 | "pump": { 762 | "version": "3.0.0", 763 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 764 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 765 | "dev": true, 766 | "requires": { 767 | "end-of-stream": "^1.1.0", 768 | "once": "^1.3.1" 769 | } 770 | }, 771 | "quill-delta": { 772 | "version": "4.2.1", 773 | "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.1.tgz", 774 | "integrity": "sha512-Y2nksOj6Q+4hizre8n0dml76vLNGK4/y86EoI1d7rv6EL1bx7DPDYRmqQMPu1UqFQO/uQuVHQ3fOmm4ZSzWrfA==", 775 | "requires": { 776 | "deep-equal": "^1.0.1", 777 | "extend": "^3.0.2", 778 | "fast-diff": "1.2.0" 779 | } 780 | }, 781 | "require-directory": { 782 | "version": "2.1.1", 783 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 784 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 785 | "dev": true 786 | }, 787 | "require-main-filename": { 788 | "version": "2.0.0", 789 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 790 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 791 | "dev": true 792 | }, 793 | "seedrandom": { 794 | "version": "2.4.4", 795 | "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", 796 | "integrity": "sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==", 797 | "dev": true 798 | }, 799 | "semver": { 800 | "version": "5.7.0", 801 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 802 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", 803 | "dev": true 804 | }, 805 | "set-blocking": { 806 | "version": "2.0.0", 807 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 808 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 809 | "dev": true 810 | }, 811 | "shebang-command": { 812 | "version": "1.2.0", 813 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 814 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 815 | "dev": true, 816 | "requires": { 817 | "shebang-regex": "^1.0.0" 818 | } 819 | }, 820 | "shebang-regex": { 821 | "version": "1.0.0", 822 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 823 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 824 | "dev": true 825 | }, 826 | "signal-exit": { 827 | "version": "3.0.2", 828 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 829 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 830 | "dev": true 831 | }, 832 | "sprintf-js": { 833 | "version": "1.0.3", 834 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 835 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 836 | "dev": true 837 | }, 838 | "string-width": { 839 | "version": "2.1.1", 840 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 841 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 842 | "dev": true, 843 | "requires": { 844 | "is-fullwidth-code-point": "^2.0.0", 845 | "strip-ansi": "^4.0.0" 846 | } 847 | }, 848 | "strip-ansi": { 849 | "version": "4.0.0", 850 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 851 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 852 | "dev": true, 853 | "requires": { 854 | "ansi-regex": "^3.0.0" 855 | } 856 | }, 857 | "strip-eof": { 858 | "version": "1.0.0", 859 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 860 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 861 | "dev": true 862 | }, 863 | "strip-json-comments": { 864 | "version": "2.0.1", 865 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 866 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 867 | "dev": true 868 | }, 869 | "supports-color": { 870 | "version": "6.0.0", 871 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 872 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 873 | "dev": true, 874 | "requires": { 875 | "has-flag": "^3.0.0" 876 | } 877 | }, 878 | "type-detect": { 879 | "version": "4.0.8", 880 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 881 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 882 | "dev": true 883 | }, 884 | "which": { 885 | "version": "1.3.1", 886 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 887 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 888 | "dev": true, 889 | "requires": { 890 | "isexe": "^2.0.0" 891 | } 892 | }, 893 | "which-module": { 894 | "version": "2.0.0", 895 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 896 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 897 | "dev": true 898 | }, 899 | "wide-align": { 900 | "version": "1.1.3", 901 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 902 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 903 | "dev": true, 904 | "requires": { 905 | "string-width": "^1.0.2 || 2" 906 | } 907 | }, 908 | "wrap-ansi": { 909 | "version": "2.1.0", 910 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 911 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 912 | "dev": true, 913 | "requires": { 914 | "string-width": "^1.0.1", 915 | "strip-ansi": "^3.0.1" 916 | }, 917 | "dependencies": { 918 | "ansi-regex": { 919 | "version": "2.1.1", 920 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 921 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 922 | "dev": true 923 | }, 924 | "is-fullwidth-code-point": { 925 | "version": "1.0.0", 926 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 927 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 928 | "dev": true, 929 | "requires": { 930 | "number-is-nan": "^1.0.0" 931 | } 932 | }, 933 | "string-width": { 934 | "version": "1.0.2", 935 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 936 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 937 | "dev": true, 938 | "requires": { 939 | "code-point-at": "^1.0.0", 940 | "is-fullwidth-code-point": "^1.0.0", 941 | "strip-ansi": "^3.0.0" 942 | } 943 | }, 944 | "strip-ansi": { 945 | "version": "3.0.1", 946 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 947 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 948 | "dev": true, 949 | "requires": { 950 | "ansi-regex": "^2.0.0" 951 | } 952 | } 953 | } 954 | }, 955 | "wrappy": { 956 | "version": "1.0.2", 957 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 958 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 959 | "dev": true 960 | }, 961 | "y18n": { 962 | "version": "4.0.0", 963 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 964 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 965 | "dev": true 966 | }, 967 | "yargs": { 968 | "version": "13.2.2", 969 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", 970 | "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", 971 | "dev": true, 972 | "requires": { 973 | "cliui": "^4.0.0", 974 | "find-up": "^3.0.0", 975 | "get-caller-file": "^2.0.1", 976 | "os-locale": "^3.1.0", 977 | "require-directory": "^2.1.1", 978 | "require-main-filename": "^2.0.0", 979 | "set-blocking": "^2.0.0", 980 | "string-width": "^3.0.0", 981 | "which-module": "^2.0.0", 982 | "y18n": "^4.0.0", 983 | "yargs-parser": "^13.0.0" 984 | }, 985 | "dependencies": { 986 | "ansi-regex": { 987 | "version": "4.1.0", 988 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 989 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 990 | "dev": true 991 | }, 992 | "string-width": { 993 | "version": "3.1.0", 994 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 995 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 996 | "dev": true, 997 | "requires": { 998 | "emoji-regex": "^7.0.1", 999 | "is-fullwidth-code-point": "^2.0.0", 1000 | "strip-ansi": "^5.1.0" 1001 | } 1002 | }, 1003 | "strip-ansi": { 1004 | "version": "5.2.0", 1005 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1006 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1007 | "dev": true, 1008 | "requires": { 1009 | "ansi-regex": "^4.1.0" 1010 | } 1011 | } 1012 | } 1013 | }, 1014 | "yargs-parser": { 1015 | "version": "13.0.0", 1016 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", 1017 | "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", 1018 | "dev": true, 1019 | "requires": { 1020 | "camelcase": "^5.0.0", 1021 | "decamelize": "^1.2.0" 1022 | } 1023 | }, 1024 | "yargs-unparser": { 1025 | "version": "1.5.0", 1026 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", 1027 | "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", 1028 | "dev": true, 1029 | "requires": { 1030 | "flat": "^4.1.0", 1031 | "lodash": "^4.17.11", 1032 | "yargs": "^12.0.5" 1033 | }, 1034 | "dependencies": { 1035 | "get-caller-file": { 1036 | "version": "1.0.3", 1037 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 1038 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", 1039 | "dev": true 1040 | }, 1041 | "require-main-filename": { 1042 | "version": "1.0.1", 1043 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 1044 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", 1045 | "dev": true 1046 | }, 1047 | "yargs": { 1048 | "version": "12.0.5", 1049 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", 1050 | "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", 1051 | "dev": true, 1052 | "requires": { 1053 | "cliui": "^4.0.0", 1054 | "decamelize": "^1.2.0", 1055 | "find-up": "^3.0.0", 1056 | "get-caller-file": "^1.0.1", 1057 | "os-locale": "^3.0.0", 1058 | "require-directory": "^2.1.1", 1059 | "require-main-filename": "^1.0.1", 1060 | "set-blocking": "^2.0.0", 1061 | "string-width": "^2.0.0", 1062 | "which-module": "^2.0.0", 1063 | "y18n": "^3.2.1 || ^4.0.0", 1064 | "yargs-parser": "^11.1.1" 1065 | } 1066 | }, 1067 | "yargs-parser": { 1068 | "version": "11.1.1", 1069 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", 1070 | "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", 1071 | "dev": true, 1072 | "requires": { 1073 | "camelcase": "^5.0.0", 1074 | "decamelize": "^1.2.0" 1075 | } 1076 | } 1077 | } 1078 | } 1079 | } 1080 | } 1081 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rich-text", 3 | "version": "4.1.0", 4 | "description": "OT type for rich text", 5 | "author": "Jason Chen ", 6 | "homepage": "https://github.com/ottypes/rich-text", 7 | "main": "index.js", 8 | "dependencies": { 9 | "quill-delta": "^4.2.1" 10 | }, 11 | "devDependencies": { 12 | "chai": "^4.2.0", 13 | "lodash": "^4.17.15", 14 | "mocha": "^6.1.4", 15 | "ot-fuzzer": "^1.2.1" 16 | }, 17 | "engines": { 18 | "node": ">=0.10" 19 | }, 20 | "license": "MIT", 21 | "scripts": { 22 | "test": "mocha test/**/*.js --timeout 5000" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/ottypes/rich-text" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/ottypes/rich-text/issues" 30 | }, 31 | "keywords": [ 32 | "rich text", 33 | "ot", 34 | "operational transform", 35 | "sharejs" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /test/fuzzer.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var expect = require('chai').expect; 3 | var fuzzer = require('ot-fuzzer'); 4 | var richText = require('../lib/type'); 5 | var Delta = richText.Delta; 6 | 7 | var FORMATS = { 8 | color: ['red', 'orange', 'yellow', 'green', 'blue', 'purple', null], 9 | font: ['serif', 'sans-serif', 'monospace', null], 10 | bold: [true, null], 11 | italic: [true, null] 12 | }; 13 | 14 | function generateRandomEmbed () { 15 | switch(fuzzer.randomInt(4)) { 16 | case 0: return 1; 17 | case 1: return 2; 18 | case 2: return { image: 'http://quilljs.com' }; 19 | case 3: return { url: 'http://quilljs.com' }; 20 | } 21 | }; 22 | 23 | function generateRandomFormat (includeNull) { 24 | var format = {}; 25 | for (var key in FORMATS) { 26 | if (fuzzer.randomReal() < 0.5) { 27 | var value = FORMATS[key][fuzzer.randomInt(FORMATS[key].length)]; 28 | if (value || includeNull) { 29 | format[key] = value; 30 | } 31 | } 32 | } 33 | return Object.keys(format).length > 0 ? format : undefined; 34 | }; 35 | 36 | function generateRandomOp (snapshot) { 37 | snapshot = _.cloneDeep(snapshot); 38 | var length = snapshot.ops.reduce(function(length, op) { 39 | if (!op.insert) { 40 | console.error(snapshot); 41 | throw new Error('Snapshot should only have inserts'); 42 | } 43 | // Snapshot should only have inserts 44 | return length + (_.isString(op.insert) ? op.insert.length : 1); 45 | }, 0); 46 | 47 | var base = length > 100 ? 10 : 7; // Favor deleting on long documents 48 | var delta = new Delta(); 49 | var result = new Delta(); 50 | 51 | do { 52 | // Allows insert/delete to occur at the end (deletes will be noop) 53 | var modIndex = fuzzer.randomInt(Math.min(length, 5) + 1); 54 | length -= modIndex; 55 | var modLength = Math.min(length, fuzzer.randomInt(4) + 1); 56 | 57 | delta.retain(modIndex); 58 | var ops = next(snapshot, modIndex); 59 | for (var i in ops) { 60 | result.push(ops[i]); 61 | } 62 | 63 | switch (fuzzer.randomInt(base)) { 64 | case 0: 65 | // Insert plain text 66 | var word = fuzzer.randomWord(); 67 | delta.insert(word); 68 | result.insert(word); 69 | break; 70 | case 1: 71 | // Insert formatted text 72 | var word = fuzzer.randomWord(); 73 | var formats = generateRandomFormat(false); 74 | delta.insert(word, formats); 75 | result.insert(word, formats); 76 | break; 77 | case 2: 78 | // Insert embed 79 | var type = generateRandomEmbed(); 80 | var formats = generateRandomFormat(false); 81 | delta.insert(type, formats); 82 | result.insert(type, formats); 83 | break; 84 | case 3: case 4: 85 | var attributes = generateRandomFormat(true); 86 | delta.retain(modLength, attributes); 87 | ops = next(snapshot, modLength); 88 | for (var i in ops) { 89 | ops[i].attributes = ops[i].attributes || {}; 90 | for (var key in attributes) { 91 | ops[i].attributes[key] = (attributes[key] === null) ? undefined : attributes[key]; 92 | } 93 | ops[i].attributes = _.reduce(ops[i].attributes, function (memo, value, key) { 94 | if (value !== null && value !== undefined) { 95 | memo[key] = value; 96 | } 97 | return memo; 98 | }, {}); 99 | var newOp = { insert: ops[i].insert }; 100 | if (_.keys(ops[i].attributes).length > 0) newOp.attributes = ops[i].attributes; 101 | result.push(newOp); 102 | } 103 | length -= modLength; 104 | break; 105 | default: 106 | next(snapshot, modLength); 107 | delta.delete(modLength); 108 | length -= modLength; 109 | break; 110 | } 111 | } while (length > 0 && fuzzer.randomInt(2) > 0); 112 | 113 | for (var i in snapshot.ops) { 114 | result.push(snapshot.ops[i]); 115 | } 116 | 117 | return [delta, result]; 118 | }; 119 | 120 | function next (snapshot, length) { 121 | var ops = []; 122 | while (length > 0) { 123 | var opLength; 124 | if (_.isString(snapshot.ops[0].insert)) { 125 | if (length >= snapshot.ops[0].insert.length) { 126 | opLength = snapshot.ops[0].insert.length; 127 | ops.push(snapshot.ops.shift()); 128 | } else { 129 | var insert = snapshot.ops[0].insert.substr(0, length); 130 | snapshot.ops[0].insert = snapshot.ops[0].insert.substr(insert.length); 131 | opLength = insert.length; 132 | var op = { insert: insert }; 133 | if (snapshot.ops[0].attributes) { 134 | op.attributes = _.clone(snapshot.ops[0].attributes); 135 | } 136 | ops.push(op); 137 | } 138 | } else { 139 | ops.push(snapshot.ops.shift()); 140 | opLength = 1; 141 | } 142 | length -= opLength; 143 | } 144 | return ops; 145 | }; 146 | 147 | describe('fuzzer', function() { 148 | it('random operations', function () { 149 | expect(function () { 150 | fuzzer(richText.type, generateRandomOp, 100); 151 | }).to.not.throw(Error); 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/presence.js: -------------------------------------------------------------------------------- 1 | const Delta = require("quill-delta"); 2 | var richText = require('../lib/type').type; 3 | var expect = require('chai').expect; 4 | 5 | describe('presence', function() { 6 | it('transforms a zero-length range by an op before it', function() { 7 | const range = {index: 10, length: 0}; 8 | const op = new Delta().insert('foo'); 9 | const transformed = richText.transformPresence(range, op); 10 | expect(transformed).to.eql({index: 13, length: 0}); 11 | }); 12 | 13 | it('does not transform a zero-length range by an op after it', function() { 14 | const range = {index: 10, length: 0}; 15 | const op = new Delta().retain(20).insert('foo'); 16 | const transformed = richText.transformPresence(range, op); 17 | expect(transformed).to.eql({index: 10, length: 0}); 18 | }); 19 | 20 | it('transforms a range with length by an op before it', function() { 21 | const range = {index: 10, length: 3}; 22 | const op = new Delta().insert('foo'); 23 | const transformed = richText.transformPresence(range, op); 24 | expect(transformed).to.eql({index: 13, length: 3}); 25 | }); 26 | 27 | it('transforms a range with length by an op that deletes part of it', function() { 28 | const range = {index: 10, length: 3}; 29 | const op = new Delta().retain(9).delete(3); 30 | const transformed = richText.transformPresence(range, op); 31 | expect(transformed).to.eql({index: 9, length: 1}); 32 | }); 33 | 34 | it('transforms a range with length by an op that deletes the whole range', function() { 35 | const range = {index: 10, length: 3}; 36 | const op = new Delta().retain(9).delete(5); 37 | const transformed = richText.transformPresence(range, op); 38 | expect(transformed).to.eql({index: 9, length: 0}); 39 | }); 40 | 41 | it('keeps extra metadata when transforming', function() { 42 | const range = {index: 10, length: 0, meta: 'lorem ipsum'}; 43 | const op = new Delta().insert('foo'); 44 | const transformed = richText.transformPresence(range, op); 45 | expect(transformed).to.eql({index: 13, length: 0, meta: 'lorem ipsum'}); 46 | }); 47 | 48 | it('returns null when no presence is provided', function() { 49 | const op = new Delta().insert('foo'); 50 | const transformed = richText.transformPresence(undefined, op); 51 | expect(transformed).to.be.null; 52 | }); 53 | 54 | it('advances the cursor if inserting at own index', function() { 55 | const range = {index: 10, length: 2}; 56 | const op = new Delta().retain(10).insert('foo'); 57 | const transformed = richText.transformPresence(range, op, true); 58 | expect(transformed).to.eql({index: 13, length: 2}); 59 | }); 60 | 61 | it('does not advance the cursor if not own op', function () { 62 | const range = {index: 10, length: 2}; 63 | const op = new Delta().retain(10).insert('foo'); 64 | const transformed = richText.transformPresence(range, op, false); 65 | expect(transformed).to.eql({index: 10, length: 5}); 66 | }); 67 | 68 | it('accepts an array of ops rather than a Delta', function() { 69 | const range = {index: 10, length: 0}; 70 | const op = [{insert: 'foo'}]; 71 | const transformed = richText.transformPresence(range, op); 72 | expect(transformed).to.eql({index: 13, length: 0}); 73 | }); 74 | 75 | it('does nothing if no op is provided', function() { 76 | const range = {index: 10, length: 0}; 77 | const transformed = richText.transformPresence(range, undefined); 78 | expect(transformed).to.eql({index: 10, length: 0}); 79 | }); 80 | 81 | it('does not mutate the original range', function() { 82 | const range = {index: 10, length: 0}; 83 | const op = new Delta().insert('foo'); 84 | richText.transformPresence(range, op); 85 | expect(range).to.eql({index: 10, length: 0}); 86 | }); 87 | }); 88 | --------------------------------------------------------------------------------