@livingdocs/editable.js 3.0.2 → 3.0.6

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.2",
4
+ "version": "3.0.6",
5
5
  "dependencies": {
6
6
  "@babel/runtime": "^7.13.17",
7
7
  "bowser": "2.11.0",
@@ -9,21 +9,21 @@
9
9
  "rangy": "^1.3.0"
10
10
  },
11
11
  "devDependencies": {
12
- "@babel/cli": "^7.13.16",
13
- "@babel/core": "^7.13.16",
14
- "@babel/plugin-transform-runtime": "^7.13.15",
15
- "@babel/preset-env": "^7.13.15",
12
+ "@babel/cli": "^7.15.7",
13
+ "@babel/core": "^7.15.8",
14
+ "@babel/plugin-transform-runtime": "^7.15.8",
15
+ "@babel/preset-env": "^7.15.8",
16
16
  "@babel/preset-react": "^7.13.13",
17
17
  "@esm-bundle/chai": "^4.3.4-fix.0",
18
- "babel-loader": "^8.2.2",
18
+ "babel-loader": "^8.2.3",
19
19
  "babel-plugin-add-module-exports": "^1.0.4",
20
- "babel-plugin-istanbul": "^6.0.0",
21
- "css-loader": "^5.2.4",
22
- "eslint": "^7.24.0",
20
+ "babel-plugin-istanbul": "^6.1.1",
21
+ "css-loader": "^6.4.0",
22
+ "eslint": "^7.32.0",
23
23
  "eslint-plugin-react": "^7.23.2",
24
24
  "font-awesome": "^4.7.0",
25
25
  "gh-pages": "^3.1.0",
26
- "karma": "^6.3.2",
26
+ "karma": "^6.3.6",
27
27
  "karma-chrome-launcher": "^3.1.0",
28
28
  "karma-coverage": "^2.0.3",
29
29
  "karma-firefox-launcher": "^2.1.0",
@@ -31,9 +31,9 @@
31
31
  "karma-safari-launcher": "^1.0.0",
32
32
  "karma-sourcemap-loader": "^0.3.8",
33
33
  "karma-webpack": "^5.0.0",
34
- "mocha": "^9.0.3",
34
+ "mocha": "^9.1.3",
35
35
  "normalize.css": "^8.0.1",
36
- "prismjs": "^1.23.0",
36
+ "prismjs": "^1.25.0",
37
37
  "react": "^17.0.2",
38
38
  "react-dom": "^17.0.2",
39
39
  "react-transition-group": "^4.4.1",
@@ -41,9 +41,9 @@
41
41
  "sinon": "^10.0.0",
42
42
  "style-loader": "^2.0.0",
43
43
  "url-loader": "^4.1.1",
44
- "webpack": "^5.35.0",
45
- "webpack-cli": "^4.6.0",
46
- "webpack-dev-server": "^4.0.0-beta.3"
44
+ "webpack": "^5.60.0",
45
+ "webpack-cli": "^4.9.1",
46
+ "webpack-dev-server": "^4.3.1"
47
47
  },
48
48
  "engines": {
49
49
  "node": ">=12"
@@ -84,11 +84,11 @@
84
84
  "isCi": true,
85
85
  "name": "Drone",
86
86
  "service": "drone",
87
- "commit": "ab9af60d16071cd20777eec7eee75fc932559f05",
88
- "build": "337",
87
+ "commit": "371fd4f350c9d109eb57b0e68d2308702137f6c7",
88
+ "build": "376",
89
89
  "branch": "master",
90
90
  "isPr": false,
91
91
  "slug": "livingdocsIO/editable.js",
92
- "date": "2021-09-07T16:42:50.857Z"
92
+ "date": "2021-10-26T16:02:22.840Z"
93
93
  }
94
94
  }
package/src/clipboard.js CHANGED
@@ -25,46 +25,20 @@ export function updateConfig (conf) {
25
25
  rules.splitIntoBlocks.forEach((name) => { splitIntoBlocks[name] = true })
26
26
  }
27
27
 
