vendor: 7,591 bytes, lines 1-226
1/*! 2 * Signature Pad v4.1.7 | https://github.com/szimek/signature_pad 3 * (c) 2023 Szymon Nowak | Released under the MIT license 4 */ 5 6class Point { 7 constructor(x, y, pressure, time) { 8 if (isNaN(x) || isNaN(y)) { 9 throw new Error(`Point is invalid: (${x}, ${y})`); 10 } 11 this.x = +x; 12 this.y = +y; 13 this.pressure = pressure || 0; 14 this.time = time || Date.now(); 15 } 16 distanceTo(start) { 17 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2)); 18 } 19 equals(other) { 20 return (this.x === other.x && 21 this.y === other.y && 22 this.pressure === other.pressure && 23 this.time === other.time); 24 } 25 velocityFrom(start) { 26 return this.time !== start.time 27 ? this.distanceTo(start) / (this.time - start.time) 28 : 0; 29 } 30} 31 32class Bezier { 33 static fromPoints(points, widths) { 34 const c2 = this.calculateControlPoints(points[0], points[1], points[2]).c2; 35 const c3 = this.calculateControlPoints(points[1], points[2], points[3]).c1; 36 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end); 37 } 38 static calculateControlPoints(s1, s2, s3) { 39 const dx1 = s1.x - s2.x; 40 const dy1 = s1.y - s2.y; 41 const dx2 = s2.x - s3.x; 42 const dy2 = s2.y - s3.y; 43 const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 }; 44 const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 }; 45 const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); 46 const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); 47 const dxm = m1.x - m2.x; 48 const dym = m1.y - m2.y; 49 const k = l2 / (l1 + l2); 50 const cm = { x: m2.x + dxm * k, y: m2.y + dym * k }; 51 const tx = s2.x - cm.x; 52 const ty = s2.y - cm.y; 53 return { 54 c1: new Point(m1.x + tx, m1.y + ty), 55 c2: new Point(m2.x + tx, m2.y + ty), 56 }; 57 } 58 constructor(startPoint, control2, control1, endPoint, startWidth, endWidth) { 59 this.startPoint = startPoint; 60 this.control2 = control2; 61 this.control1 = control1; 62 this.endPoint = endPoint; 63 this.startWidth = startWidth; 64 this.endWidth = endWidth; 65 } 66 length() { 67 const steps = 10; 68 let length = 0; 69 let px; 70 let py; 71 for (let i = 0; i <= steps; i += 1) { 72 const t = i / steps; 73 const cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x); 74 const cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y); 75 if (i > 0) { 76 const xdiff = cx - px; 77 const ydiff = cy - py; 78 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff); 79 } 80 px = cx; 81 py = cy; 82 } 83 return length; 84 } 85 point(t, start, c1, c2, end) { 86 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t)) 87 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t) 88 + (3.0 * c2 * (1.0 - t) * t * t) 89 + (end * t * t * t); 90 } 91} 92 93class SignatureEventTarget { 94 constructor() { 95 try { 96 this._et = new EventTarget(); 97 } 98 catch (error) { 99 this._et = document; 100 } 101 } 102 addEventListener(type, listener, options) { 103 this._et.addEventListener(type, listener, options); 104 } 105 dispatchEvent(event) { 106 return this._et.dispatchEvent(event); 107 } 108 removeEventListener(type, callback, options) { 109 this._et.removeEventListener(type, callback, options); 110 } 111} 112 113function throttle(fn, wait = 250) { 114 let previous = 0; 115 let timeout = null; 116 let result; 117 let storedContext; 118 let storedArgs; 119 const later = () => { 120 previous = Date.now(); 121 timeout = null; 122 result = fn.apply(storedContext, storedArgs); 123 if (!timeout) { 124 storedContext = null; 125 storedArgs = []; 126 } 127 }; 128 return function wrapper(...args) { 129 const now = Date.now(); 130 const remaining = wait - (now - previous); 131 storedContext = this; 132 storedArgs = args; 133 if (remaining <= 0 || remaining > wait) { 134 if (timeout) { 135 clearTimeout(timeout); 136 timeout = null; 137 } 138 previous = now; 139 result = fn.apply(storedContext, storedArgs); 140 if (!timeout) { 141 storedContext = null; 142 storedArgs = []; 143 } 144 } 145 else if (!timeout) { 146 timeout = window.setTimeout(later, remaining); 147 } 148 return result; 149 }; 150} 151 152class SignaturePad extends SignatureEventTarget { 153 constructor(canvas, options = {}) { 154 super(); 155 this.canvas = canvas; 156 this._drawingStroke = false; 157 this._isEmpty = true; 158 this._lastPoints = []; 159 this._data = []; 160 this._lastVelocity = 0; 161 this._lastWidth = 0; 162 this._handleMouseDown = (event) => { 163 if (event.buttons === 1) { 164 this._strokeBegin(event); 165 } 166 }; 167 this._handleMouseMove = (event) => { 168 this._strokeMoveUpdate(event); 169 }; 170 this._handleMouseUp = (event) => { 171 if (event.buttons === 1) { 172 this._strokeEnd(event); 173 } 174 }; 175 this._handleTouchStart = (event) => { 176 if (event.cancelable) { 177 event.preventDefault(); 178 } 179 if (event.targetTouches.length === 1) { 180 const touch = event.changedTouches[0]; 181 this._strokeBegin(touch); 182 } 183 }; 184 this._handleTouchMove = (event) => { 185 if (event.cancelable) { 186 event.preventDefault(); 187 } 188 const touch = event.targetTouches[0]; 189 this._strokeMoveUpdate(touch); 190 }; 191 this._handleTouchEnd = (event) => { 192 const wasCanvasTouched = event.target === this.canvas; 193 if (wasCanvasTouched) { 194 if (event.cancelable) { 195 event.preventDefault(); 196 } 197 const touch = event.changedTouches[0]; 198 this._strokeEnd(touch); 199 } 200 }; 201 this._handlePointerStart = (event) => { 202 event.preventDefault(); 203 this._strokeBegin(event); 204 }; 205 this._handlePointerMove = (event) => { 206 this._strokeMoveUpdate(event); 207 }; 208 this._handlePointerEnd = (event) => { 209 if (this._drawingStroke) { 210 event.preventDefault(); 211 this._strokeEnd(event); 212 } 213 }; 214 this.velocityFilterWeight = options.velocityFilterWeight || 0.7; 215 this.minWidth = options.minWidth || 0.5; 216 this.maxWidth = options.maxWidth || 2.5; 217 this.throttle = ('throttle' in options ? options.throttle : 16); 218 this.minDistance = ('minDistance' in options ? options.minDistance : 5); 219 this.dotSize = options.dotSize || 0; 220 this.penColor = options.penColor || 'black'; 221 this.backgroundColor = options.backgroundColor || 'rgba(0,0,0,0)'; 222 this.compositeOperation = options.compositeOperation || 'source-over'; 223 this._strokeMoveUpdate = this.throttle 224 ? throttle(SignaturePad.prototype._strokeUpdate, this.throttle) 225 : SignaturePad.prototype._strokeUpdate; 226 this._ctx = canvas.getContext('2d'
vendor: 14,233 bytes, lines 226-564
226); 227 this.clear(); 228 this.on(); 229 } 230 clear() { 231 const { _ctx: ctx, canvas } = this; 232 ctx.fillStyle = this.backgroundColor; 233 ctx.clearRect(0, 0, canvas.width, canvas.height); 234 ctx.fillRect(0, 0, canvas.width, canvas.height); 235 this._data = []; 236 this._reset(this._getPointGroupOptions()); 237 this._isEmpty = true; 238 } 239 fromDataURL(dataUrl, options = {}) { 240 return new Promise((resolve, reject) => { 241 const image = new Image(); 242 const ratio = options.ratio || window.devicePixelRatio || 1; 243 const width = options.width || this.canvas.width / ratio; 244 const height = options.height || this.canvas.height / ratio; 245 const xOffset = options.xOffset || 0; 246 const yOffset = options.yOffset || 0; 247 this._reset(this._getPointGroupOptions()); 248 image.onload = () => { 249 this._ctx.drawImage(image, xOffset, yOffset, width, height); 250 resolve(); 251 }; 252 image.onerror = (error) => { 253 reject(error); 254 }; 255 image.crossOrigin = 'anonymous'; 256 image.src = dataUrl; 257 this._isEmpty = false; 258 }); 259 } 260 toDataURL(type = 'image/png', encoderOptions) { 261 switch (type) { 262 case 'image/svg+xml': 263 if (typeof encoderOptions !== 'object') { 264 encoderOptions = undefined; 265 } 266 return `data:image/svg+xml;base64,${btoa(this.toSVG(encoderOptions))}`; 267 default: 268 if (typeof encoderOptions !== 'number') { 269 encoderOptions = undefined; 270 } 271 return this.canvas.toDataURL(type, encoderOptions); 272 } 273 } 274 on() { 275 this.canvas.style.touchAction = 'none'; 276 this.canvas.style.msTouchAction = 'none'; 277 this.canvas.style.userSelect = 'none'; 278 const isIOS = /Macintosh/.test(navigator.userAgent) && 'ontouchstart' in document; 279 if (window.PointerEvent && !isIOS) { 280 this._handlePointerEvents(); 281 } 282 else { 283 this._handleMouseEvents(); 284 if ('ontouchstart' in window) { 285 this._handleTouchEvents(); 286 } 287 } 288 } 289 off() { 290 this.canvas.style.touchAction = 'auto'; 291 this.canvas.style.msTouchAction = 'auto'; 292 this.canvas.style.userSelect = 'auto'; 293 this.canvas.removeEventListener('pointerdown', this._handlePointerStart); 294 this.canvas.removeEventListener('pointermove', this._handlePointerMove); 295 this.canvas.ownerDocument.removeEventListener('pointerup', this._handlePointerEnd); 296 this.canvas.removeEventListener('mousedown', this._handleMouseDown); 297 this.canvas.removeEventListener('mousemove', this._handleMouseMove); 298 this.canvas.ownerDocument.removeEventListener('mouseup', this._handleMouseUp); 299 this.canvas.removeEventListener('touchstart', this._handleTouchStart); 300 this.canvas.removeEventListener('touchmove', this._handleTouchMove); 301 this.canvas.removeEventListener('touchend', this._handleTouchEnd); 302 } 303 isEmpty() { 304 return this._isEmpty; 305 } 306 fromData(pointGroups, { clear = true } = {}) { 307 if (clear) { 308 this.clear(); 309 } 310 this._fromData(pointGroups, this._drawCurve.bind(this), this._drawDot.bind(this)); 311 this._data = this._data.concat(pointGroups); 312 } 313 toData() { 314 return this._data; 315 } 316 _getPointGroupOptions(group) { 317 return { 318 penColor: group && 'penColor' in group ? group.penColor : this.penColor, 319 dotSize: group && 'dotSize' in group ? group.dotSize : this.dotSize, 320 minWidth: group && 'minWidth' in group ? group.minWidth : this.minWidth, 321 maxWidth: group && 'maxWidth' in group ? group.maxWidth : this.maxWidth, 322 velocityFilterWeight: group && 'velocityFilterWeight' in group 323 ? group.velocityFilterWeight 324 : this.velocityFilterWeight, 325 compositeOperation: group && 'compositeOperation' in group 326 ? group.compositeOperation 327 : this.compositeOperation, 328 }; 329 } 330 _strokeBegin(event) { 331 const cancelled = !this.dispatchEvent(new CustomEvent('beginStroke', { detail: event, cancelable: true })); 332 if (cancelled) { 333 return; 334 } 335 this._drawingStroke = true; 336 const pointGroupOptions = this._getPointGroupOptions(); 337 const newPointGroup = Object.assign(Object.assign({}, pointGroupOptions), { points: [] }); 338 this._data.push(newPointGroup); 339 this._reset(pointGroupOptions); 340 this._strokeUpdate(event); 341 } 342 _strokeUpdate(event) { 343 if (!this._drawingStroke) { 344 return; 345 } 346 if (this._data.length === 0) { 347 this._strokeBegin(event); 348 return; 349 } 350 this.dispatchEvent(new CustomEvent('beforeUpdateStroke', { detail: event })); 351 const x = event.clientX; 352 const y = event.clientY; 353 const pressure = event.pressure !== undefined 354 ? event.pressure 355 : event.force !== undefined 356 ? event.force 357 : 0; 358 const point = this._createPoint(x, y, pressure); 359 const lastPointGroup = this._data[this._data.length - 1]; 360 const lastPoints = lastPointGroup.points; 361 const lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1]; 362 const isLastPointTooClose = lastPoint 363 ? point.distanceTo(lastPoint) <= this.minDistance 364 : false; 365 const pointGroupOptions = this._getPointGroupOptions(lastPointGroup); 366 if (!lastPoint || !(lastPoint && isLastPointTooClose)) { 367 const curve = this._addPoint(point, pointGroupOptions); 368 if (!lastPoint) { 369 this._drawDot(point, pointGroupOptions); 370 } 371 else if (curve) { 372 this._drawCurve(curve, pointGroupOptions); 373 } 374 lastPoints.push({ 375 time: point.time, 376 x: point.x, 377 y: point.y, 378 pressure: point.pressure, 379 }); 380 } 381 this.dispatchEvent(new CustomEvent('afterUpdateStroke', { detail: event })); 382 } 383 _strokeEnd(event) { 384 if (!this._drawingStroke) { 385 return; 386 } 387 this._strokeUpdate(event); 388 this._drawingStroke = false; 389 this.dispatchEvent(new CustomEvent('endStroke', { detail: event })); 390 } 391 _handlePointerEvents() { 392 this._drawingStroke = false; 393 this.canvas.addEventListener('pointerdown', this._handlePointerStart); 394 this.canvas.addEventListener('pointermove', this._handlePointerMove); 395 this.canvas.ownerDocument.addEventListener('pointerup', this._handlePointerEnd); 396 } 397 _handleMouseEvents() { 398 this._drawingStroke = false; 399 this.canvas.addEventListener('mousedown', this._handleMouseDown); 400 this.canvas.addEventListener('mousemove', this._handleMouseMove); 401 this.canvas.ownerDocument.addEventListener('mouseup', this._handleMouseUp); 402 } 403 _handleTouchEvents() { 404 this.canvas.addEventListener('touchstart', this._handleTouchStart); 405 this.canvas.addEventListener('touchmove', this._handleTouchMove); 406 this.canvas.addEventListener('touchend', this._handleTouchEnd); 407 } 408 _reset(options) { 409 this._lastPoints = []; 410 this._lastVelocity = 0; 411 this._lastWidth = (options.minWidth + options.maxWidth) / 2; 412 this._ctx.fillStyle = options.penColor; 413 this._ctx.globalCompositeOperation = options.compositeOperation; 414 } 415 _createPoint(x, y, pressure) { 416 const rect = this.canvas.getBoundingClientRect(); 417 return new Point(x - rect.left, y - rect.top, pressure, new Date().getTime()); 418 } 419 _addPoint(point, options) { 420 const { _lastPoints } = this; 421 _lastPoints.push(point); 422 if (_lastPoints.length > 2) { 423 if (_lastPoints.length === 3) { 424 _lastPoints.unshift(_lastPoints[0]); 425 } 426 const widths = this._calculateCurveWidths(_lastPoints[1], _lastPoints[2], options); 427 const curve = Bezier.fromPoints(_lastPoints, widths); 428 _lastPoints.shift(); 429 return curve; 430 } 431 return null; 432 } 433 _calculateCurveWidths(startPoint, endPoint, options) { 434 const velocity = options.velocityFilterWeight * endPoint.velocityFrom(startPoint) + 435 (1 - options.velocityFilterWeight) * this._lastVelocity; 436 const newWidth = this._strokeWidth(velocity, options); 437 const widths = { 438 end: newWidth, 439 start: this._lastWidth, 440 }; 441 this._lastVelocity = velocity; 442 this._lastWidth = newWidth; 443 return widths; 444 } 445 _strokeWidth(velocity, options) { 446 return Math.max(options.maxWidth / (velocity + 1), options.minWidth); 447 } 448 _drawCurveSegment(x, y, width) { 449 const ctx = this._ctx; 450 ctx.moveTo(x, y); 451 ctx.arc(x, y, width, 0, 2 * Math.PI, false); 452 this._isEmpty = false; 453 } 454 _drawCurve(curve, options) { 455 const ctx = this._ctx; 456 const widthDelta = curve.endWidth - curve.startWidth; 457 const drawSteps = Math.ceil(curve.length()) * 2; 458 ctx.beginPath(); 459 ctx.fillStyle = options.penColor; 460 for (let i = 0; i < drawSteps; i += 1) { 461 const t = i / drawSteps; 462 const tt = t * t; 463 const ttt = tt * t; 464 const u = 1 - t; 465 const uu = u * u; 466 const uuu = uu * u; 467 let x = uuu * curve.startPoint.x; 468 x += 3 * uu * t * curve.control1.x; 469 x += 3 * u * tt * curve.control2.x; 470 x += ttt * curve.endPoint.x; 471 let y = uuu * curve.startPoint.y; 472 y += 3 * uu * t * curve.control1.y; 473 y += 3 * u * tt * curve.control2.y; 474 y += ttt * curve.endPoint.y; 475 const width = Math.min(curve.startWidth + ttt * widthDelta, options.maxWidth); 476 this._drawCurveSegment(x, y, width); 477 } 478 ctx.closePath(); 479 ctx.fill(); 480 } 481 _drawDot(point, options) { 482 const ctx = this._ctx; 483 const width = options.dotSize > 0 484 ? options.dotSize 485 : (options.minWidth + options.maxWidth) / 2; 486 ctx.beginPath(); 487 this._drawCurveSegment(point.x, point.y, width); 488 ctx.closePath(); 489 ctx.fillStyle = options.penColor; 490 ctx.fill(); 491 } 492 _fromData(pointGroups, drawCurve, drawDot) { 493 for (const group of pointGroups) { 494 const { points } = group; 495 const pointGroupOptions = this._getPointGroupOptions(group); 496 if (points.length > 1) { 497 for (let j = 0; j < points.length; j += 1) { 498 const basicPoint = points[j]; 499 const point = new Point(basicPoint.x, basicPoint.y, basicPoint.pressure, basicPoint.time); 500 if (j === 0) { 501 this._reset(pointGroupOptions); 502 } 503 const curve = this._addPoint(point, pointGroupOptions); 504 if (curve) { 505 drawCurve(curve, pointGroupOptions); 506 } 507 } 508 } 509 else { 510 this._reset(pointGroupOptions); 511 drawDot(points[0], pointGroupOptions); 512 } 513 } 514 } 515 toSVG({ includeBackgroundColor = false } = {}) { 516 const pointGroups = this._data; 517 const ratio = Math.max(window.devicePixelRatio || 1, 1); 518 const minX = 0; 519 const minY = 0; 520 const maxX = this.canvas.width / ratio; 521 const maxY = this.canvas.height / ratio; 522 const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 523 svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 524 svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); 525 svg.setAttribute('viewBox', `${minX} ${minY} ${maxX} ${maxY}`); 526 svg.setAttribute('width', maxX.toString()); 527 svg.setAttribute('height', maxY.toString()); 528 if (includeBackgroundColor && this.backgroundColor) { 529 const rect = document.createElement('rect'); 530 rect.setAttribute('width', '100%'); 531 rect.setAttribute('height', '100%'); 532 rect.setAttribute('fill', this.backgroundColor); 533 svg.appendChild(rect); 534 } 535 this._fromData(pointGroups, (curve, { penColor }) => { 536 const path = document.createElement('path'); 537 if (!isNaN(curve.control1.x) && 538 !isNaN(curve.control1.y) && 539 !isNaN(curve.control2.x) && 540 !isNaN(curve.control2.y)) { 541 const attr = `M ${curve.startPoint.x.toFixed(3)},${curve.startPoint.y.toFixed(3)} ` + 542 `C ${curve.control1.x.toFixed(3)},${curve.control1.y.toFixed(3)} ` + 543 `${curve.control2.x.toFixed(3)},${curve.control2.y.toFixed(3)} ` + 544 `${curve.endPoint.x.toFixed(3)},${curve.endPoint.y.toFixed(3)}`; 545 path.setAttribute('d', attr); 546 path.setAttribute('stroke-width', (curve.endWidth * 2.25).toFixed(3)); 547 path.setAttribute('stroke', penColor); 548 path.setAttribute('fill', 'none'); 549 path.setAttribute('stroke-linecap', 'round'); 550 svg.appendChild(path); 551 } 552 }, (point, { penColor, dotSize, minWidth, maxWidth }) => { 553 const circle = document.createElement('circle'); 554 const size = dotSize > 0 ? dotSize : (minWidth + maxWidth) / 2; 555 circle.setAttribute('r', size.toString()); 556 circle.setAttribute('cx', point.x.toString()); 557 circle.setAttribute('cy', point.y.toString()); 558 circle.setAttribute('fill', penColor); 559 svg.appendChild(circle); 560 }); 561 return svg.outerHTML; 562 } 563} 564
565//# sourceMappingURL=signature_pad.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.