@livingdocs/editable.js 3.0.14 → 3.1.0

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@livingdocs/editable.js",
3
3
  "description": "Friendly contenteditable API",
4
- "version": "3.0.14",
4
+ "version": "3.1.0",
5
5
  "dependencies": {
6
6
  "@babel/runtime": "^7.16.7",
7
7
  "bowser": "2.11.0",
@@ -83,11 +83,11 @@
83
83
  "isCi": true,
84
84
  "name": "Drone",
85
85
  "service": "drone",
86
- "commit": "7118df530cd823cb0da198a546b99cd16192739a",
87
- "build": "440",
86
+ "commit": "2e7b8ed5efd8fb418a159ded67be45ddf9941999",
87
+ "build": "482",
88
88
  "branch": "master",
89
89
  "isPr": false,
90
90
  "slug": "livingdocsIO/editable.js",
91
- "date": "2022-02-03T20:50:51.262Z"
91
+ "date": "2022-03-22T20:40:52.111Z"
92
92
  }
93
93
  }
package/src/config.js CHANGED
@@ -10,6 +10,7 @@ export default {
10
10
  editableClass: 'js-editable',
11
11
  editableDisabledClass: 'js-editable-disabled',
12
12
  pastingAttribute: 'data-editable-is-pasting',
13
+ trimLeadingAndTrailingWhitespaces: true,
13
14
  boldMarkup: {
14
15
  type: 'tag',
15
16
  name: 'strong',
package/src/content.js CHANGED
@@ -4,6 +4,7 @@ import * as rangeSaveRestore from './range-save-restore'
4
4
  import * as parser from './parser'
5
5
  import * as string from './util/string'
6
6
  import {createElement} from './util/dom'
7
+ import config from './config'
7
8
 
8
9
  function restoreRange (host, range, func) {
9
10
  range = rangeSaveRestore.save(range)
@@ -161,7 +162,9 @@ function removeWhitespaces (node, type, firstCall = true) {
161
162
  if (elem?.nodeType !== nodeType.textNode) return
162
163
  // Remove whitespaces at the end or start of a block with content
163
164
  // e.g. ' Hello world' > 'Hello World'
164
- elem.textContent = elem.textContent.replace(type.startsWith('last') ? /\s+$/ : /^\s+/, '')
165
+ if (config.trimLeadingAndTrailingWhitespaces) {
166
+ elem.textContent = elem.textContent.replace(type.startsWith('last') ? / +$/ : /^ +/, '')
167
+ }
165
168
  }
166
169
 
167
170
  // Remove elements that were inserted for internal or user interface purposes
package/src/dispatcher.js CHANGED
@@ -5,6 +5,7 @@ import SelectionWatcher from './selection-watcher'
5
5
  import config from './config'
6
6
  import Keyboard from './keyboard'
7
7
  import {closest} from './util/dom'
8
+ import {replaceLast, endsWithSingleSpace} from './util/string'
8
9
 
9
10
  // This will be set to true once we detect the input event is working.
10
11
  // Input event description on MDN:
@@ -134,7 +135,12 @@ export default class Dispatcher {
134
135
 
135
136
  const {blocks, cursor} = clipboard.paste(block, selection, clipboardContent)
136
137
  if (blocks.length) {
137
- this.notify('paste', block, blocks, cursor)
138
+ if (endsWithSingleSpace(evt.target.innerText)) {
139
+ block.innerHTML = replaceLast(block.innerHTML, ' ', ' ')
140
+ this.notify('paste', block, blocks, this.editable.createCursorAtEnd(block))
141
+ } else {
142
+ this.notify('paste', block, blocks, cursor)
143
+ }
138
144
 
139
145
  // The input event does not fire when we process the content manually
140
146
  // and insert it via script
@@ -134,7 +134,7 @@ export default {
134
134
  // This has two notable behaviours:
135
135
  // - It uses a NodeIterator which will skip elements
136
136
  // with data-editable="remove"
137
- // - It returns a space for <br> elements
137
+ // - It returns a \n for <br> elements
138
138
  // (The only block level element allowed inside of editables)
139
139
  function getText (element, callback) {
140
140
  const iterator = new NodeIterator(element)
@@ -143,7 +143,7 @@ function getText (element, callback) {
143
143
  if (next.nodeType === nodeType.textNode && next.data !== '') {
144
144
  callback(next.data)
145
145
  } else if (next.nodeType === nodeType.elementNode && next.nodeName === 'BR') {
146
- callback(' ')
146
+ callback('\n')
147
147
  }
148
148
  }
149
149
  }
package/src/selection.js CHANGED
@@ -94,10 +94,10 @@ export default class Selection extends Cursor {
94
94
 
95
95
  // trims whitespaces on the left and right of a selection, i.e. what you want in case of links
96
96
  trimRange () {
97
- const textToLink = this.range.toString()
98
- const whitespacesOnTheLeft = textToLink.search(/\S|$/)
99
- const lastNonWhitespace = textToLink.search(/\S[\s]+$/)
100
- const whitespacesOnTheRight = lastNonWhitespace === -1 ? 0 : textToLink.length - (lastNonWhitespace + 1)
97
+ const textToTrim = this.range.toString()
98
+ const whitespacesOnTheLeft = textToTrim.search(/\S|$/)
99
+ const lastNonWhitespace = textToTrim.search(/\S[\s]+$/)
100
+ const whitespacesOnTheRight = lastNonWhitespace === -1 ? 0 : textToTrim.length - (lastNonWhitespace + 1)
101
101
  this.range.setStart(this.range.startContainer, this.range.startOffset + whitespacesOnTheLeft)
102
102
  this.range.setEnd(this.range.endContainer, this.range.endOffset - whitespacesOnTheRight)
103
103
  }
@@ -61,3 +61,21 @@ export function browserEscapeHtml (str) {
61
61
  div.appendChild(document.createTextNode(str))
62
62
  return div.innerHTML
63
63
  }
64
+
65
+ export function replaceLast (text, searchValue, replaceValue) {
66
+ if (!text) return ''
67
+ text = `${text}`
68
+ if (!searchValue || replaceValue == null) return text
69
+ const lastOccurrenceIndex = text.lastIndexOf(searchValue)
70
+ return `${
71
+ text.slice(0, lastOccurrenceIndex)
72
+ }${
73
+ replaceValue
74
+ }${
75
+ text.slice(lastOccurrenceIndex + searchValue.length)
76
+ }`
77
+ }
78
+
79
+ export function endsWithSingleSpace (text) {
80
+ return /\S+\s{1}$/.test(text)
81
+ }