@svgedit/svgcanvas 7.1.4
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/blur-event.js +156 -0
- package/clear.js +43 -0
- package/coords.js +298 -0
- package/copy-elem.js +45 -0
- package/dataStorage.js +28 -0
- package/dist/svgcanvas.js +515 -0
- package/dist/svgcanvas.js.map +1 -0
- package/draw.js +1064 -0
- package/elem-get-set.js +1077 -0
- package/event.js +1388 -0
- package/history.js +619 -0
- package/historyrecording.js +161 -0
- package/json.js +110 -0
- package/layer.js +228 -0
- package/math.js +221 -0
- package/namespaces.js +40 -0
- package/package.json +54 -0
- package/paint.js +88 -0
- package/paste-elem.js +127 -0
- package/path-actions.js +1237 -0
- package/path-method.js +1012 -0
- package/path.js +781 -0
- package/recalculate.js +794 -0
- package/rollup.config.js +40 -0
- package/sanitize.js +252 -0
- package/select.js +543 -0
- package/selected-elem.js +1297 -0
- package/selection.js +482 -0
- package/svg-exec.js +1289 -0
- package/svgcanvas.js +1347 -0
- package/svgroot.js +36 -0
- package/text-actions.js +530 -0
- package/touch.js +51 -0
- package/undo.js +279 -0
- package/utilities.js +1214 -0
package/svgroot.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools for SVG Root Element.
|
|
3
|
+
* @module svgcanvas
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
|
|
7
|
+
*/
|
|
8
|
+
import { NS } from './namespaces.js'
|
|
9
|
+
import { text2xml } from './utilities.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @function module:svgcanvas.svgRootElement svgRootElement the svg node and its children.
|
|
13
|
+
* @param {Element} svgdoc - window.document
|
|
14
|
+
* @param {ArgumentsArray} dimensions - dimensions of width and height
|
|
15
|
+
* @returns {svgRootElement}
|
|
16
|
+
*/
|
|
17
|
+
export const svgRootElement = function (svgdoc, dimensions) {
|
|
18
|
+
return svgdoc.importNode(
|
|
19
|
+
text2xml(
|
|
20
|
+
`<svg id="svgroot" xmlns="${NS.SVG}" xlinkns="${NS.XLINK}" width="${dimensions[0]}"
|
|
21
|
+
height="${dimensions[1]}" x="${dimensions[0]}" y="${dimensions[1]}" overflow="visible">
|
|
22
|
+
<defs>
|
|
23
|
+
<filter id="canvashadow" filterUnits="objectBoundingBox">
|
|
24
|
+
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
|
|
25
|
+
<feOffset in="blur" dx="5" dy="5" result="offsetBlur"/>
|
|
26
|
+
<feMerge>
|
|
27
|
+
<feMergeNode in="offsetBlur"/>
|
|
28
|
+
<feMergeNode in="SourceGraphic"/>
|
|
29
|
+
</feMerge>
|
|
30
|
+
</filter>
|
|
31
|
+
</defs>
|
|
32
|
+
</svg>`
|
|
33
|
+
).documentElement,
|
|
34
|
+
true
|
|
35
|
+
)
|
|
36
|
+
}
|
package/text-actions.js
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module text-actions Tools for Text edit functions
|
|
3
|
+
* @license MIT
|
|
4
|
+
*
|
|
5
|
+
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NS } from './namespaces.js'
|
|
9
|
+
import {
|
|
10
|
+
transformPoint, getMatrix
|
|
11
|
+
} from './math.js'
|
|
12
|
+
import {
|
|
13
|
+
assignAttributes, getElement, getBBox as utilsGetBBox
|
|
14
|
+
} from './utilities.js'
|
|
15
|
+
import {
|
|
16
|
+
supportsGoodTextCharPos
|
|
17
|
+
} from '../../src/common/browser.js'
|
|
18
|
+
|
|
19
|
+
let svgCanvas = null
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @function module:text-actions.init
|
|
23
|
+
* @param {module:text-actions.svgCanvas} textActionsContext
|
|
24
|
+
* @returns {void}
|
|
25
|
+
*/
|
|
26
|
+
export const init = (canvas) => {
|
|
27
|
+
svgCanvas = canvas
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Group: Text edit functions
|
|
32
|
+
* Functions relating to editing text elements.
|
|
33
|
+
* @namespace {PlainObject} textActions
|
|
34
|
+
* @memberof module:svgcanvas.SvgCanvas#
|
|
35
|
+
*/
|
|
36
|
+
export const textActionsMethod = (function () {
|
|
37
|
+
let curtext
|
|
38
|
+
let textinput
|
|
39
|
+
let cursor
|
|
40
|
+
let selblock
|
|
41
|
+
let blinker
|
|
42
|
+
let chardata = []
|
|
43
|
+
let textbb // , transbb;
|
|
44
|
+
let matrix
|
|
45
|
+
let lastX; let lastY
|
|
46
|
+
let allowDbl
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {Integer} index
|
|
51
|
+
* @returns {void}
|
|
52
|
+
*/
|
|
53
|
+
function setCursor (index) {
|
|
54
|
+
const empty = (textinput.value === '')
|
|
55
|
+
textinput.focus()
|
|
56
|
+
|
|
57
|
+
if (!arguments.length) {
|
|
58
|
+
if (empty) {
|
|
59
|
+
index = 0
|
|
60
|
+
} else {
|
|
61
|
+
if (textinput.selectionEnd !== textinput.selectionStart) { return }
|
|
62
|
+
index = textinput.selectionEnd
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const charbb = chardata[index]
|
|
67
|
+
if (!empty) {
|
|
68
|
+
textinput.setSelectionRange(index, index)
|
|
69
|
+
}
|
|
70
|
+
cursor = getElement('text_cursor')
|
|
71
|
+
if (!cursor) {
|
|
72
|
+
cursor = document.createElementNS(NS.SVG, 'line')
|
|
73
|
+
assignAttributes(cursor, {
|
|
74
|
+
id: 'text_cursor',
|
|
75
|
+
stroke: '#333',
|
|
76
|
+
'stroke-width': 1
|
|
77
|
+
})
|
|
78
|
+
getElement('selectorParentGroup').append(cursor)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!blinker) {
|
|
82
|
+
blinker = setInterval(function () {
|
|
83
|
+
const show = (cursor.getAttribute('display') === 'none')
|
|
84
|
+
cursor.setAttribute('display', show ? 'inline' : 'none')
|
|
85
|
+
}, 600)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const startPt = ptToScreen(charbb.x, textbb.y)
|
|
89
|
+
const endPt = ptToScreen(charbb.x, (textbb.y + textbb.height))
|
|
90
|
+
|
|
91
|
+
assignAttributes(cursor, {
|
|
92
|
+
x1: startPt.x,
|
|
93
|
+
y1: startPt.y,
|
|
94
|
+
x2: endPt.x,
|
|
95
|
+
y2: endPt.y,
|
|
96
|
+
visibility: 'visible',
|
|
97
|
+
display: 'inline'
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if (selblock) { selblock.setAttribute('d', '') }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
*
|
|
105
|
+
* @param {Integer} start
|
|
106
|
+
* @param {Integer} end
|
|
107
|
+
* @param {boolean} skipInput
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
function setSelection (start, end, skipInput) {
|
|
111
|
+
if (start === end) {
|
|
112
|
+
setCursor(end)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!skipInput) {
|
|
117
|
+
textinput.setSelectionRange(start, end)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
selblock = getElement('text_selectblock')
|
|
121
|
+
if (!selblock) {
|
|
122
|
+
selblock = document.createElementNS(NS.SVG, 'path')
|
|
123
|
+
assignAttributes(selblock, {
|
|
124
|
+
id: 'text_selectblock',
|
|
125
|
+
fill: 'green',
|
|
126
|
+
opacity: 0.5,
|
|
127
|
+
style: 'pointer-events:none'
|
|
128
|
+
})
|
|
129
|
+
getElement('selectorParentGroup').append(selblock)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const startbb = chardata[start]
|
|
133
|
+
const endbb = chardata[end]
|
|
134
|
+
|
|
135
|
+
cursor.setAttribute('visibility', 'hidden')
|
|
136
|
+
|
|
137
|
+
const tl = ptToScreen(startbb.x, textbb.y)
|
|
138
|
+
const tr = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y)
|
|
139
|
+
const bl = ptToScreen(startbb.x, textbb.y + textbb.height)
|
|
140
|
+
const br = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y + textbb.height)
|
|
141
|
+
|
|
142
|
+
const dstr = 'M' + tl.x + ',' + tl.y +
|
|
143
|
+
' L' + tr.x + ',' + tr.y +
|
|
144
|
+
' ' + br.x + ',' + br.y +
|
|
145
|
+
' ' + bl.x + ',' + bl.y + 'z'
|
|
146
|
+
|
|
147
|
+
assignAttributes(selblock, {
|
|
148
|
+
d: dstr,
|
|
149
|
+
display: 'inline'
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
*
|
|
155
|
+
* @param {Float} mouseX
|
|
156
|
+
* @param {Float} mouseY
|
|
157
|
+
* @returns {Integer}
|
|
158
|
+
*/
|
|
159
|
+
function getIndexFromPoint (mouseX, mouseY) {
|
|
160
|
+
// Position cursor here
|
|
161
|
+
const pt = svgCanvas.getSvgRoot().createSVGPoint()
|
|
162
|
+
pt.x = mouseX
|
|
163
|
+
pt.y = mouseY
|
|
164
|
+
|
|
165
|
+
// No content, so return 0
|
|
166
|
+
if (chardata.length === 1) { return 0 }
|
|
167
|
+
// Determine if cursor should be on left or right of character
|
|
168
|
+
let charpos = curtext.getCharNumAtPosition(pt)
|
|
169
|
+
if (charpos < 0) {
|
|
170
|
+
// Out of text range, look at mouse coords
|
|
171
|
+
charpos = chardata.length - 2
|
|
172
|
+
if (mouseX <= chardata[0].x) {
|
|
173
|
+
charpos = 0
|
|
174
|
+
}
|
|
175
|
+
} else if (charpos >= chardata.length - 2) {
|
|
176
|
+
charpos = chardata.length - 2
|
|
177
|
+
}
|
|
178
|
+
const charbb = chardata[charpos]
|
|
179
|
+
const mid = charbb.x + (charbb.width / 2)
|
|
180
|
+
if (mouseX > mid) {
|
|
181
|
+
charpos++
|
|
182
|
+
}
|
|
183
|
+
return charpos
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param {Float} mouseX
|
|
189
|
+
* @param {Float} mouseY
|
|
190
|
+
* @returns {void}
|
|
191
|
+
*/
|
|
192
|
+
function setCursorFromPoint (mouseX, mouseY) {
|
|
193
|
+
setCursor(getIndexFromPoint(mouseX, mouseY))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
*
|
|
198
|
+
* @param {Float} x
|
|
199
|
+
* @param {Float} y
|
|
200
|
+
* @param {boolean} apply
|
|
201
|
+
* @returns {void}
|
|
202
|
+
*/
|
|
203
|
+
function setEndSelectionFromPoint (x, y, apply) {
|
|
204
|
+
const i1 = textinput.selectionStart
|
|
205
|
+
const i2 = getIndexFromPoint(x, y)
|
|
206
|
+
|
|
207
|
+
const start = Math.min(i1, i2)
|
|
208
|
+
const end = Math.max(i1, i2)
|
|
209
|
+
setSelection(start, end, !apply)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
*
|
|
214
|
+
* @param {Float} xIn
|
|
215
|
+
* @param {Float} yIn
|
|
216
|
+
* @returns {module:math.XYObject}
|
|
217
|
+
*/
|
|
218
|
+
function screenToPt (xIn, yIn) {
|
|
219
|
+
const out = {
|
|
220
|
+
x: xIn,
|
|
221
|
+
y: yIn
|
|
222
|
+
}
|
|
223
|
+
const zoom = svgCanvas.getZoom()
|
|
224
|
+
out.x /= zoom
|
|
225
|
+
out.y /= zoom
|
|
226
|
+
|
|
227
|
+
if (matrix) {
|
|
228
|
+
const pt = transformPoint(out.x, out.y, matrix.inverse())
|
|
229
|
+
out.x = pt.x
|
|
230
|
+
out.y = pt.y
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return out
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
*
|
|
238
|
+
* @param {Float} xIn
|
|
239
|
+
* @param {Float} yIn
|
|
240
|
+
* @returns {module:math.XYObject}
|
|
241
|
+
*/
|
|
242
|
+
function ptToScreen (xIn, yIn) {
|
|
243
|
+
const out = {
|
|
244
|
+
x: xIn,
|
|
245
|
+
y: yIn
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (matrix) {
|
|
249
|
+
const pt = transformPoint(out.x, out.y, matrix)
|
|
250
|
+
out.x = pt.x
|
|
251
|
+
out.y = pt.y
|
|
252
|
+
}
|
|
253
|
+
const zoom = svgCanvas.getZoom()
|
|
254
|
+
out.x *= zoom
|
|
255
|
+
out.y *= zoom
|
|
256
|
+
|
|
257
|
+
return out
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
*
|
|
262
|
+
* @param {Event} evt
|
|
263
|
+
* @returns {void}
|
|
264
|
+
*/
|
|
265
|
+
function selectAll (evt) {
|
|
266
|
+
setSelection(0, curtext.textContent.length)
|
|
267
|
+
evt.target.removeEventListener('click', selectAll)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
*
|
|
272
|
+
* @param {Event} evt
|
|
273
|
+
* @returns {void}
|
|
274
|
+
*/
|
|
275
|
+
function selectWord (evt) {
|
|
276
|
+
if (!allowDbl || !curtext) { return }
|
|
277
|
+
const zoom = svgCanvas.getZoom()
|
|
278
|
+
const ept = transformPoint(evt.pageX, evt.pageY, svgCanvas.getrootSctm())
|
|
279
|
+
const mouseX = ept.x * zoom
|
|
280
|
+
const mouseY = ept.y * zoom
|
|
281
|
+
const pt = screenToPt(mouseX, mouseY)
|
|
282
|
+
|
|
283
|
+
const index = getIndexFromPoint(pt.x, pt.y)
|
|
284
|
+
const str = curtext.textContent
|
|
285
|
+
const first = str.substr(0, index).replace(/[a-z\d]+$/i, '').length
|
|
286
|
+
const m = str.substr(index).match(/^[a-z\d]+/i)
|
|
287
|
+
const last = (m ? m[0].length : 0) + index
|
|
288
|
+
setSelection(first, last)
|
|
289
|
+
|
|
290
|
+
// Set tripleclick
|
|
291
|
+
svgCanvas.$click(evt.target, selectAll)
|
|
292
|
+
|
|
293
|
+
setTimeout(function () {
|
|
294
|
+
evt.target.removeEventListener('click', selectAll)
|
|
295
|
+
}, 300)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return /** @lends module:svgcanvas.SvgCanvas#textActions */ {
|
|
299
|
+
/**
|
|
300
|
+
* @param {Element} target
|
|
301
|
+
* @param {Float} x
|
|
302
|
+
* @param {Float} y
|
|
303
|
+
* @returns {void}
|
|
304
|
+
*/
|
|
305
|
+
select (target, x, y) {
|
|
306
|
+
curtext = target
|
|
307
|
+
svgCanvas.textActions.toEditMode(x, y)
|
|
308
|
+
},
|
|
309
|
+
/**
|
|
310
|
+
* @param {Element} elem
|
|
311
|
+
* @returns {void}
|
|
312
|
+
*/
|
|
313
|
+
start (elem) {
|
|
314
|
+
curtext = elem
|
|
315
|
+
svgCanvas.textActions.toEditMode()
|
|
316
|
+
},
|
|
317
|
+
/**
|
|
318
|
+
* @param {external:MouseEvent} evt
|
|
319
|
+
* @param {Element} mouseTarget
|
|
320
|
+
* @param {Float} startX
|
|
321
|
+
* @param {Float} startY
|
|
322
|
+
* @returns {void}
|
|
323
|
+
*/
|
|
324
|
+
mouseDown (evt, mouseTarget, startX, startY) {
|
|
325
|
+
const pt = screenToPt(startX, startY)
|
|
326
|
+
|
|
327
|
+
textinput.focus()
|
|
328
|
+
setCursorFromPoint(pt.x, pt.y)
|
|
329
|
+
lastX = startX
|
|
330
|
+
lastY = startY
|
|
331
|
+
|
|
332
|
+
// TODO: Find way to block native selection
|
|
333
|
+
},
|
|
334
|
+
/**
|
|
335
|
+
* @param {Float} mouseX
|
|
336
|
+
* @param {Float} mouseY
|
|
337
|
+
* @returns {void}
|
|
338
|
+
*/
|
|
339
|
+
mouseMove (mouseX, mouseY) {
|
|
340
|
+
const pt = screenToPt(mouseX, mouseY)
|
|
341
|
+
setEndSelectionFromPoint(pt.x, pt.y)
|
|
342
|
+
},
|
|
343
|
+
/**
|
|
344
|
+
* @param {external:MouseEvent} evt
|
|
345
|
+
* @param {Float} mouseX
|
|
346
|
+
* @param {Float} mouseY
|
|
347
|
+
* @returns {void}
|
|
348
|
+
*/
|
|
349
|
+
mouseUp (evt, mouseX, mouseY) {
|
|
350
|
+
const pt = screenToPt(mouseX, mouseY)
|
|
351
|
+
|
|
352
|
+
setEndSelectionFromPoint(pt.x, pt.y, true)
|
|
353
|
+
|
|
354
|
+
// TODO: Find a way to make this work: Use transformed BBox instead of evt.target
|
|
355
|
+
// if (lastX === mouseX && lastY === mouseY
|
|
356
|
+
// && !rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) {
|
|
357
|
+
// svgCanvas.textActions.toSelectMode(true);
|
|
358
|
+
// }
|
|
359
|
+
|
|
360
|
+
if (
|
|
361
|
+
evt.target !== curtext &&
|
|
362
|
+
mouseX < lastX + 2 &&
|
|
363
|
+
mouseX > lastX - 2 &&
|
|
364
|
+
mouseY < lastY + 2 &&
|
|
365
|
+
mouseY > lastY - 2
|
|
366
|
+
) {
|
|
367
|
+
svgCanvas.textActions.toSelectMode(true)
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
/**
|
|
371
|
+
* @function
|
|
372
|
+
* @param {Integer} index
|
|
373
|
+
* @returns {void}
|
|
374
|
+
*/
|
|
375
|
+
setCursor,
|
|
376
|
+
/**
|
|
377
|
+
* @param {Float} x
|
|
378
|
+
* @param {Float} y
|
|
379
|
+
* @returns {void}
|
|
380
|
+
*/
|
|
381
|
+
toEditMode (x, y) {
|
|
382
|
+
allowDbl = false
|
|
383
|
+
svgCanvas.setCurrentMode('textedit')
|
|
384
|
+
svgCanvas.selectorManager.requestSelector(curtext).showGrips(false)
|
|
385
|
+
// Make selector group accept clicks
|
|
386
|
+
/* const selector = */ svgCanvas.selectorManager.requestSelector(curtext) // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used
|
|
387
|
+
// const sel = selector.selectorRect;
|
|
388
|
+
|
|
389
|
+
svgCanvas.textActions.init()
|
|
390
|
+
|
|
391
|
+
curtext.style.cursor = 'text'
|
|
392
|
+
|
|
393
|
+
// if (supportsEditableText()) {
|
|
394
|
+
// curtext.setAttribute('editable', 'simple');
|
|
395
|
+
// return;
|
|
396
|
+
// }
|
|
397
|
+
|
|
398
|
+
if (!arguments.length) {
|
|
399
|
+
setCursor()
|
|
400
|
+
} else {
|
|
401
|
+
const pt = screenToPt(x, y)
|
|
402
|
+
setCursorFromPoint(pt.x, pt.y)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
setTimeout(function () {
|
|
406
|
+
allowDbl = true
|
|
407
|
+
}, 300)
|
|
408
|
+
},
|
|
409
|
+
/**
|
|
410
|
+
* @param {boolean|Element} selectElem
|
|
411
|
+
* @fires module:svgcanvas.SvgCanvas#event:selected
|
|
412
|
+
* @returns {void}
|
|
413
|
+
*/
|
|
414
|
+
toSelectMode (selectElem) {
|
|
415
|
+
svgCanvas.setCurrentMode('select')
|
|
416
|
+
clearInterval(blinker)
|
|
417
|
+
blinker = null
|
|
418
|
+
if (selblock) { selblock.setAttribute('display', 'none') }
|
|
419
|
+
if (cursor) { cursor.setAttribute('visibility', 'hidden') }
|
|
420
|
+
curtext.style.cursor = 'move'
|
|
421
|
+
|
|
422
|
+
if (selectElem) {
|
|
423
|
+
svgCanvas.clearSelection()
|
|
424
|
+
curtext.style.cursor = 'move'
|
|
425
|
+
|
|
426
|
+
svgCanvas.call('selected', [curtext])
|
|
427
|
+
svgCanvas.addToSelection([curtext], true)
|
|
428
|
+
}
|
|
429
|
+
if (!curtext?.textContent.length) {
|
|
430
|
+
// No content, so delete
|
|
431
|
+
svgCanvas.deleteSelectedElements()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
textinput.blur()
|
|
435
|
+
|
|
436
|
+
curtext = false
|
|
437
|
+
|
|
438
|
+
// if (supportsEditableText()) {
|
|
439
|
+
// curtext.removeAttribute('editable');
|
|
440
|
+
// }
|
|
441
|
+
},
|
|
442
|
+
/**
|
|
443
|
+
* @param {Element} elem
|
|
444
|
+
* @returns {void}
|
|
445
|
+
*/
|
|
446
|
+
setInputElem (elem) {
|
|
447
|
+
textinput = elem
|
|
448
|
+
},
|
|
449
|
+
/**
|
|
450
|
+
* @returns {void}
|
|
451
|
+
*/
|
|
452
|
+
clear () {
|
|
453
|
+
if (svgCanvas.getCurrentMode() === 'textedit') {
|
|
454
|
+
svgCanvas.textActions.toSelectMode()
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
/**
|
|
458
|
+
* @param {Element} _inputElem Not in use
|
|
459
|
+
* @returns {void}
|
|
460
|
+
*/
|
|
461
|
+
init (_inputElem) {
|
|
462
|
+
if (!curtext) { return }
|
|
463
|
+
let i; let end
|
|
464
|
+
// if (supportsEditableText()) {
|
|
465
|
+
// curtext.select();
|
|
466
|
+
// return;
|
|
467
|
+
// }
|
|
468
|
+
|
|
469
|
+
if (!curtext.parentNode) {
|
|
470
|
+
// Result of the ffClone, need to get correct element
|
|
471
|
+
const selectedElements = svgCanvas.getSelectedElements()
|
|
472
|
+
curtext = selectedElements[0]
|
|
473
|
+
svgCanvas.selectorManager.requestSelector(curtext).showGrips(false)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const str = curtext.textContent
|
|
477
|
+
const len = str.length
|
|
478
|
+
|
|
479
|
+
const xform = curtext.getAttribute('transform')
|
|
480
|
+
|
|
481
|
+
textbb = utilsGetBBox(curtext)
|
|
482
|
+
|
|
483
|
+
matrix = xform ? getMatrix(curtext) : null
|
|
484
|
+
|
|
485
|
+
chardata = []
|
|
486
|
+
chardata.length = len
|
|
487
|
+
textinput.focus()
|
|
488
|
+
|
|
489
|
+
curtext.removeEventListener('dblclick', selectWord)
|
|
490
|
+
curtext.addEventListener('dblclick', selectWord)
|
|
491
|
+
|
|
492
|
+
if (!len) {
|
|
493
|
+
end = { x: textbb.x + (textbb.width / 2), width: 0 }
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
for (i = 0; i < len; i++) {
|
|
497
|
+
const start = curtext.getStartPositionOfChar(i)
|
|
498
|
+
end = curtext.getEndPositionOfChar(i)
|
|
499
|
+
|
|
500
|
+
if (!supportsGoodTextCharPos()) {
|
|
501
|
+
const zoom = svgCanvas.getZoom()
|
|
502
|
+
const offset = svgCanvas.contentW * zoom
|
|
503
|
+
start.x -= offset
|
|
504
|
+
end.x -= offset
|
|
505
|
+
|
|
506
|
+
start.x /= zoom
|
|
507
|
+
end.x /= zoom
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Get a "bbox" equivalent for each character. Uses the
|
|
511
|
+
// bbox data of the actual text for y, height purposes
|
|
512
|
+
|
|
513
|
+
// TODO: Decide if y, width and height are actually necessary
|
|
514
|
+
chardata[i] = {
|
|
515
|
+
x: start.x,
|
|
516
|
+
y: textbb.y, // start.y?
|
|
517
|
+
width: end.x - start.x,
|
|
518
|
+
height: textbb.height
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Add a last bbox for cursor at end of text
|
|
523
|
+
chardata.push({
|
|
524
|
+
x: end.x,
|
|
525
|
+
width: 0
|
|
526
|
+
})
|
|
527
|
+
setSelection(textinput.selectionStart, textinput.selectionEnd, true)
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}())
|
package/touch.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param {Event} ev
|
|
5
|
+
* @returns {void}
|
|
6
|
+
*/
|
|
7
|
+
const touchHandler = (ev) => {
|
|
8
|
+
ev.preventDefault()
|
|
9
|
+
const { changedTouches } = ev
|
|
10
|
+
const first = changedTouches[0]
|
|
11
|
+
|
|
12
|
+
let type = ''
|
|
13
|
+
switch (ev.type) {
|
|
14
|
+
case 'touchstart': type = 'mousedown'; break
|
|
15
|
+
case 'touchmove': type = 'mousemove'; break
|
|
16
|
+
case 'touchend': type = 'mouseup'; break
|
|
17
|
+
default: return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { screenX, screenY, clientX, clientY } = first
|
|
21
|
+
const simulatedEvent = new MouseEvent(type, {
|
|
22
|
+
// Event interface
|
|
23
|
+
bubbles: true,
|
|
24
|
+
cancelable: true,
|
|
25
|
+
// UIEvent interface
|
|
26
|
+
view: window,
|
|
27
|
+
detail: 1, // click count
|
|
28
|
+
// MouseEvent interface (customized)
|
|
29
|
+
screenX,
|
|
30
|
+
screenY,
|
|
31
|
+
clientX,
|
|
32
|
+
clientY,
|
|
33
|
+
// MouseEvent interface (defaults) - these could be removed
|
|
34
|
+
ctrlKey: false,
|
|
35
|
+
altKey: false,
|
|
36
|
+
shiftKey: false,
|
|
37
|
+
metaKey: false,
|
|
38
|
+
button: 0, // main button (usually left)
|
|
39
|
+
relatedTarget: null
|
|
40
|
+
})
|
|
41
|
+
if (changedTouches.length < 2) {
|
|
42
|
+
first.target.dispatchEvent(simulatedEvent)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const init = (svgCanvas) => {
|
|
47
|
+
svgCanvas.svgroot.addEventListener('touchstart', touchHandler)
|
|
48
|
+
svgCanvas.svgroot.addEventListener('touchmove', touchHandler)
|
|
49
|
+
svgCanvas.svgroot.addEventListener('touchend', touchHandler)
|
|
50
|
+
svgCanvas.svgroot.addEventListener('touchcancel', touchHandler)
|
|
51
|
+
}
|