ee04a32a7d533a5a3d7613155e1961ccb6822529595970e29a89d79b2ff1c423c6165dfd25269ee55915e91d0b230c7ea7b2b319c24c06679a42a7a2e7fd87 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. # Delta [![Build Status](https://github.com/quilljs/delta/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/quilljs/delta/actions?query=branch%3Amaster) [![Coverage Status](https://img.shields.io/coveralls/quilljs/delta.svg)](https://coveralls.io/r/quilljs/delta)
  2. Deltas are a simple, yet expressive format that can be used to describe contents and changes. The format is JSON based, and is human readable, yet easily parsible by machines. Deltas can describe any rich text document, includes all text and formatting information, without the ambiguity and complexity of HTML.
  3. A Delta is made up of an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of Operations, which describe changes 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.
  4. Don’t be confused by its name Delta—Deltas represents both documents and changes to documents. If you think of Deltas as the instructions from going from one document to another, the way Deltas represent a document is by expressing the instructions starting from an empty document.
  5. ## Quick Example
  6. ```js
  7. // Document with text "Gandalf the Grey"
  8. // with "Gandalf" bolded, and "Grey" in grey
  9. const delta = new Delta([
  10. { insert: 'Gandalf', attributes: { bold: true } },
  11. { insert: ' the ' },
  12. { insert: 'Grey', attributes: { color: '#ccc' } }
  13. ]);
  14. // Change intended to be applied to above:
  15. // Keep the first 12 characters, insert a white 'White'
  16. // and delete the next four characters ('Grey')
  17. const death = new Delta().retain(12)
  18. .insert('White', { color: '#fff' })
  19. .delete(4);
  20. // {
  21. // ops: [
  22. // { retain: 12 },
  23. // { insert: 'White', attributes: { color: '#fff' } },
  24. // { delete: 4 }
  25. // ]
  26. // }
  27. // Applying the above:
  28. const restored = delta.compose(death);
  29. // {
  30. // ops: [
  31. // { insert: 'Gandalf', attributes: { bold: true } },
  32. // { insert: ' the ' },
  33. // { insert: 'White', attributes: { color: '#fff' } }
  34. // ]
  35. // }
  36. ```
  37. This README describes Deltas in its general form and API functionality. Additional information on the way Quill specifically uses Deltas can be found on its own [Delta docs](http://quilljs.com/docs/delta/). A walkthough of the motivation and design thinking behind Deltas are on [Designing the Delta Format](http://quilljs.com/guides/designing-the-delta-format/).
  38. This format is suitable for [Operational Transform](https://en.wikipedia.org/wiki/Operational_transformation) and defines several functions to support this use case.
  39. ## Contents
  40. #### Operations
  41. - [`insert`](#insert-operation)
  42. - [`delete`](#delete-operation)
  43. - [`retain`](#retain-operation)
  44. #### Construction
  45. - [`constructor`](#constructor)
  46. - [`insert`](#insert)
  47. - [`delete`](#delete)
  48. - [`retain`](#retain)
  49. #### Documents
  50. These methods called on or with non-document Deltas will result in undefined behavior.
  51. - [`concat`](#concat)
  52. - [`diff`](#diff)
  53. - [`eachLine`](#eachline)
  54. - [`invert`](#invert)
  55. #### Utility
  56. - [`filter`](#filter)
  57. - [`forEach`](#foreach)
  58. - [`length`](#length)
  59. - [`map`](#map)
  60. - [`partition`](#partition)
  61. - [`reduce`](#reduce)
  62. - [`slice`](#slice)
  63. #### Operational Transform
  64. - [`compose`](#compose)
  65. - [`transform`](#transform)
  66. - [`transformPosition`](#transformposition)
  67. ## Operations
  68. ### Insert Operation
  69. 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).
  70. 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.
  71. ```js
  72. // Insert a bolded "Text"
  73. { insert: "Text", attributes: { bold: true } }
  74. // Insert a link
  75. { insert: "Google", attributes: { link: 'https://www.google.com' } }
  76. // Insert an embed
  77. {
  78. insert: { image: 'https://octodex.github.com/images/labtocat.png' },
  79. attributes: { alt: "Lab Octocat" }
  80. }
  81. // Insert another embed
  82. {
  83. insert: { video: 'https://www.youtube.com/watch?v=dMH0bHeiRNg' },
  84. attributes: {
  85. width: 420,
  86. height: 315
  87. }
  88. }
  89. ```
  90. ### Delete Operation
  91. Delete operations have a Number `delete` key defined representing the number of characters to delete. All embeds have a length of 1.
  92. ```js
  93. // Delete the next 10 characters
  94. { delete: 10 }
  95. ```
  96. ### Retain Operation
  97. 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.
  98. *Note: It is not necessary to retain the last characters of a document as this is implied.*
  99. ```js
  100. // Keep the next 5 characters
  101. { retain: 5 }
  102. // Keep and bold the next 5 characters
  103. { retain: 5, attributes: { bold: true } }
  104. // Keep and unbold the next 5 characters
  105. // More specifically, remove the bold key in the attributes Object
  106. // in the next 5 characters
  107. { retain: 5, attributes: { bold: null } }
  108. ```
  109. ## Construction
  110. ### constructor
  111. Creates a new Delta object.
  112. #### Methods
  113. - `new Delta()`
  114. - `new Delta(ops)`
  115. - `new Delta(delta)`
  116. #### Parameters
  117. - `ops` - Array of operations
  118. - `delta` - Object with an `ops` key set to an array of operations
  119. *Note: No validity/sanity check is performed when constructed with ops or delta. The new delta's internal ops array will also be assigned from ops or delta.ops without deep copying.*
  120. #### Example
  121. ```js
  122. const delta = new Delta([
  123. { insert: 'Hello World' },
  124. { insert: '!', attributes: { bold: true }}
  125. ]);
  126. const packet = JSON.stringify(delta);
  127. const other = new Delta(JSON.parse(packet));
  128. const chained = new Delta().insert('Hello World').insert('!', { bold: true });
  129. ```
  130. ---
  131. ### insert()
  132. Appends an insert operation. Returns `this` for chainability.
  133. #### Methods
  134. - `insert(text, attributes)`
  135. - `insert(embed, attributes)`
  136. #### Parameters
  137. - `text` - String representing text to insert
  138. - `embed` - Object representing embed type to insert
  139. - `attributes` - Optional attributes to apply
  140. #### Example
  141. ```js
  142. delta.insert('Text', { bold: true, color: '#ccc' });
  143. delta.insert({ image: 'https://octodex.github.com/images/labtocat.png' });
  144. ```
  145. ---
  146. ### delete()
  147. Appends a delete operation. Returns `this` for chainability.
  148. #### Methods
  149. - `delete(length)`
  150. #### Parameters
  151. - `length` - Number of characters to delete
  152. #### Example
  153. ```js
  154. delta.delete(5);
  155. ```
  156. ---
  157. ### retain()
  158. Appends a retain operation. Returns `this` for chainability.
  159. #### Methods
  160. - `retain(length, attributes)`
  161. #### Parameters
  162. - `length` - Number of characters to retain
  163. - `attributes` - Optional attributes to apply
  164. #### Example
  165. ```js
  166. delta.retain(4).retain(5, { color: '#0c6' });
  167. ```
  168. ## Documents
  169. ### concat()
  170. Returns a new Delta representing the concatenation of this and another document Delta's operations.
  171. #### Methods
  172. - `concat(other)`
  173. #### Parameters
  174. - `other` - Document Delta to concatenate
  175. #### Returns
  176. - `Delta` - Concatenated document Delta
  177. #### Example
  178. ```js
  179. const a = new Delta().insert('Hello');
  180. const b = new Delta().insert('!', { bold: true });
  181. // {
  182. // ops: [
  183. // { insert: 'Hello' },
  184. // { insert: '!', attributes: { bold: true } }
  185. // ]
  186. // }
  187. const concat = a.concat(b);
  188. ```
  189. ---
  190. ### diff()
  191. Returns a Delta representing the difference between two documents. Optionally, accepts a suggested index where change took place, often representing a cursor position *before* change.
  192. #### Methods
  193. - `diff(other)`
  194. - `diff(other, index)`
  195. #### Parameters
  196. - `other` - Document Delta to diff against
  197. - `index` - Suggested index where change took place
  198. #### Returns
  199. - `Delta` - difference between the two documents
  200. #### Example
  201. ```js
  202. const a = new Delta().insert('Hello');
  203. const b = new Delta().insert('Hello!');
  204. const diff = a.diff(b); // { ops: [{ retain: 5 }, { insert: '!' }] }
  205. // a.compose(diff) == b
  206. ```
  207. ---
  208. ### eachLine()
  209. Iterates through document Delta, calling a given function with a Delta and attributes object, representing the line segment.
  210. #### Methods
  211. - `eachLine(predicate, newline)`
  212. #### Parameters
  213. - `predicate` - function to call on each line group
  214. - `newline` - newline character, defaults to `\n`
  215. #### Example
  216. ```js
  217. const delta = new Delta().insert('Hello\n\n')
  218. .insert('World')
  219. .insert({ image: 'octocat.png' })
  220. .insert('\n', { align: 'right' })
  221. .insert('!');
  222. delta.eachLine((line, attributes, i) => {
  223. console.log(line, attributes, i);
  224. // Can return false to exit loop early
  225. });
  226. // Should log:
  227. // { ops: [{ insert: 'Hello' }] }, {}, 0
  228. // { ops: [] }, {}, 1
  229. // { ops: [{ insert: 'World' }, { insert: { image: 'octocat.png' } }] }, { align: 'right' }, 2
  230. // { ops: [{ insert: '!' }] }, {}, 3
  231. ```
  232. ---
  233. ### invert()
  234. Returned an inverted delta that has the opposite effect of against a base document delta. That is `base.compose(delta).compose(inverted) === base`.
  235. #### Methods
  236. - `invert(base)`
  237. #### Parameters
  238. - `base` - Document delta to invert against
  239. #### Returns
  240. - `Delta` - inverted delta against the base delta
  241. #### Example
  242. ```js
  243. const base = new Delta().insert('Hello\n')
  244. .insert('World');
  245. const delta = new Delta().retain(6, { bold: true }).insert('!').delete(5);
  246. const inverted = delta.invert(base); // { ops: [
  247. // { retain: 6, attributes: { bold: null } },
  248. // { insert: 'World' },
  249. // { delete: 1 }
  250. // ]}
  251. // base.compose(delta).compose(inverted) === base
  252. ```
  253. ## Utility
  254. ### filter()
  255. Returns an array of operations that passes a given function.
  256. #### Methods
  257. - `filter(predicate)`
  258. #### Parameters
  259. - `predicate` - Function to test each operation against. Return `true` to keep the operation, `false` otherwise.
  260. #### Returns
  261. - `Array` - Filtered resulting array
  262. #### Example
  263. ```js
  264. const delta = new Delta().insert('Hello', { bold: true })
  265. .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
  266. .insert('World!');
  267. const text = delta
  268. .filter((op) => typeof op.insert === 'string')
  269. .map((op) => op.insert)
  270. .join('');
  271. ```
  272. ---
  273. ### forEach()
  274. Iterates through operations, calling the provided function for each operation.
  275. #### Methods
  276. - `forEach(predicate)`
  277. #### Parameters
  278. - `predicate` - Function to call during iteration, passing in the current operation.
  279. #### Example
  280. ```js
  281. delta.forEach((op) => {
  282. console.log(op);
  283. });
  284. ```
  285. ---
  286. ### length()
  287. Returns length of a Delta, which is the sum of the lengths of its operations.
  288. #### Methods
  289. - `length()`
  290. #### Example
  291. ```js
  292. new Delta().insert('Hello').length(); // Returns 5
  293. new Delta().insert('A').retain(2).delete(1).length(); // Returns 4
  294. ```
  295. ---
  296. ### map()
  297. Returns a new array with the results of calling provided function on each operation.
  298. #### Methods
  299. - `map(predicate)`
  300. #### Parameters
  301. - `predicate` - Function to call, passing in the current operation, returning an element of the new array to be returned
  302. #### Returns
  303. - `Array` - A new array with each element being the result of the given function.
  304. #### Example
  305. ```js
  306. const delta = new Delta().insert('Hello', { bold: true })
  307. .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
  308. .insert('World!');
  309. const text = delta
  310. .map((op) => {
  311. if (typeof op.insert === 'string') {
  312. return op.insert;
  313. } else {
  314. return '';
  315. }
  316. })
  317. .join('');
  318. ```
  319. ---
  320. ### partition()
  321. Create an array of two arrays, the first with operations that pass the given function, the other that failed.
  322. #### Methods
  323. - `partition(predicate)`
  324. #### Parameters
  325. - `predicate` - Function to call, passing in the current operation, returning whether that operation passed
  326. #### Returns
  327. - `Array` - A new array of two Arrays, the first with passed operations, the other with failed operations
  328. #### Example
  329. ```js
  330. const delta = new Delta().insert('Hello', { bold: true })
  331. .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
  332. .insert('World!');
  333. const results = delta.partition((op) => typeof op.insert === 'string');
  334. const passed = results[0]; // [{ insert: 'Hello', attributes: { bold: true }},
  335. // { insert: 'World'}]
  336. const failed = results[1]; // [{ insert: { image: 'https://octodex.github.com/images/labtocat.png' }}]
  337. ```
  338. ---
  339. ### reduce()
  340. Applies given function against an accumulator and each operation to reduce to a single value.
  341. #### Methods
  342. - `reduce(predicate, initialValue)`
  343. #### Parameters
  344. - `predicate` - Function to call per iteration, returning an accumulated value
  345. - `initialValue` - Initial value to pass to first call to predicate
  346. #### Returns
  347. - `any` - the accumulated value
  348. #### Example
  349. ```js
  350. const delta = new Delta().insert('Hello', { bold: true })
  351. .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
  352. .insert('World!');
  353. const length = delta.reduce((length, op) => (
  354. length + (op.insert.length || 1);
  355. ), 0);
  356. ```
  357. ---
  358. ### slice()
  359. Returns copy of delta with subset of operations.
  360. #### Methods
  361. - `slice()`
  362. - `slice(start)`
  363. - `slice(start, end)`
  364. #### Parameters
  365. - `start` - Start index of subset, defaults to 0
  366. - `end` - End index of subset, defaults to rest of operations
  367. #### Example
  368. ```js
  369. const delta = new Delta().insert('Hello', { bold: true }).insert(' World');
  370. // {
  371. // ops: [
  372. // { insert: 'Hello', attributes: { bold: true } },
  373. // { insert: ' World' }
  374. // ]
  375. // }
  376. const copy = delta.slice();
  377. // { ops: [{ insert: 'World' }] }
  378. const world = delta.slice(6);
  379. // { ops: [{ insert: ' ' }] }
  380. const space = delta.slice(5, 6);
  381. ```
  382. ## Operational Transform
  383. ### compose()
  384. Returns a Delta that is equivalent to applying the operations of own Delta, followed by another Delta.
  385. #### Methods
  386. - `compose(other)`
  387. #### Parameters
  388. - `other` - Delta to compose
  389. #### Example
  390. ```js
  391. const a = new Delta().insert('abc');
  392. const b = new Delta().retain(1).delete(1);
  393. const composed = a.compose(b); // composed == new Delta().insert('ac');
  394. ```
  395. ---
  396. ### transform()
  397. Transform given Delta against own operations.
  398. #### Methods
  399. - `transform(other, priority = false)`
  400. - `transform(index, priority = false)` - Alias for [`transformPosition`](#tranformposition)
  401. #### Parameters
  402. - `other` - Delta to transform
  403. - `priority` - Boolean used to break ties. If `true`, then `this` takes priority
  404. over `other`, that is, its actions are considered to happen "first."
  405. #### Returns
  406. - `Delta` - transformed Delta
  407. #### Example
  408. ```js
  409. const a = new Delta().insert('a');
  410. const b = new Delta().insert('b').retain(5).insert('c');
  411. a.transform(b, true); // new Delta().retain(1).insert('b').retain(5).insert('c');
  412. a.transform(b, false); // new Delta().insert('b').retain(6).insert('c');
  413. ```
  414. ---
  415. ### transformPosition()
  416. Transform an index against the delta. Useful for representing cursor/selection positions.
  417. #### Methods
  418. - `transformPosition(index, priority = false)`
  419. #### Parameters
  420. - `index` - index to transform
  421. #### Returns
  422. - `Number` - transformed index
  423. #### Example
  424. ```js
  425. const delta = new Delta().retain(5).insert('a');
  426. delta.transformPosition(4); // 4
  427. delta.transformPosition(5); // 6
  428. ```