28
- export function paste (element, cursor, callback) {
28
+ export function paste (element, cursor, clipboardContent) {
29
29
  const document = element.ownerDocument
30
30
  element.setAttribute(config.pastingAttribute, true)
31
31
 
32
32
  if (cursor.isSelection) cursor = cursor.deleteContent()
33
33
 
34
- // Create a placeholder and set the focus to the pasteholder
35
- // to redirect the browser pasting into the pasteholder.
36
- cursor.save()
37
- const pasteHolder = injectPasteholder(document)
38
- pasteHolder.focus()
39
-
40
- // Use a timeout to give the browser some time to paste the content.
41
- // After that grab the pasted content, filter it and restore the focus.
42
- setTimeout(() => {
43
- const blocks = parseContent(pasteHolder)
44
- pasteHolder.remove()
45
- element.removeAttribute(config.pastingAttribute)
46
- cursor.restore()
47
- callback(blocks, cursor)
48
- }, 0)
49
- }
50
-
51
- /**
52
- * @param { Document } document
53
- */
54
- export function injectPasteholder (document) {
34
+ // Create a placeholder to help parse HTML
55
35
  const pasteHolder = document.createElement('div')
56
- pasteHolder.setAttribute('contenteditable', true)
57
- pasteHolder.style.position = 'fixed'
58
- pasteHolder.style.right = '5px'
59
- pasteHolder.style.top = '50%'
60
- pasteHolder.style.width = '1px'
61
- pasteHolder.style.height = '1px'
62
- pasteHolder.style.overflow = 'hidden'
63
- pasteHolder.style.outline = 'none'
64
-
65
- document.body.appendChild(pasteHolder)
66
-
67
- return pasteHolder
36
+ pasteHolder.innerHTML = clipboardContent
37
+
38
+ const blocks = parseContent(pasteHolder)
39
+
40
+ element.removeAttribute(config.pastingAttribute)
41
+ return {blocks, cursor}
68
42
  }
69
43
 
70
44
  /**
package/src/cursor.js CHANGED
@@ -96,7 +96,7 @@ export default class Cursor {
96
96
  hostRange.collapse(false)
97
97
  const hostCoords = getRangeBoundingClientRect(hostRange, this.win)
98
98
  const cursorCoords = getRangeBoundingClientRect(this.range.nativeRange, this.win)
99
- return hostCoords.bottom === cursorCoords.bottom
99
+ return isCloseTo(hostCoords.bottom, cursorCoords.bottom)
100
100
  }
101
101
 
102
102
  isAtFirstLine () {
@@ -105,7 +105,7 @@ export default class Cursor {
105
105
  hostRange.collapse(true)
106
106
  const hostCoords = getRangeBoundingClientRect(hostRange, this.win)
107
107
  const cursorCoords = getRangeBoundingClientRect(this.range.nativeRange, this.win)
108
- return hostCoords.top === cursorCoords.top
108
+ return isCloseTo(hostCoords.top, cursorCoords.top)
109
109
  }
110
110
 
111
111
  isAtBeginning () {
@@ -368,3 +368,9 @@ function getRangeBoundingClientRect (range, win) {
368
368
  el.remove()
369
369
  return coords
370
370
  }
371
+
372
+ function isCloseTo (a, b) {
373
+ if (a === b) return true
374
+ if (Math.abs(a - b) <= 2) return true
375
+ return false
376
+ }
package/src/dispatcher.js CHANGED
@@ -127,20 +127,21 @@ export default class Dispatcher {
127
127
  .setupDocumentListener('paste', function pasteListener (evt) {
128
128
  const block = this.getEditableBlockByEvent(evt)
129
129
  if (!block) return
130
- const afterPaste = (blocks, cursor) => {
131
- if (blocks.length) {
132
- this.notify('paste', block, blocks, cursor)
133
-
134
- // The input event does not fire when we process the content manually
135
- // and insert it via script
136
- this.notify('change', block)
137
- } else {
138
- cursor.setVisibleSelection()
139
- }
140
- }
141
130
 
142
- const cursor = this.selectionWatcher.getFreshSelection()
143
- clipboard.paste(block, cursor, afterPaste)
131
+ evt.preventDefault()
132
+ const selection = this.selectionWatcher.getFreshSelection()
133
+ const clipboardContent = evt.clipboardData.getData('text/html') || evt.clipboardData.getData('text/plain')
134
+
135
+ const {blocks, cursor} = clipboard.paste(block, selection, clipboardContent)
136
+ if (blocks.length) {
137
+ this.notify('paste', block, blocks, cursor)
138
+
139
+ // The input event does not fire when we process the content manually
140
+ // and insert it via script
141
+ this.notify('change', block)
142
+ } else {
143
+ cursor.setVisibleSelection()
144
+ }
144
145
  })
145
146
  .setupDocumentListener('input', function inputListener (evt) {
146
147
  const block = this.getEditableBlockByEvent(evt)
@@ -7,8 +7,22 @@ export default class WordHighlighting {
7
7
  this.matchMode = matchMode
8
8
  }
9
9
 
10
+ isElement (obj) {
11
+ try {
12
+ // Using W3 DOM2 (works for FF, Opera and Chrome)
13
+ return obj instanceof HTMLElement
14
+ } catch (e) {
15
+ // Browsers not supporting W3 DOM2 don't have HTMLElement and
16
+ // an exception is thrown and we end up here. Testing some
17
+ // properties that all elements have (works on IE7)
18
+ return (typeof obj === 'object') &&
19
+ (obj.nodeType === 1) && (typeof obj.style === 'object') &&
20
+ (typeof obj.ownerDocument === 'object')
21
+ }
22
+ }
23
+
10
24
  findMatches (text, highlights) {
11
- if (!text) return
25
+ if (!text || text === '' || !this.isElement(this.marker)) return
12
26
 
13
27
  if (highlights && highlights.length > 0) {
14
28
  return this.searchMatches(text, highlights)
@@ -21,11 +35,7 @@ export default class WordHighlighting {
21
35
  : createHighlightRegex
22
36
 
23
37
  const regex = createRegex(highlights)
24
- const matches = []
25
- let match
26
- while ((match = regex.exec(text))) {
27
- matches.push(match)
28
- }
38
+ const matches = [...text.matchAll(regex)]
29
39
 
30
40
  return matches.map((entry) => this.prepareMatch(entry))
31
41
  }
package/src/selection.js CHANGED
@@ -78,7 +78,15 @@ export default class Selection extends Cursor {
78
78
  link (href, attrs = {}) {
79
79
  if (href) attrs.href = href
80
80
  const link = this.createElement(config.linkMarkup.name, config.linkMarkup.attribs)
81
- for (const key in attrs) link.setAttribute(key, attrs[key])
81
+ for (const key in attrs) {
82
+ const value = attrs[key]
83
+ if (value === undefined) continue
84
+ if (value === null) {
85
+ link.removeAttribute(key)
86
+ } else {
87
+ link.setAttribute(key, value)
88
+ }
89
+ }
82
90
  this.forceWrap(link)
83
91
  }
84
92