@nasser-sw/fabric 7.0.1-beta16 → 7.0.1-beta17
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/.claude/settings.local.json +7 -0
- package/dist/index.js +1982 -649
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +1982 -649
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +1982 -649
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +1982 -649
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/shapes/IText/IText.d.ts +31 -6
- package/dist/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist/src/shapes/IText/IText.min.mjs +1 -1
- package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.mjs +495 -126
- package/dist/src/shapes/IText/IText.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextBehavior.d.ts +12 -0
- package/dist/src/shapes/IText/ITextBehavior.d.ts.map +1 -1
- package/dist/src/shapes/IText/ITextBehavior.min.mjs +1 -1
- package/dist/src/shapes/IText/ITextBehavior.min.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextBehavior.mjs +127 -36
- package/dist/src/shapes/IText/ITextBehavior.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.d.ts.map +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.min.mjs +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.min.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.mjs +21 -4
- package/dist/src/shapes/IText/ITextClickBehavior.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextKeyBehavior.min.mjs +1 -1
- package/dist/src/shapes/IText/ITextKeyBehavior.min.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextKeyBehavior.mjs +17 -21
- package/dist/src/shapes/IText/ITextKeyBehavior.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.d.ts +69 -1
- package/dist/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist/src/shapes/Text/Text.min.mjs +1 -1
- package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.mjs +374 -60
- package/dist/src/shapes/Text/Text.mjs.map +1 -1
- package/dist/src/shapes/Text/constants.d.ts.map +1 -1
- package/dist/src/shapes/Text/constants.min.mjs +1 -1
- package/dist/src/shapes/Text/constants.min.mjs.map +1 -1
- package/dist/src/shapes/Text/constants.mjs +2 -1
- package/dist/src/shapes/Text/constants.mjs.map +1 -1
- package/dist/src/shapes/Textbox.d.ts +8 -1
- package/dist/src/shapes/Textbox.d.ts.map +1 -1
- package/dist/src/shapes/Textbox.min.mjs +1 -1
- package/dist/src/shapes/Textbox.min.mjs.map +1 -1
- package/dist/src/shapes/Textbox.mjs +406 -63
- package/dist/src/shapes/Textbox.mjs.map +1 -1
- package/dist/src/text/hitTest.min.mjs +1 -1
- package/dist/src/text/hitTest.min.mjs.map +1 -1
- package/dist/src/text/hitTest.mjs +1 -198
- package/dist/src/text/hitTest.mjs.map +1 -1
- package/dist/src/text/layout.min.mjs +1 -1
- package/dist/src/text/layout.min.mjs.map +1 -1
- package/dist/src/text/layout.mjs +122 -5
- package/dist/src/text/layout.mjs.map +1 -1
- package/dist/src/text/overlayEditor.min.mjs +1 -1
- package/dist/src/text/overlayEditor.min.mjs.map +1 -1
- package/dist/src/text/overlayEditor.mjs +132 -142
- package/dist/src/text/overlayEditor.mjs.map +1 -1
- package/dist/src/text/unicode.d.ts +28 -0
- package/dist/src/text/unicode.d.ts.map +1 -1
- package/dist/src/text/unicode.min.mjs +1 -1
- package/dist/src/text/unicode.min.mjs.map +1 -1
- package/dist/src/text/unicode.mjs +294 -1
- package/dist/src/text/unicode.mjs.map +1 -1
- package/dist-extensions/src/shapes/IText/IText.d.ts +31 -6
- package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist-extensions/src/shapes/IText/ITextBehavior.d.ts +12 -0
- package/dist-extensions/src/shapes/IText/ITextBehavior.d.ts.map +1 -1
- package/dist-extensions/src/shapes/IText/ITextClickBehavior.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/Text.d.ts +69 -1
- package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/constants.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Textbox.d.ts +8 -1
- package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
- package/dist-extensions/src/text/unicode.d.ts +28 -0
- package/dist-extensions/src/text/unicode.d.ts.map +1 -1
- package/package.json +164 -164
- package/rtl-debug.html +358 -200
- package/src/shapes/IText/IText.ts +524 -110
- package/src/shapes/IText/ITextBehavior.ts +174 -80
- package/src/shapes/IText/ITextClickBehavior.ts +20 -6
- package/src/shapes/IText/ITextKeyBehavior.ts +15 -15
- package/src/shapes/Text/Text.ts +488 -107
- package/src/shapes/Text/constants.ts +4 -2
- package/src/shapes/Textbox.ts +414 -65
- package/src/text/layout.ts +150 -23
- package/src/text/overlayEditor.ts +148 -148
- package/src/text/unicode.ts +177 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var a="7.0.1-
|
|
1
|
+
var a="7.0.1-beta16";export{a as version};
|
|
2
2
|
//# sourceMappingURL=package.json.min.mjs.map
|
package/dist/package.json.mjs
CHANGED
|
@@ -82,6 +82,12 @@ export declare class IText<Props extends TOptions<ITextProps> = Partial<ITextPro
|
|
|
82
82
|
* @type Number
|
|
83
83
|
*/
|
|
84
84
|
selectionEnd: number;
|
|
85
|
+
/**
|
|
86
|
+
* Cache for visual positions per line to ensure consistency
|
|
87
|
+
* during selection operations
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
private _visualPositionsCache;
|
|
85
91
|
compositionStart: number;
|
|
86
92
|
compositionEnd: number;
|
|
87
93
|
/**
|
|
@@ -252,19 +258,37 @@ export declare class IText<Props extends TOptions<ITextProps> = Partial<ITextPro
|
|
|
252
258
|
*/
|
|
253
259
|
_getCursorBoundariesAdvanced(index: number): CursorBoundaries;
|
|
254
260
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
261
|
+
* Override selection to use measureText-based visual positions
|
|
262
|
+
* This ensures hit testing matches actual browser BiDi rendering
|
|
257
263
|
*/
|
|
258
264
|
getSelectionStartFromPointer(e: TPointerEvent): number;
|
|
265
|
+
/**
|
|
266
|
+
* Clear the visual positions cache
|
|
267
|
+
* Should be called when text content or dimensions change
|
|
268
|
+
*/
|
|
269
|
+
_clearVisualPositionsCache(): void;
|
|
270
|
+
/**
|
|
271
|
+
* Measure visual character positions for hit testing using BiDi analysis
|
|
272
|
+
* This properly handles mixed RTL/LTR text by analyzing BiDi runs
|
|
273
|
+
* Results are cached per line for consistency during selection operations
|
|
274
|
+
*/
|
|
275
|
+
_measureVisualPositions(lineIndex: number, lineText: string): Array<{
|
|
276
|
+
logicalIndex: number;
|
|
277
|
+
visualX: number;
|
|
278
|
+
width: number;
|
|
279
|
+
isRtl: boolean;
|
|
280
|
+
}>;
|
|
259
281
|
/**
|
|
260
282
|
* Original cursor boundaries implementation
|
|
261
283
|
* @private
|
|
262
284
|
*/
|
|
263
285
|
_getCursorBoundariesOriginal(index: number, skipCaching?: boolean): CursorBoundaries;
|
|
264
286
|
/**
|
|
265
|
-
* Calculates cursor left/top offset relative to
|
|
287
|
+
* Calculates cursor left/top offset relative to _getLeftOffset()
|
|
288
|
+
* Uses visual positions for BiDi text support
|
|
289
|
+
* Handles kashida by converting original indices to display indices
|
|
266
290
|
* @private
|
|
267
|
-
* @param {number} index index from start
|
|
291
|
+
* @param {number} index index from start (in original text space, without tatweels)
|
|
268
292
|
*/
|
|
269
293
|
__getCursorBoundariesOffsets(index: number): {
|
|
270
294
|
top: number;
|
|
@@ -306,9 +330,10 @@ export declare class IText<Props extends TOptions<ITextProps> = Partial<ITextPro
|
|
|
306
330
|
renderDragSourceEffect(): void;
|
|
307
331
|
renderDropTargetEffect(e: DragEvent): void;
|
|
308
332
|
/**
|
|
309
|
-
* Renders text selection
|
|
333
|
+
* Renders text selection using visual positions for BiDi support
|
|
334
|
+
* Handles kashida by converting original indices to display indices
|
|
310
335
|
* @private
|
|
311
|
-
* @param {{ selectionStart: number, selectionEnd: number }} selection
|
|
336
|
+
* @param {{ selectionStart: number, selectionEnd: number }} selection (in original text space)
|
|
312
337
|
* @param {Object} boundaries Object with left/top/leftOffset/topOffset
|
|
313
338
|
* @param {CanvasRenderingContext2D} ctx transformed context to draw on
|
|
314
339
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IText.d.ts","sourceRoot":"","sources":["../../../../src/shapes/IText/IText.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQnE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"IText.d.ts","sourceRoot":"","sources":["../../../../src/shapes/IText/IText.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{defineProperty as t}from"../../../_virtual/_rollupPluginBabelHelpers.min.mjs";import{Canvas as e}from"../../canvas/Canvas.min.mjs";import{ITextClickBehavior as i}from"./ITextClickBehavior.min.mjs";import{getCursorRect as s,hitTest as r}from"../../text/hitTest.min.mjs";import{ctrlKeysMapUp as n,ctrlKeysMapDown as o,keysMapRtl as a,keysMap as h}from"./constants.min.mjs";import{classRegistry as l}from"../../ClassRegistry.min.mjs";import{JUSTIFY as c,JUSTIFY_RIGHT as d,JUSTIFY_LEFT as f,JUSTIFY_CENTER as g}from"../Text/constants.min.mjs";import{RIGHT as u,LEFT as p,CENTER as C,FILL as m}from"../../constants.min.mjs";import{createCanvasElementFor as x}from"../../util/misc/dom.min.mjs";import{applyCanvasTransform as _}from"../../util/internals/applyCanvasTransform.min.mjs";import{Point as S}from"../../Point.min.mjs";import{invertTransform as v}from"../../util/misc/matrix.min.mjs";const O={selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"",cursorDelay:1e3,cursorDuration:600,caching:!0,hiddenTextareaContainer:null,keysMap:h,keysMapRtl:a,ctrlKeysMapDown:o,ctrlKeysMapUp:n,_selectionDirection:null,_reSpace:/\s|\r?\n/,inCompositionMode:!1};class y extends i{static getDefaults(){return{...super.getDefaults(),...y.ownDefaults}}get type(){const t=super.type;return"itext"===t?"i-text":t}constructor(t,e){super(t,{...y.ownDefaults,...e}),this.initBehavior()}_set(t,i){return this.isEditing&&this._savedProps&&t in this._savedProps?(this._savedProps[t]=i,this):("canvas"===t&&(this.canvas instanceof e&&this.canvas.textEditingManager.remove(this),i instanceof e&&i.textEditingManager.add(this)),super._set(t,i))}setSelectionStart(t){t=Math.max(t,0),this._updateAndFire("selectionStart",t)}setSelectionEnd(t){t=Math.min(t,this.text.length),this._updateAndFire("selectionEnd",t)}_updateAndFire(t,e){this[t]!==e&&(this._fireSelectionChanged(),this[t]=e),this._updateTextarea()}_fireSelectionChanged(){this.fire("selection:changed"),this.canvas&&this.canvas.fire("text:selection:changed",{target:this})}initDimensions(){this.isEditing&&this.initDelayedCursor(),super.initDimensions()}getSelectionStyles(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart||0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selectionEnd,i=arguments.length>2?arguments[2]:void 0;return super.getSelectionStyles(t,e,i)}setSelectionStyles(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selectionStart||0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.selectionEnd;return super.setSelectionStyles(t,e,i)}get2DCursorLocation(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1?arguments[1]:void 0;return super.get2DCursorLocation(t,e)}render(t){super.render(t),this.cursorOffsetCache={},this.renderCursorOrSelection()}toCanvasElement(t){const e=this.isEditing;this.isEditing=!1;const i=super.toCanvasElement(t);return this.isEditing=e,i}renderCursorOrSelection(){if(!this.isEditing||!this.canvas)return;const t=this.clearContextTop(!0);if(!t)return;const e=this._getCursorBoundaries(),i=this.findAncestorsWithClipPath(),s=i.length>0;let r,n=t;if(s){r=x(t.canvas),n=r.getContext("2d"),_(n,this.canvas);const e=this.calcTransformMatrix();n.transform(e[0],e[1],e[2],e[3],e[4],e[5])}if(this.selectionStart!==this.selectionEnd||this.inCompositionMode?this.renderSelection(n,e):this.renderCursor(n,e),s)for(const e of i){const i=e.clipPath,s=x(t.canvas),r=s.getContext("2d");if(_(r,this.canvas),!i.absolutePositioned){const t=e.calcTransformMatrix();r.transform(t[0],t[1],t[2],t[3],t[4],t[5])}i.transform(r),i.drawObject(r,!0,{}),this.drawClipPathOnCache(n,i,s)}s&&(t.setTransform(1,0,0,1,0,0),t.drawImage(r,0,0)),this.canvas.contextTopDirty=!0,t.restore()}findAncestorsWithClipPath(){const t=[];let e=this;for(;e;)e.clipPath&&t.push(e),e=e.parent;return t}_getCursorBoundaries(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1?arguments[1]:void 0;return this.enableAdvancedLayout?this._getCursorBoundariesAdvanced(t):this._getCursorBoundariesOriginal(t,e)}_getCursorBoundariesOffsets(t,e){return e?this.__getCursorBoundariesOffsets(t):this.cursorOffsetCache&&"top"in this.cursorOffsetCache?this.cursorOffsetCache:this.cursorOffsetCache=this.__getCursorBoundariesOffsets(t)}_getCursorBoundariesAdvanced(t){if(!this.enableAdvancedLayout||!this._layoutTextAdvanced)return this._getCursorBoundariesOriginal(t);const e=this._layoutTextAdvanced(),i=s(t,e,this._getAdvancedLayoutOptions());return{left:this._getLeftOffset(),top:this._getTopOffset(),leftOffset:i.x,topOffset:i.y}}getSelectionStartFromPointer(t){if(!this.enableAdvancedLayout||!this._layoutTextAdvanced)return super.getSelectionStartFromPointer(t);const e=this.canvas.getScenePoint(t).transform(v(this.calcTransformMatrix())).add(new S(-this._getLeftOffset(),-this._getTopOffset())),i=this._layoutTextAdvanced(),s=r(e.x,e.y,i,this._getAdvancedLayoutOptions());return Math.min(s.charIndex,this._text.length)}_getCursorBoundariesOriginal(t,e){const i=this._getLeftOffset(),s=this._getTopOffset(),r=this._getCursorBoundariesOffsets(t,e);return{left:i,top:s,leftOffset:r.left,topOffset:r.top}}__getCursorBoundariesOffsets(t){let e=0,i=0;const{charIndex:s,lineIndex:r}=this.get2DCursorLocation(t);for(let t=0;t<r;t++)e+=this.getHeightOfLine(t);const n=this._getLineLeftOffset(r),o=this.__charBounds[r][s];o&&(i=o.left),0!==this.charSpacing&&s===this._textLines[r].length&&(i-=this._getWidthOfCharSpacing());const a={top:e,left:n+(i>0?i:0)};return"rtl"===this.direction&&(this.textAlign===u||this.textAlign===c||this.textAlign===d?a.left*=-1:this.textAlign===p||this.textAlign===f?a.left=n-(i>0?i:0):this.textAlign!==C&&this.textAlign!==g||(a.left=n-(i>0?i:0))),a}renderCursorAt(t){this._renderCursor(this.canvas.contextTop,this._getCursorBoundaries(t,!0),t)}renderCursor(t,e){this._renderCursor(t,e,this.selectionStart)}getCursorRenderingData(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getCursorBoundaries(t);const i=this.get2DCursorLocation(t),s=i.lineIndex,r=i.charIndex>0?i.charIndex-1:0,n=this.getValueOfPropertyAt(s,r,"fontSize"),o=this.getObjectScaling().x*this.canvas.getZoom(),a=this.cursorWidth/o,h=this.getValueOfPropertyAt(s,r,"deltaY"),l=e.topOffset+(1-this._fontSizeFraction)*this.getHeightOfLine(s)/this.lineHeight-n*(1-this._fontSizeFraction);return{color:this.cursorColor||this.getValueOfPropertyAt(s,r,"fill"),opacity:this._currentCursorOpacity,left:e.left+e.leftOffset-a/2,top:l+e.top+h,width:a,height:n}}_renderCursor(t,e,i){const{color:s,opacity:r,left:n,top:o,width:a,height:h}=this.getCursorRenderingData(i,e);t.fillStyle=s,t.globalAlpha=r,t.fillRect(n,o,a,h)}renderSelection(t,e){const i={selectionStart:this.inCompositionMode?this.hiddenTextarea.selectionStart:this.selectionStart,selectionEnd:this.inCompositionMode?this.hiddenTextarea.selectionEnd:this.selectionEnd};this._renderSelection(t,i,e)}renderDragSourceEffect(){const t=this.draggableTextDelegate.getDragStartSelection();this._renderSelection(this.canvas.contextTop,t,this._getCursorBoundaries(t.selectionStart,!0))}renderDropTargetEffect(t){const e=this.getSelectionStartFromPointer(t);this.renderCursorAt(e)}_renderSelection(t,e,i){const s=e.selectionStart,r=e.selectionEnd,n=this.textAlign.includes(c),o=this.get2DCursorLocation(s),a=this.get2DCursorLocation(r),h=o.lineIndex,l=a.lineIndex,m=o.charIndex<0?0:o.charIndex,x=a.charIndex<0?0:a.charIndex;for(let e=h;e<=l;e++){const s=this._getLineLeftOffset(e)||0;let r=this.getHeightOfLine(e),o=0,a=0,_=0;if(e===h&&(a=this.__charBounds[h][m].left),e>=h&&e<l)_=n&&!this.isEndOfWrapping(e)?this.width:this.getLineWidth(e)||5;else if(e===l)if(0===x)_=this.__charBounds[l][x].left;else{const t=this._getWidthOfCharSpacing();_=this.__charBounds[l][x-1].left+this.__charBounds[l][x-1].width-t}o=r,(this.lineHeight<1||e===l&&this.lineHeight>1)&&(r/=this.lineHeight);let S=i.left+s+a,v=r,O=0;const y=_-a;this.inCompositionMode?(t.fillStyle=this.compositionColor||"black",v=1,O=r):t.fillStyle=this.selectionColor,"rtl"===this.direction&&(this.textAlign===u||this.textAlign===c||this.textAlign===d?S=this.width-S-y:this.textAlign===p||this.textAlign===f?S=i.left+s-_:this.textAlign!==C&&this.textAlign!==g||(S=i.left+s-_)),t.fillRect(S,i.top+i.topOffset+O,y,v),i.topOffset+=o}}getCurrentCharFontSize(){const t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,"fontSize")}getCurrentCharColor(){const t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,m)}_getCurrentCharIndex(){const t=this.get2DCursorLocation(this.selectionStart,!0),e=t.charIndex>0?t.charIndex-1:0;return{l:t.lineIndex,c:e}}dispose(){this.exitEditingImpl(),this.draggableTextDelegate.dispose(),super.dispose()}}t(y,"ownDefaults",O),t(y,"type","IText"),l.setClass(y),l.setClass(y,"i-text");export{y as IText,O as iTextDefaultValues};
|
|
1
|
+
import{defineProperty as t}from"../../../_virtual/_rollupPluginBabelHelpers.min.mjs";import{Canvas as e}from"../../canvas/Canvas.min.mjs";import{ITextClickBehavior as i}from"./ITextClickBehavior.min.mjs";import{getCursorRect as s}from"../../text/hitTest.min.mjs";import{analyzeBiDi as n}from"../../text/unicode.min.mjs";import{ctrlKeysMapUp as r,ctrlKeysMapDown as o,keysMapRtl as l,keysMap as h}from"./constants.min.mjs";import{classRegistry as a}from"../../ClassRegistry.min.mjs";import{JUSTIFY as c}from"../Text/constants.min.mjs";import{FILL as d}from"../../constants.min.mjs";import{createCanvasElementFor as g}from"../../util/misc/dom.min.mjs";import{applyCanvasTransform as f}from"../../util/internals/applyCanvasTransform.min.mjs";import{invertTransform as u}from"../../util/misc/matrix.min.mjs";const p={selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"",cursorDelay:1e3,cursorDuration:600,caching:!0,hiddenTextareaContainer:null,keysMap:h,keysMapRtl:l,ctrlKeysMapDown:o,ctrlKeysMapUp:r,_selectionDirection:null,_reSpace:/\s|\r?\n/,inCompositionMode:!1};class x extends i{static getDefaults(){return{...super.getDefaults(),...x.ownDefaults}}get type(){const t=super.type;return"itext"===t?"i-text":t}constructor(e,i){super(e,{...x.ownDefaults,...i}),t(this,"_visualPositionsCache",new Map),this.initBehavior()}_set(t,i){return this.isEditing&&this._savedProps&&t in this._savedProps?(this._savedProps[t]=i,this):("canvas"===t&&(this.canvas instanceof e&&this.canvas.textEditingManager.remove(this),i instanceof e&&i.textEditingManager.add(this)),super._set(t,i))}setSelectionStart(t){t=Math.max(t,0),this._updateAndFire("selectionStart",t)}setSelectionEnd(t){t=Math.min(t,this.text.length),this._updateAndFire("selectionEnd",t)}_updateAndFire(t,e){this[t]!==e&&(this._fireSelectionChanged(),this[t]=e),this._updateTextarea()}_fireSelectionChanged(){this.fire("selection:changed"),this.canvas&&this.canvas.fire("text:selection:changed",{target:this})}initDimensions(){this.isEditing&&this.initDelayedCursor(),super.initDimensions()}getSelectionStyles(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart||0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selectionEnd,i=arguments.length>2?arguments[2]:void 0;return super.getSelectionStyles(t,e,i)}setSelectionStyles(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selectionStart||0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.selectionEnd;return super.setSelectionStyles(t,e,i)}get2DCursorLocation(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1?arguments[1]:void 0;return super.get2DCursorLocation(t,e)}render(t){super.render(t),this.cursorOffsetCache={},this._clearVisualPositionsCache(),this.renderCursorOrSelection()}toCanvasElement(t){const e=this.isEditing;this.isEditing=!1;const i=super.toCanvasElement(t);return this.isEditing=e,i}renderCursorOrSelection(){if(!this.isEditing||!this.canvas)return;const t=this.clearContextTop(!0);if(!t)return;this.cursorOffsetCache={};const e=this._getCursorBoundaries(),i=this.findAncestorsWithClipPath(),s=i.length>0;let n,r=t;if(s){n=g(t.canvas),r=n.getContext("2d"),f(r,this.canvas);const e=this.calcTransformMatrix();r.transform(e[0],e[1],e[2],e[3],e[4],e[5])}if(this.selectionStart!==this.selectionEnd||this.inCompositionMode?this.renderSelection(r,e):this.renderCursor(r,e),s)for(const e of i){const i=e.clipPath,s=g(t.canvas),n=s.getContext("2d");if(f(n,this.canvas),!i.absolutePositioned){const t=e.calcTransformMatrix();n.transform(t[0],t[1],t[2],t[3],t[4],t[5])}i.transform(n),i.drawObject(n,!0,{}),this.drawClipPathOnCache(r,i,s)}s&&(t.setTransform(1,0,0,1,0,0),t.drawImage(n,0,0)),this.canvas.contextTopDirty=!0,t.restore()}findAncestorsWithClipPath(){const t=[];let e=this;for(;e;)e.clipPath&&t.push(e),e=e.parent;return t}_getCursorBoundaries(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1?arguments[1]:void 0;return this._getCursorBoundariesOriginal(t,e)}_getCursorBoundariesOffsets(t,e){return e?this.__getCursorBoundariesOffsets(t):this.cursorOffsetCache&&"top"in this.cursorOffsetCache?this.cursorOffsetCache:this.cursorOffsetCache=this.__getCursorBoundariesOffsets(t)}_getCursorBoundariesAdvanced(t){if(!this.enableAdvancedLayout||!this._layoutTextAdvanced)return this._getCursorBoundariesOriginal(t);const e=this._layoutTextAdvanced(),i=s(t,e,this._getAdvancedLayoutOptions());return{left:this._getLeftOffset(),top:this._getTopOffset(),leftOffset:i.x,topOffset:i.y}}getSelectionStartFromPointer(t){const e=this.canvas.getScenePoint(t).transform(u(this.calcTransformMatrix())),i=e.x+this.width/2,s=e.y+this.height/2;let n=0,r=0;for(let t=0;t<this._textLines.length;t++){const e=this.getHeightOfLine(t);if(s>=n&&s<n+e){r=t;break}n+=e,t===this._textLines.length-1&&(r=t)}let o=0;for(let t=0;t<r;t++){const e=this._getOriginalLineLength(t),i=this.missingNewlineOffset(t);console.log(`📍 Line ${t}: origLen=${e}, displayLen=${this._textLines[t].length}, tatweels=${this._getTatweelCountForLine(t)}, newlineOffset=${i}`),o+=e+i}console.log(`📍 Click on line ${r}, lineStartIndex=${o}`);const l=this._textLines[r],h=l.join(""),a=l.length,c=this._getOriginalLineLength(r);if(0===a)return o;const d=this._measureVisualPositions(r,h),g=this.getLineWidth(r);let f=0;"center"===this.textAlign||"justify-center"===this.textAlign?f=(this.width-g)/2:"right"===this.textAlign||"justify-right"===this.textAlign?f=this.width-g:"rtl"!==this.direction||"justify"!==this.textAlign&&"left"!==this.textAlign||(f=this.width-g);const p=i-f,x=[...d].sort((t,e)=>t.visualX-e.visualX);if(x.length>0&&p<x[0].visualX)return"rtl"===this.direction?o+c:o;if(x.length>0){const t=x[x.length-1];if(p>t.visualX+t.width)return"rtl"===this.direction?o:o+c}for(let t=0;t<x.length;t++){const e=x[t],i=e.visualX+e.width;if(p>=e.visualX&&p<=i){const t=this._displayToOriginalIndex(r,e.logicalIndex),i=this._isTatweelAtDisplayIndex(r,e.logicalIndex);console.log(`📍 Hit char: displayIdx=${e.logicalIndex}, origIdx=${t}, isTatweel=${i}, char="${this._textLines[r][e.logicalIndex]}"`);const s=p<=e.visualX+e.width/2;if(i){const e=o+t;return console.log(`📍 Tatweel click result: ${e}`),e}if(e.isRtl){const e=o+(s?t+1:t);return console.log(`📍 RTL char result: ${e} (clickedLeftHalf=${s})`),e}{const e=o+(s?t:t+1);return console.log(`📍 LTR char result: ${e} (clickedLeftHalf=${s})`),e}}}return o+c}_clearVisualPositionsCache(){this._visualPositionsCache.clear()}_measureVisualPositions(t,e){if(this._visualPositionsCache.has(t))return this._visualPositionsCache.get(t);const i=this._textLines[t],s=[],r=this.__charBounds[t];if(!r||0===r.length)return this._visualPositionsCache.set(t,s),s;if("rtl"!==this.direction){for(let t=0;t<i.length;t++){var o,l;s.push({logicalIndex:t,visualX:(null===(o=r[t])||void 0===o?void 0:o.left)||0,width:(null===(l=r[t])||void 0===l?void 0:l.kernedWidth)||0,isRtl:!1})}return this._visualPositionsCache.set(t,s),s}const h=n(e,"rtl"),a=[];let c=0;for(let t=0;t<i.length;t++){const e=i[t];for(let i=0;i<e.length;i++)a[c+i]=t;c+=e.length}const d=[];for(const t of h){const e=[];let i=0;const s=new Set;for(let n=t.start;n<t.end;n++){const t=a[n];var g;if(void 0!==t&&!s.has(t))s.add(t),e.push(t),i+=(null===(g=r[t])||void 0===g?void 0:g.kernedWidth)||0}d.push({run:t,width:i,charIndices:e})}let f=this.getLineWidth(t);for(const t of d){f-=t.width;if("rtl"===t.run.direction){let e=f+t.width;for(const i of t.charIndices){var u;const t=(null===(u=r[i])||void 0===u?void 0:u.kernedWidth)||0;e-=t,s.push({logicalIndex:i,visualX:e,width:t,isRtl:!0})}}else{let e=f;for(const i of t.charIndices){var p;const t=(null===(p=r[i])||void 0===p?void 0:p.kernedWidth)||0;s.push({logicalIndex:i,visualX:e,width:t,isRtl:!1}),e+=t}}}return this._visualPositionsCache.set(t,s),s}_getCursorBoundariesOriginal(t,e){const i=this._getLeftOffset(),s=this._getTopOffset(),n=this._getCursorBoundariesOffsets(t,e);return{left:i,top:s,leftOffset:n.left,topOffset:n.top}}__getCursorBoundariesOffsets(t){let e=0,i=0,s=t;for(let t=0;t<this._textLines.length;t++){const e=this._getOriginalLineLength(t);if(s<=e){i=t;break}s-=e+this.missingNewlineOffset(t),i=t+1}i>=this._textLines.length&&(i=this._textLines.length-1,s=this._getOriginalLineLength(i));for(let t=0;t<i;t++)e+=this.getHeightOfLine(t);const n=this._originalToDisplayIndex(i,s),r=this._textLines[i].join(""),o=this._measureVisualPositions(i,r),l=this.getLineWidth(i);this._textLines[i].length;const h=this._getOriginalLineLength(i);let a=0;if(0===o.length)return{top:e,left:0};if(0===s)a="rtl"===this.direction?l:0;else if(s>=h)a="rtl"===this.direction?0:l;else{const t=o.find(t=>t.logicalIndex===n);if(t)a=t.isRtl?t.visualX+t.width:t.visualX;else{const t=n>0?n-1:0,e=o.find(e=>e.logicalIndex===t);if(e)a=e.isRtl?e.visualX:e.visualX+e.width;else{const t=this.__charBounds[i][n];a=(null==t?void 0:t.left)||0}}}let c,d=0;return"center"===this.textAlign||"justify-center"===this.textAlign?d=(this.width-l)/2:"right"===this.textAlign||"justify-right"===this.textAlign?d=this.width-l:"rtl"!==this.direction||"justify"!==this.textAlign&&"left"!==this.textAlign||(d=this.width-l),c="rtl"===this.direction?-this.width+d+a:d+a,{top:e,left:c}}renderCursorAt(t){this._renderCursor(this.canvas.contextTop,this._getCursorBoundaries(t,!0),t)}renderCursor(t,e){this._renderCursor(t,e,this.selectionStart)}getCursorRenderingData(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.selectionStart,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getCursorBoundaries(t);const i=this.get2DCursorLocation(t),s=i.lineIndex,n=i.charIndex>0?i.charIndex-1:0,r=this.getValueOfPropertyAt(s,n,"fontSize"),o=this.getObjectScaling().x*this.canvas.getZoom(),l=this.cursorWidth/o,h=this.getValueOfPropertyAt(s,n,"deltaY"),a=e.topOffset+(1-this._fontSizeFraction)*this.getHeightOfLine(s)/this.lineHeight-r*(1-this._fontSizeFraction);return{color:this.cursorColor||this.getValueOfPropertyAt(s,n,"fill"),opacity:this._currentCursorOpacity,left:e.left+e.leftOffset-l/2,top:a+e.top+h,width:l,height:r}}_renderCursor(t,e,i){const{color:s,opacity:n,left:r,top:o,width:l,height:h}=this.getCursorRenderingData(i,e);t.fillStyle=s,t.globalAlpha=n,t.fillRect(r,o,l,h)}renderSelection(t,e){const i={selectionStart:this.inCompositionMode?this.hiddenTextarea.selectionStart:this.selectionStart,selectionEnd:this.inCompositionMode?this.hiddenTextarea.selectionEnd:this.selectionEnd};this._renderSelection(t,i,e)}renderDragSourceEffect(){const t=this.draggableTextDelegate.getDragStartSelection();this._renderSelection(this.canvas.contextTop,t,this._getCursorBoundaries(t.selectionStart,!0))}renderDropTargetEffect(t){const e=this.getSelectionStartFromPointer(t);this.renderCursorAt(e)}_renderSelection(t,e,i){const s=e.selectionStart,n=e.selectionEnd,r=this.textAlign.includes(c);let o=0,l=0,h=s,a=n,d=0;for(let t=0;t<this._textLines.length;t++){const e=this._getOriginalLineLength(t);if(d+e>=s){o=t,h=s-d;break}d+=e+this.missingNewlineOffset(t)}d=0;for(let t=0;t<this._textLines.length;t++){const e=this._getOriginalLineLength(t);if(d+e>=n){l=t,a=n-d;break}d+=e+this.missingNewlineOffset(t),t===this._textLines.length-1&&(l=t,a=e)}for(let e=o;e<=l;e++){let s=this.getHeightOfLine(e),n=0;const c=this._textLines[e].join(""),d=this._measureVisualPositions(e,c);this._textLines[e].length;let g=0,f=this._getOriginalLineLength(e);e===o&&(g=h),e===l&&(f=a);const u=this._originalToDisplayIndex(e,g),p=this._originalToDisplayIndex(e,f);let x=1/0,_=-1/0;for(const t of d)t.logicalIndex>=u&&t.logicalIndex<p&&(x=Math.min(x,t.visualX),_=Math.max(_,t.visualX+t.width));if(x===1/0||_===-1/0){if(!(e>=o&&e<l))continue;x=0,_=r&&!this.isEndOfWrapping(e)?this.width:this.getLineWidth(e)||5}n=s,(this.lineHeight<1||e===l&&this.lineHeight>1)&&(s/=this.lineHeight);const m=this.getLineWidth(e);let C=0;"center"===this.textAlign||"justify-center"===this.textAlign?C=(this.width-m)/2:"right"===this.textAlign||"justify-right"===this.textAlign?C=this.width-m:"rtl"!==this.direction||"justify"!==this.textAlign&&"left"!==this.textAlign||(C=this.width-m);const v=-this.width/2+C+x,L=_-x;let O=s,S=0;this.inCompositionMode?(t.fillStyle=this.compositionColor||"black",O=1,S=s):t.fillStyle=this.selectionColor,t.fillRect(v,i.top+i.topOffset+S,L,O),i.topOffset+=n}}getCurrentCharFontSize(){const t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,"fontSize")}getCurrentCharColor(){const t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,d)}_getCurrentCharIndex(){const t=this.get2DCursorLocation(this.selectionStart,!0),e=t.charIndex>0?t.charIndex-1:0;return{l:t.lineIndex,c:e}}dispose(){this.exitEditingImpl(),this.draggableTextDelegate.dispose(),super.dispose()}}t(x,"ownDefaults",p),t(x,"type","IText"),a.setClass(x),a.setClass(x,"i-text");export{x as IText,p as iTextDefaultValues};
|
|
2
2
|
//# sourceMappingURL=IText.min.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IText.min.mjs","sources":["../../../../src/shapes/IText/IText.ts"],"sourcesContent":["import { Canvas } from '../../canvas/Canvas';\r\nimport type { ITextEvents } from './ITextBehavior';\r\nimport { ITextClickBehavior } from './ITextClickBehavior';\r\nimport { hitTest, getCursorRect, getSelectionRects } from '../../text/hitTest';\r\nimport type { HitTestResult, CursorRect } from '../../text/hitTest';\r\nimport {\r\n ctrlKeysMapDown,\r\n ctrlKeysMapUp,\r\n keysMap,\r\n keysMapRtl,\r\n} from './constants';\r\nimport type { TClassProperties, TFiller, TOptions } from '../../typedefs';\r\nimport type { TPointerEvent } from '../../EventTypeDefs';\r\nimport { classRegistry } from '../../ClassRegistry';\r\nimport type { SerializedTextProps, TextProps } from '../Text/Text';\r\nimport {\r\n JUSTIFY,\r\n JUSTIFY_CENTER,\r\n JUSTIFY_LEFT,\r\n JUSTIFY_RIGHT,\r\n} from '../Text/constants';\r\nimport { CENTER, FILL, LEFT, RIGHT } from '../../constants';\r\nimport type { ObjectToCanvasElementOptions } from '../Object/Object';\r\nimport type { FabricObject } from '../Object/FabricObject';\r\nimport { createCanvasElementFor } from '../../util/misc/dom';\r\nimport { applyCanvasTransform } from '../../util/internals/applyCanvasTransform';\r\nimport { Point } from '../../Point';\r\nimport { invertTransform } from '../../util/misc/matrix';\r\n\r\nexport type CursorBoundaries = {\r\n left: number;\r\n top: number;\r\n leftOffset: number;\r\n topOffset: number;\r\n};\r\n\r\nexport type CursorRenderingData = {\r\n color: string;\r\n opacity: number;\r\n left: number;\r\n top: number;\r\n width: number;\r\n height: number;\r\n};\r\n\r\n// Declare IText protected properties to workaround TS\r\nconst protectedDefaultValues = {\r\n _selectionDirection: null,\r\n _reSpace: /\\s|\\r?\\n/,\r\n inCompositionMode: false,\r\n};\r\n\r\nexport const iTextDefaultValues: Partial<TClassProperties<IText>> = {\r\n selectionStart: 0,\r\n selectionEnd: 0,\r\n selectionColor: 'rgba(17,119,255,0.3)',\r\n isEditing: false,\r\n editable: true,\r\n editingBorderColor: 'rgba(102,153,255,0.25)',\r\n cursorWidth: 2,\r\n cursorColor: '',\r\n cursorDelay: 1000,\r\n cursorDuration: 600,\r\n caching: true,\r\n hiddenTextareaContainer: null,\r\n keysMap,\r\n keysMapRtl,\r\n ctrlKeysMapDown,\r\n ctrlKeysMapUp,\r\n ...protectedDefaultValues,\r\n};\r\n\r\n// @TODO this is not complete\r\ninterface UniqueITextProps {\r\n selectionStart: number;\r\n selectionEnd: number;\r\n}\r\n\r\nexport interface SerializedITextProps\r\n extends SerializedTextProps,\r\n UniqueITextProps {}\r\n\r\nexport interface ITextProps extends TextProps, UniqueITextProps {}\r\n\r\n/**\r\n * @fires changed\r\n * @fires selection:changed\r\n * @fires editing:entered\r\n * @fires editing:exited\r\n * @fires dragstart\r\n * @fires drag drag event firing on the drag source\r\n * @fires dragend\r\n * @fires copy\r\n * @fires cut\r\n * @fires paste\r\n *\r\n * #### Supported key combinations\r\n * ```\r\n * Move cursor: left, right, up, down\r\n * Select character: shift + left, shift + right\r\n * Select text vertically: shift + up, shift + down\r\n * Move cursor by word: alt + left, alt + right\r\n * Select words: shift + alt + left, shift + alt + right\r\n * Move cursor to line start/end: cmd + left, cmd + right or home, end\r\n * Select till start/end of line: cmd + shift + left, cmd + shift + right or shift + home, shift + end\r\n * Jump to start/end of text: cmd + up, cmd + down\r\n * Select till start/end of text: cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown\r\n * Delete character: backspace\r\n * Delete word: alt + backspace\r\n * Delete line: cmd + backspace\r\n * Forward delete: delete\r\n * Copy text: ctrl/cmd + c\r\n * Paste text: ctrl/cmd + v\r\n * Cut text: ctrl/cmd + x\r\n * Select entire text: ctrl/cmd + a\r\n * Quit editing tab or esc\r\n * ```\r\n *\r\n * #### Supported mouse/touch combination\r\n * ```\r\n * Position cursor: click/touch\r\n * Create selection: click/touch & drag\r\n * Create selection: click & shift + click\r\n * Select word: double click\r\n * Select line: triple click\r\n * ```\r\n */\r\nexport class IText<\r\n Props extends TOptions<ITextProps> = Partial<ITextProps>,\r\n SProps extends SerializedITextProps = SerializedITextProps,\r\n EventSpec extends ITextEvents = ITextEvents,\r\n >\r\n extends ITextClickBehavior<Props, SProps, EventSpec>\r\n implements UniqueITextProps\r\n{\r\n /**\r\n * Index where text selection starts (or where cursor is when there is no selection)\r\n * @type Number\r\n */\r\n declare selectionStart: number;\r\n\r\n /**\r\n * Index where text selection ends\r\n * @type Number\r\n */\r\n declare selectionEnd: number;\r\n\r\n declare compositionStart: number;\r\n\r\n declare compositionEnd: number;\r\n\r\n /**\r\n * Color of text selection\r\n * @type String\r\n */\r\n declare selectionColor: string;\r\n\r\n /**\r\n * Indicates whether text is in editing mode\r\n * @type Boolean\r\n */\r\n declare isEditing: boolean;\r\n\r\n /**\r\n * Indicates whether a text can be edited\r\n * @type Boolean\r\n */\r\n declare editable: boolean;\r\n\r\n /**\r\n * Border color of text object while it's in editing mode\r\n * @type String\r\n */\r\n declare editingBorderColor: string;\r\n\r\n /**\r\n * Width of cursor (in px)\r\n * @type Number\r\n */\r\n declare cursorWidth: number;\r\n\r\n /**\r\n * Color of text cursor color in editing mode.\r\n * if not set (default) will take color from the text.\r\n * if set to a color value that fabric can understand, it will\r\n * be used instead of the color of the text at the current position.\r\n * @type String\r\n */\r\n declare cursorColor: string;\r\n\r\n /**\r\n * Delay between cursor blink (in ms)\r\n * @type Number\r\n */\r\n declare cursorDelay: number;\r\n\r\n /**\r\n * Duration of cursor fade in (in ms)\r\n * @type Number\r\n */\r\n declare cursorDuration: number;\r\n\r\n declare compositionColor: string;\r\n\r\n /**\r\n * Indicates whether internal text char widths can be cached\r\n * @type Boolean\r\n */\r\n declare caching: boolean;\r\n\r\n static ownDefaults = iTextDefaultValues;\r\n\r\n static getDefaults(): Record<string, any> {\r\n return { ...super.getDefaults(), ...IText.ownDefaults };\r\n }\r\n\r\n static type = 'IText';\r\n\r\n get type() {\r\n const type = super.type;\r\n // backward compatibility\r\n return type === 'itext' ? 'i-text' : type;\r\n }\r\n\r\n /**\r\n * Constructor\r\n * @param {String} text Text string\r\n * @param {Object} [options] Options object\r\n */\r\n constructor(text: string, options?: Props) {\r\n super(text, { ...IText.ownDefaults, ...options } as Props);\r\n this.initBehavior();\r\n }\r\n\r\n /**\r\n * While editing handle differently\r\n * @private\r\n * @param {string} key\r\n * @param {*} value\r\n */\r\n _set(key: string, value: any) {\r\n if (this.isEditing && this._savedProps && key in this._savedProps) {\r\n // @ts-expect-error irritating TS\r\n this._savedProps[key] = value;\r\n return this;\r\n }\r\n if (key === 'canvas') {\r\n this.canvas instanceof Canvas &&\r\n this.canvas.textEditingManager.remove(this);\r\n value instanceof Canvas && value.textEditingManager.add(this);\r\n }\r\n return super._set(key, value);\r\n }\r\n\r\n /**\r\n * Sets selection start (left boundary of a selection)\r\n * @param {Number} index Index to set selection start to\r\n */\r\n setSelectionStart(index: number) {\r\n index = Math.max(index, 0);\r\n this._updateAndFire('selectionStart', index);\r\n }\r\n\r\n /**\r\n * Sets selection end (right boundary of a selection)\r\n * @param {Number} index Index to set selection end to\r\n */\r\n setSelectionEnd(index: number) {\r\n index = Math.min(index, this.text.length);\r\n this._updateAndFire('selectionEnd', index);\r\n }\r\n\r\n /**\r\n * @private\r\n * @param {String} property 'selectionStart' or 'selectionEnd'\r\n * @param {Number} index new position of property\r\n */\r\n protected _updateAndFire(\r\n property: 'selectionStart' | 'selectionEnd',\r\n index: number,\r\n ) {\r\n if (this[property] !== index) {\r\n this._fireSelectionChanged();\r\n this[property] = index;\r\n }\r\n this._updateTextarea();\r\n }\r\n\r\n /**\r\n * Fires the even of selection changed\r\n * @private\r\n */\r\n _fireSelectionChanged() {\r\n this.fire('selection:changed');\r\n this.canvas && this.canvas.fire('text:selection:changed', { target: this });\r\n }\r\n\r\n /**\r\n * Initialize text dimensions. Render all text on given context\r\n * or on a offscreen canvas to get the text width with measureText.\r\n * Updates this.width and this.height with the proper values.\r\n * Does not return dimensions.\r\n * @private\r\n */\r\n initDimensions() {\r\n this.isEditing && this.initDelayedCursor();\r\n super.initDimensions();\r\n }\r\n\r\n /**\r\n * Gets style of a current selection/cursor (at the start position)\r\n * if startIndex or endIndex are not provided, selectionStart or selectionEnd will be used.\r\n * @param {Number} startIndex Start index to get styles at\r\n * @param {Number} endIndex End index to get styles at, if not specified selectionEnd or startIndex + 1\r\n * @param {Boolean} [complete] get full style or not\r\n * @return {Array} styles an array with one, zero or more Style objects\r\n */\r\n getSelectionStyles(\r\n startIndex: number = this.selectionStart || 0,\r\n endIndex: number = this.selectionEnd,\r\n complete?: boolean,\r\n ) {\r\n return super.getSelectionStyles(startIndex, endIndex, complete);\r\n }\r\n\r\n /**\r\n * Sets style of a current selection, if no selection exist, do not set anything.\r\n * @param {Object} [styles] Styles object\r\n * @param {Number} [startIndex] Start index to get styles at\r\n * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1\r\n */\r\n setSelectionStyles(\r\n styles: object,\r\n startIndex: number = this.selectionStart || 0,\r\n endIndex: number = this.selectionEnd,\r\n ) {\r\n return super.setSelectionStyles(styles, startIndex, endIndex);\r\n }\r\n\r\n /**\r\n * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)\r\n * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.\r\n * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. useful to manage styles.\r\n */\r\n get2DCursorLocation(\r\n selectionStart = this.selectionStart,\r\n skipWrapping?: boolean,\r\n ) {\r\n return super.get2DCursorLocation(selectionStart, skipWrapping);\r\n }\r\n\r\n /**\r\n * @private\r\n * @param {CanvasRenderingContext2D} ctx Context to render on\r\n */\r\n render(ctx: CanvasRenderingContext2D) {\r\n super.render(ctx);\r\n // clear the cursorOffsetCache, so we ensure to calculate once per renderCursor\r\n // the correct position but not at every cursor animation.\r\n this.cursorOffsetCache = {};\r\n this.renderCursorOrSelection();\r\n }\r\n\r\n /**\r\n * @override block cursor/selection logic while rendering the exported canvas\r\n * @todo this workaround should be replaced with a more robust solution\r\n */\r\n toCanvasElement(options?: ObjectToCanvasElementOptions): HTMLCanvasElement {\r\n const isEditing = this.isEditing;\r\n this.isEditing = false;\r\n const canvas = super.toCanvasElement(options);\r\n this.isEditing = isEditing;\r\n return canvas;\r\n }\r\n\r\n /**\r\n * Renders cursor or selection (depending on what exists)\r\n * it does on the contextTop. If contextTop is not available, do nothing.\r\n */\r\n renderCursorOrSelection() {\r\n if (!this.isEditing || !this.canvas) {\r\n return;\r\n }\r\n const ctx = this.clearContextTop(true);\r\n if (!ctx) {\r\n return;\r\n }\r\n const boundaries = this._getCursorBoundaries();\r\n\r\n const ancestors = this.findAncestorsWithClipPath();\r\n const hasAncestorsWithClipping = ancestors.length > 0;\r\n let drawingCtx: CanvasRenderingContext2D = ctx;\r\n let drawingCanvas: HTMLCanvasElement | undefined = undefined;\r\n if (hasAncestorsWithClipping) {\r\n // we have some clipPath, we need to draw the selection on an intermediate layer.\r\n drawingCanvas = createCanvasElementFor(ctx.canvas);\r\n drawingCtx = drawingCanvas.getContext('2d')!;\r\n applyCanvasTransform(drawingCtx, this.canvas);\r\n const m = this.calcTransformMatrix();\r\n drawingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\r\n }\r\n\r\n if (this.selectionStart === this.selectionEnd && !this.inCompositionMode) {\r\n this.renderCursor(drawingCtx, boundaries);\r\n } else {\r\n this.renderSelection(drawingCtx, boundaries);\r\n }\r\n\r\n if (hasAncestorsWithClipping) {\r\n // we need a neutral context.\r\n // this won't work for nested clippaths in which a clippath\r\n // has its own clippath\r\n for (const ancestor of ancestors) {\r\n const clipPath = ancestor.clipPath!;\r\n const clippingCanvas = createCanvasElementFor(ctx.canvas);\r\n const clippingCtx = clippingCanvas.getContext('2d')!;\r\n applyCanvasTransform(clippingCtx, this.canvas);\r\n // position the ctx in the center of the outer ancestor\r\n if (!clipPath.absolutePositioned) {\r\n const m = ancestor.calcTransformMatrix();\r\n clippingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\r\n }\r\n clipPath.transform(clippingCtx);\r\n // we assign an empty drawing context, we don't plan to have this working for nested clippaths for now\r\n clipPath.drawObject(clippingCtx, true, {});\r\n this.drawClipPathOnCache(drawingCtx, clipPath, clippingCanvas);\r\n }\r\n }\r\n\r\n if (hasAncestorsWithClipping) {\r\n ctx.setTransform(1, 0, 0, 1, 0, 0);\r\n ctx.drawImage(drawingCanvas!, 0, 0);\r\n }\r\n\r\n this.canvas.contextTopDirty = true;\r\n ctx.restore();\r\n }\r\n\r\n /**\r\n * Finds and returns an array of clip paths that are applied to the parent\r\n * group(s) of the current FabricObject instance. The object's hierarchy is\r\n * traversed upwards (from the current object towards the root of the canvas),\r\n * checking each parent object for the presence of a `clipPath` that is not\r\n * absolutely positioned.\r\n */\r\n findAncestorsWithClipPath(): FabricObject[] {\r\n const clipPathAncestors: FabricObject[] = [];\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n let obj: FabricObject | undefined = this;\r\n while (obj) {\r\n if (obj.clipPath) {\r\n clipPathAncestors.push(obj);\r\n }\r\n obj = obj.parent;\r\n }\r\n\r\n return clipPathAncestors;\r\n }\r\n\r\n /**\r\n * Returns cursor boundaries (left, top, leftOffset, topOffset)\r\n * left/top are left/top of entire text box\r\n * leftOffset/topOffset are offset from that left/top point of a text box\r\n * @private\r\n * @param {number} [index] index from start\r\n * @param {boolean} [skipCaching]\r\n */\r\n _getCursorBoundaries(\r\n index: number = this.selectionStart,\r\n skipCaching?: boolean,\r\n ): CursorBoundaries {\r\n // Use advanced cursor positioning if available\r\n if (this.enableAdvancedLayout) {\r\n return this._getCursorBoundariesAdvanced(index);\r\n }\r\n \r\n // Fall back to original method\r\n return this._getCursorBoundariesOriginal(index, skipCaching);\r\n }\r\n\r\n\r\n /**\r\n * Caches and returns cursor left/top offset relative to instance's center point\r\n * @private\r\n * @param {number} index index from start\r\n * @param {boolean} [skipCaching]\r\n */\r\n _getCursorBoundariesOffsets(\r\n index: number,\r\n skipCaching?: boolean,\r\n ): { left: number; top: number } {\r\n if (skipCaching) {\r\n return this.__getCursorBoundariesOffsets(index);\r\n }\r\n if (this.cursorOffsetCache && 'top' in this.cursorOffsetCache) {\r\n return this.cursorOffsetCache as { left: number; top: number };\r\n }\r\n return (this.cursorOffsetCache = this.__getCursorBoundariesOffsets(index));\r\n }\r\n\r\n /**\r\n * Enhanced cursor boundaries using advanced hit testing when available\r\n * @private\r\n */\r\n _getCursorBoundariesAdvanced(index: number): CursorBoundaries {\r\n if (!this.enableAdvancedLayout || !(this as any)._layoutTextAdvanced) {\r\n return this._getCursorBoundariesOriginal(index);\r\n }\r\n\r\n const layout = (this as any)._layoutTextAdvanced();\r\n const cursorRect = getCursorRect(index, layout, (this as any)._getAdvancedLayoutOptions());\r\n \r\n return {\r\n left: this._getLeftOffset(),\r\n top: this._getTopOffset(),\r\n leftOffset: cursorRect.x,\r\n topOffset: cursorRect.y,\r\n };\r\n }\r\n\r\n /**\r\n * Enhanced selection start from pointer using BiDi-aware hit testing\r\n * @override\r\n */\r\n getSelectionStartFromPointer(e: TPointerEvent): number {\r\n if (!this.enableAdvancedLayout || !(this as any)._layoutTextAdvanced) {\r\n return super.getSelectionStartFromPointer(e);\r\n }\r\n\r\n const mouseOffset = this.canvas!.getScenePoint(e)\r\n .transform(invertTransform(this.calcTransformMatrix()))\r\n .add(new Point(-this._getLeftOffset(), -this._getTopOffset()));\r\n\r\n // Use BiDi-aware hit testing instead of naive RTL coordinate flipping\r\n const layout = (this as any)._layoutTextAdvanced();\r\n const hitResult = hitTest(mouseOffset.x, mouseOffset.y, layout, (this as any)._getAdvancedLayoutOptions());\r\n \r\n return Math.min(hitResult.charIndex, this._text.length);\r\n }\r\n\r\n /**\r\n * Original cursor boundaries implementation\r\n * @private\r\n */\r\n _getCursorBoundariesOriginal(index: number, skipCaching?: boolean): CursorBoundaries {\r\n const left = this._getLeftOffset(),\r\n top = this._getTopOffset(),\r\n offsets = this._getCursorBoundariesOffsets(index, skipCaching);\r\n return {\r\n left: left,\r\n top: top,\r\n leftOffset: offsets.left,\r\n topOffset: offsets.top,\r\n };\r\n }\r\n\r\n /**\r\n * Calculates cursor left/top offset relative to instance's center point\r\n * @private\r\n * @param {number} index index from start\r\n */\r\n __getCursorBoundariesOffsets(index: number) {\r\n let topOffset = 0,\r\n leftOffset = 0;\r\n const { charIndex, lineIndex } = this.get2DCursorLocation(index);\r\n\r\n for (let i = 0; i < lineIndex; i++) {\r\n topOffset += this.getHeightOfLine(i);\r\n }\r\n const lineLeftOffset = this._getLineLeftOffset(lineIndex);\r\n const bound = this.__charBounds[lineIndex][charIndex];\r\n bound && (leftOffset = bound.left);\r\n if (\r\n this.charSpacing !== 0 &&\r\n charIndex === this._textLines[lineIndex].length\r\n ) {\r\n leftOffset -= this._getWidthOfCharSpacing();\r\n }\r\n const boundaries = {\r\n top: topOffset,\r\n left: lineLeftOffset + (leftOffset > 0 ? leftOffset : 0),\r\n };\r\n if (this.direction === 'rtl') {\r\n if (\r\n this.textAlign === RIGHT ||\r\n this.textAlign === JUSTIFY ||\r\n this.textAlign === JUSTIFY_RIGHT\r\n ) {\r\n boundaries.left *= -1;\r\n } else if (this.textAlign === LEFT || this.textAlign === JUSTIFY_LEFT) {\r\n boundaries.left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0);\r\n } else if (\r\n this.textAlign === CENTER ||\r\n this.textAlign === JUSTIFY_CENTER\r\n ) {\r\n boundaries.left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0);\r\n }\r\n }\r\n return boundaries;\r\n }\r\n\r\n /**\r\n * Renders cursor on context Top, outside the animation cycle, on request\r\n * Used for the drag/drop effect.\r\n * If contextTop is not available, do nothing.\r\n */\r\n renderCursorAt(selectionStart: number) {\r\n this._renderCursor(\r\n this.canvas!.contextTop,\r\n this._getCursorBoundaries(selectionStart, true),\r\n selectionStart,\r\n );\r\n }\r\n\r\n /**\r\n * Renders cursor\r\n * @param {Object} boundaries\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n renderCursor(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\r\n this._renderCursor(ctx, boundaries, this.selectionStart);\r\n }\r\n\r\n /**\r\n * Return the data needed to render the cursor for given selection start\r\n * The left,top are relative to the object, while width and height are prescaled\r\n * to look think with canvas zoom and object scaling,\r\n * so they depend on canvas and object scaling\r\n */\r\n getCursorRenderingData(\r\n selectionStart: number = this.selectionStart,\r\n boundaries: CursorBoundaries = this._getCursorBoundaries(selectionStart),\r\n ): CursorRenderingData {\r\n const cursorLocation = this.get2DCursorLocation(selectionStart),\r\n lineIndex = cursorLocation.lineIndex,\r\n charIndex =\r\n cursorLocation.charIndex > 0 ? cursorLocation.charIndex - 1 : 0,\r\n charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize'),\r\n multiplier = this.getObjectScaling().x * this.canvas!.getZoom(),\r\n cursorWidth = this.cursorWidth / multiplier,\r\n dy = this.getValueOfPropertyAt(lineIndex, charIndex, 'deltaY'),\r\n topOffset =\r\n boundaries.topOffset +\r\n ((1 - this._fontSizeFraction) * this.getHeightOfLine(lineIndex)) /\r\n this.lineHeight -\r\n charHeight * (1 - this._fontSizeFraction);\r\n\r\n return {\r\n color:\r\n this.cursorColor ||\r\n (this.getValueOfPropertyAt(lineIndex, charIndex, 'fill') as string),\r\n opacity: this._currentCursorOpacity,\r\n left: boundaries.left + boundaries.leftOffset - cursorWidth / 2,\r\n top: topOffset + boundaries.top + dy,\r\n width: cursorWidth,\r\n height: charHeight,\r\n };\r\n }\r\n\r\n /**\r\n * Render the cursor at the given selectionStart.\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n _renderCursor(\r\n ctx: CanvasRenderingContext2D,\r\n boundaries: CursorBoundaries,\r\n selectionStart: number,\r\n ) {\r\n const { color, opacity, left, top, width, height } =\r\n this.getCursorRenderingData(selectionStart, boundaries);\r\n ctx.fillStyle = color;\r\n ctx.globalAlpha = opacity;\r\n ctx.fillRect(left, top, width, height);\r\n }\r\n\r\n /**\r\n * Renders text selection\r\n * @param {Object} boundaries Object with left/top/leftOffset/topOffset\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n renderSelection(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\r\n const selection = {\r\n selectionStart: this.inCompositionMode\r\n ? this.hiddenTextarea!.selectionStart\r\n : this.selectionStart,\r\n selectionEnd: this.inCompositionMode\r\n ? this.hiddenTextarea!.selectionEnd\r\n : this.selectionEnd,\r\n };\r\n this._renderSelection(ctx, selection, boundaries);\r\n }\r\n\r\n /**\r\n * Renders drag start text selection\r\n */\r\n renderDragSourceEffect() {\r\n const dragStartSelection =\r\n this.draggableTextDelegate.getDragStartSelection()!;\r\n this._renderSelection(\r\n this.canvas!.contextTop,\r\n dragStartSelection,\r\n this._getCursorBoundaries(dragStartSelection.selectionStart, true),\r\n );\r\n }\r\n\r\n renderDropTargetEffect(e: DragEvent) {\r\n const dragSelection = this.getSelectionStartFromPointer(e);\r\n this.renderCursorAt(dragSelection);\r\n }\r\n\r\n /**\r\n * Renders text selection\r\n * @private\r\n * @param {{ selectionStart: number, selectionEnd: number }} selection\r\n * @param {Object} boundaries Object with left/top/leftOffset/topOffset\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n _renderSelection(\r\n ctx: CanvasRenderingContext2D,\r\n selection: { selectionStart: number; selectionEnd: number },\r\n boundaries: CursorBoundaries,\r\n ) {\r\n const selectionStart = selection.selectionStart,\r\n selectionEnd = selection.selectionEnd,\r\n isJustify = this.textAlign.includes(JUSTIFY),\r\n start = this.get2DCursorLocation(selectionStart),\r\n end = this.get2DCursorLocation(selectionEnd),\r\n startLine = start.lineIndex,\r\n endLine = end.lineIndex,\r\n startChar = start.charIndex < 0 ? 0 : start.charIndex,\r\n endChar = end.charIndex < 0 ? 0 : end.charIndex;\r\n\r\n for (let i = startLine; i <= endLine; i++) {\r\n const lineOffset = this._getLineLeftOffset(i) || 0;\r\n let lineHeight = this.getHeightOfLine(i),\r\n realLineHeight = 0,\r\n boxStart = 0,\r\n boxEnd = 0;\r\n \r\n // Simplified selection rendering that works for both LTR and RTL\r\n if (i === startLine) {\r\n boxStart = this.__charBounds[startLine][startChar].left;\r\n }\r\n if (i >= startLine && i < endLine) {\r\n boxEnd =\r\n isJustify && !this.isEndOfWrapping(i)\r\n ? this.width\r\n : this.getLineWidth(i) || 5;\r\n } else if (i === endLine) {\r\n if (endChar === 0) {\r\n boxEnd = this.__charBounds[endLine][endChar].left;\r\n } else {\r\n const charSpacing = this._getWidthOfCharSpacing();\r\n boxEnd =\r\n this.__charBounds[endLine][endChar - 1].left +\r\n this.__charBounds[endLine][endChar - 1].width -\r\n charSpacing;\r\n }\r\n }\r\n realLineHeight = lineHeight;\r\n if (this.lineHeight < 1 || (i === endLine && this.lineHeight > 1)) {\r\n lineHeight /= this.lineHeight;\r\n }\r\n let drawStart = boundaries.left + lineOffset + boxStart,\r\n drawHeight = lineHeight,\r\n extraTop = 0;\r\n const drawWidth = boxEnd - boxStart;\r\n if (this.inCompositionMode) {\r\n ctx.fillStyle = this.compositionColor || 'black';\r\n drawHeight = 1;\r\n extraTop = lineHeight;\r\n } else {\r\n ctx.fillStyle = this.selectionColor;\r\n }\r\n if (this.direction === 'rtl') {\r\n if (\r\n this.textAlign === RIGHT ||\r\n this.textAlign === JUSTIFY ||\r\n this.textAlign === JUSTIFY_RIGHT\r\n ) {\r\n drawStart = this.width - drawStart - drawWidth;\r\n } else if (this.textAlign === LEFT || this.textAlign === JUSTIFY_LEFT) {\r\n drawStart = boundaries.left + lineOffset - boxEnd;\r\n } else if (\r\n this.textAlign === CENTER ||\r\n this.textAlign === JUSTIFY_CENTER\r\n ) {\r\n drawStart = boundaries.left + lineOffset - boxEnd;\r\n }\r\n }\r\n ctx.fillRect(\r\n drawStart,\r\n boundaries.top + boundaries.topOffset + extraTop,\r\n drawWidth,\r\n drawHeight,\r\n );\r\n boundaries.topOffset += realLineHeight;\r\n }\r\n }\r\n\r\n /**\r\n * High level function to know the height of the cursor.\r\n * the currentChar is the one that precedes the cursor\r\n * Returns fontSize of char at the current cursor\r\n * Unused from the library, is for the end user\r\n * @return {Number} Character font size\r\n */\r\n getCurrentCharFontSize(): number {\r\n const cp = this._getCurrentCharIndex();\r\n return this.getValueOfPropertyAt(cp.l, cp.c, 'fontSize');\r\n }\r\n\r\n /**\r\n * High level function to know the color of the cursor.\r\n * the currentChar is the one that precedes the cursor\r\n * Returns color (fill) of char at the current cursor\r\n * if the text object has a pattern or gradient for filler, it will return that.\r\n * Unused by the library, is for the end user\r\n * @return {String | TFiller} Character color (fill)\r\n */\r\n getCurrentCharColor(): string | TFiller | null {\r\n const cp = this._getCurrentCharIndex();\r\n return this.getValueOfPropertyAt(cp.l, cp.c, FILL);\r\n }\r\n\r\n /**\r\n * Returns the cursor position for the getCurrent.. functions\r\n * @private\r\n */\r\n _getCurrentCharIndex() {\r\n const cursorPosition = this.get2DCursorLocation(this.selectionStart, true),\r\n charIndex =\r\n cursorPosition.charIndex > 0 ? cursorPosition.charIndex - 1 : 0;\r\n return { l: cursorPosition.lineIndex, c: charIndex };\r\n }\r\n\r\n dispose() {\r\n this.exitEditingImpl();\r\n this.draggableTextDelegate.dispose();\r\n super.dispose();\r\n }\r\n}\r\n\r\nclassRegistry.setClass(IText);\r\n// legacy\r\nclassRegistry.setClass(IText, 'i-text');\r\n"],"names":["iTextDefaultValues","selectionStart","selectionEnd","selectionColor","isEditing","editable","editingBorderColor","cursorWidth","cursorColor","cursorDelay","cursorDuration","caching","hiddenTextareaContainer","keysMap","keysMapRtl","ctrlKeysMapDown","ctrlKeysMapUp","_selectionDirection","_reSpace","inCompositionMode","IText","ITextClickBehavior","getDefaults","super","ownDefaults","type","constructor","text","options","this","initBehavior","_set","key","value","_savedProps","canvas","Canvas","textEditingManager","remove","add","setSelectionStart","index","Math","max","_updateAndFire","setSelectionEnd","min","length","property","_fireSelectionChanged","_updateTextarea","fire","target","initDimensions","initDelayedCursor","getSelectionStyles","startIndex","arguments","undefined","endIndex","complete","setSelectionStyles","styles","get2DCursorLocation","skipWrapping","render","ctx","cursorOffsetCache","renderCursorOrSelection","toCanvasElement","clearContextTop","boundaries","_getCursorBoundaries","ancestors","findAncestorsWithClipPath","hasAncestorsWithClipping","drawingCanvas","drawingCtx","createCanvasElementFor","getContext","applyCanvasTransform","m","calcTransformMatrix","transform","renderSelection","renderCursor","ancestor","clipPath","clippingCanvas","clippingCtx","absolutePositioned","drawObject","drawClipPathOnCache","setTransform","drawImage","contextTopDirty","restore","clipPathAncestors","obj","push","parent","skipCaching","enableAdvancedLayout","_getCursorBoundariesAdvanced","_getCursorBoundariesOriginal","_getCursorBoundariesOffsets","__getCursorBoundariesOffsets","_layoutTextAdvanced","layout","cursorRect","getCursorRect","_getAdvancedLayoutOptions","left","_getLeftOffset","top","_getTopOffset","leftOffset","x","topOffset","y","getSelectionStartFromPointer","e","mouseOffset","getScenePoint","invertTransform","Point","hitResult","hitTest","charIndex","_text","offsets","lineIndex","i","getHeightOfLine","lineLeftOffset","_getLineLeftOffset","bound","__charBounds","charSpacing","_textLines","_getWidthOfCharSpacing","direction","textAlign","RIGHT","JUSTIFY","JUSTIFY_RIGHT","LEFT","JUSTIFY_LEFT","CENTER","JUSTIFY_CENTER","renderCursorAt","_renderCursor","contextTop","getCursorRenderingData","cursorLocation","charHeight","getValueOfPropertyAt","multiplier","getObjectScaling","getZoom","dy","_fontSizeFraction","lineHeight","color","opacity","_currentCursorOpacity","width","height","fillStyle","globalAlpha","fillRect","selection","hiddenTextarea","_renderSelection","renderDragSourceEffect","dragStartSelection","draggableTextDelegate","getDragStartSelection","renderDropTargetEffect","dragSelection","isJustify","includes","start","end","startLine","endLine","startChar","endChar","lineOffset","realLineHeight","boxStart","boxEnd","isEndOfWrapping","getLineWidth","drawStart","drawHeight","extraTop","drawWidth","compositionColor","getCurrentCharFontSize","cp","_getCurrentCharIndex","l","c","getCurrentCharColor","FILL","cursorPosition","dispose","exitEditingImpl","_defineProperty","classRegistry","setClass"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IText.min.mjs","sources":["../../../../src/shapes/IText/IText.ts"],"sourcesContent":["import { Canvas } from '../../canvas/Canvas';\r\nimport type { ITextEvents } from './ITextBehavior';\r\nimport { ITextClickBehavior } from './ITextClickBehavior';\r\nimport { getCursorRect } from '../../text/hitTest';\r\nimport { analyzeBiDi } from '../../text/unicode';\r\nimport {\r\n ctrlKeysMapDown,\r\n ctrlKeysMapUp,\r\n keysMap,\r\n keysMapRtl,\r\n} from './constants';\r\nimport type { TClassProperties, TFiller, TOptions } from '../../typedefs';\r\nimport type { TPointerEvent } from '../../EventTypeDefs';\r\nimport { classRegistry } from '../../ClassRegistry';\r\nimport type { SerializedTextProps, TextProps } from '../Text/Text';\r\nimport {\r\n JUSTIFY,\r\n JUSTIFY_CENTER,\r\n JUSTIFY_LEFT,\r\n JUSTIFY_RIGHT,\r\n} from '../Text/constants';\r\nimport { CENTER, FILL, LEFT, RIGHT } from '../../constants';\r\nimport type { ObjectToCanvasElementOptions } from '../Object/Object';\r\nimport type { FabricObject } from '../Object/FabricObject';\r\nimport { createCanvasElementFor } from '../../util/misc/dom';\r\nimport { applyCanvasTransform } from '../../util/internals/applyCanvasTransform';\r\nimport { invertTransform } from '../../util/misc/matrix';\r\n\r\nexport type CursorBoundaries = {\r\n left: number;\r\n top: number;\r\n leftOffset: number;\r\n topOffset: number;\r\n};\r\n\r\nexport type CursorRenderingData = {\r\n color: string;\r\n opacity: number;\r\n left: number;\r\n top: number;\r\n width: number;\r\n height: number;\r\n};\r\n\r\n// Declare IText protected properties to workaround TS\r\nconst protectedDefaultValues = {\r\n _selectionDirection: null,\r\n _reSpace: /\\s|\\r?\\n/,\r\n inCompositionMode: false,\r\n};\r\n\r\nexport const iTextDefaultValues: Partial<TClassProperties<IText>> = {\r\n selectionStart: 0,\r\n selectionEnd: 0,\r\n selectionColor: 'rgba(17,119,255,0.3)',\r\n isEditing: false,\r\n editable: true,\r\n editingBorderColor: 'rgba(102,153,255,0.25)',\r\n cursorWidth: 2,\r\n cursorColor: '',\r\n cursorDelay: 1000,\r\n cursorDuration: 600,\r\n caching: true,\r\n hiddenTextareaContainer: null,\r\n keysMap,\r\n keysMapRtl,\r\n ctrlKeysMapDown,\r\n ctrlKeysMapUp,\r\n ...protectedDefaultValues,\r\n};\r\n\r\n// @TODO this is not complete\r\ninterface UniqueITextProps {\r\n selectionStart: number;\r\n selectionEnd: number;\r\n}\r\n\r\nexport interface SerializedITextProps\r\n extends SerializedTextProps,\r\n UniqueITextProps { }\r\n\r\nexport interface ITextProps extends TextProps, UniqueITextProps { }\r\n\r\n/**\r\n * @fires changed\r\n * @fires selection:changed\r\n * @fires editing:entered\r\n * @fires editing:exited\r\n * @fires dragstart\r\n * @fires drag drag event firing on the drag source\r\n * @fires dragend\r\n * @fires copy\r\n * @fires cut\r\n * @fires paste\r\n *\r\n * #### Supported key combinations\r\n * ```\r\n * Move cursor: left, right, up, down\r\n * Select character: shift + left, shift + right\r\n * Select text vertically: shift + up, shift + down\r\n * Move cursor by word: alt + left, alt + right\r\n * Select words: shift + alt + left, shift + alt + right\r\n * Move cursor to line start/end: cmd + left, cmd + right or home, end\r\n * Select till start/end of line: cmd + shift + left, cmd + shift + right or shift + home, shift + end\r\n * Jump to start/end of text: cmd + up, cmd + down\r\n * Select till start/end of text: cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown\r\n * Delete character: backspace\r\n * Delete word: alt + backspace\r\n * Delete line: cmd + backspace\r\n * Forward delete: delete\r\n * Copy text: ctrl/cmd + c\r\n * Paste text: ctrl/cmd + v\r\n * Cut text: ctrl/cmd + x\r\n * Select entire text: ctrl/cmd + a\r\n * Quit editing tab or esc\r\n * ```\r\n *\r\n * #### Supported mouse/touch combination\r\n * ```\r\n * Position cursor: click/touch\r\n * Create selection: click/touch & drag\r\n * Create selection: click & shift + click\r\n * Select word: double click\r\n * Select line: triple click\r\n * ```\r\n */\r\nexport class IText<\r\n Props extends TOptions<ITextProps> = Partial<ITextProps>,\r\n SProps extends SerializedITextProps = SerializedITextProps,\r\n EventSpec extends ITextEvents = ITextEvents,\r\n>\r\n extends ITextClickBehavior<Props, SProps, EventSpec>\r\n implements UniqueITextProps {\r\n /**\r\n * Index where text selection starts (or where cursor is when there is no selection)\r\n * @type Number\r\n */\r\n declare selectionStart: number;\r\n\r\n /**\r\n * Index where text selection ends\r\n * @type Number\r\n */\r\n declare selectionEnd: number;\r\n\r\n /**\r\n * Cache for visual positions per line to ensure consistency\r\n * during selection operations\r\n * @private\r\n */\r\n private _visualPositionsCache: Map<number, Array<{\r\n logicalIndex: number;\r\n visualX: number;\r\n width: number;\r\n isRtl: boolean;\r\n }>> = new Map();\r\n\r\n declare compositionStart: number;\r\n\r\n declare compositionEnd: number;\r\n\r\n /**\r\n * Color of text selection\r\n * @type String\r\n */\r\n declare selectionColor: string;\r\n\r\n /**\r\n * Indicates whether text is in editing mode\r\n * @type Boolean\r\n */\r\n declare isEditing: boolean;\r\n\r\n /**\r\n * Indicates whether a text can be edited\r\n * @type Boolean\r\n */\r\n declare editable: boolean;\r\n\r\n /**\r\n * Border color of text object while it's in editing mode\r\n * @type String\r\n */\r\n declare editingBorderColor: string;\r\n\r\n /**\r\n * Width of cursor (in px)\r\n * @type Number\r\n */\r\n declare cursorWidth: number;\r\n\r\n /**\r\n * Color of text cursor color in editing mode.\r\n * if not set (default) will take color from the text.\r\n * if set to a color value that fabric can understand, it will\r\n * be used instead of the color of the text at the current position.\r\n * @type String\r\n */\r\n declare cursorColor: string;\r\n\r\n /**\r\n * Delay between cursor blink (in ms)\r\n * @type Number\r\n */\r\n declare cursorDelay: number;\r\n\r\n /**\r\n * Duration of cursor fade in (in ms)\r\n * @type Number\r\n */\r\n declare cursorDuration: number;\r\n\r\n declare compositionColor: string;\r\n\r\n /**\r\n * Indicates whether internal text char widths can be cached\r\n * @type Boolean\r\n */\r\n declare caching: boolean;\r\n\r\n static ownDefaults = iTextDefaultValues;\r\n\r\n static getDefaults(): Record<string, any> {\r\n return { ...super.getDefaults(), ...IText.ownDefaults };\r\n }\r\n\r\n static type = 'IText';\r\n\r\n get type() {\r\n const type = super.type;\r\n // backward compatibility\r\n return type === 'itext' ? 'i-text' : type;\r\n }\r\n\r\n /**\r\n * Constructor\r\n * @param {String} text Text string\r\n * @param {Object} [options] Options object\r\n */\r\n constructor(text: string, options?: Props) {\r\n super(text, { ...IText.ownDefaults, ...options } as Props);\r\n this.initBehavior();\r\n }\r\n\r\n /**\r\n * While editing handle differently\r\n * @private\r\n * @param {string} key\r\n * @param {*} value\r\n */\r\n _set(key: string, value: any) {\r\n if (this.isEditing && this._savedProps && key in this._savedProps) {\r\n // @ts-expect-error irritating TS\r\n this._savedProps[key] = value;\r\n return this;\r\n }\r\n if (key === 'canvas') {\r\n this.canvas instanceof Canvas &&\r\n this.canvas.textEditingManager.remove(this);\r\n value instanceof Canvas && value.textEditingManager.add(this);\r\n }\r\n return super._set(key, value);\r\n }\r\n\r\n /**\r\n * Sets selection start (left boundary of a selection)\r\n * @param {Number} index Index to set selection start to\r\n */\r\n setSelectionStart(index: number) {\r\n index = Math.max(index, 0);\r\n this._updateAndFire('selectionStart', index);\r\n }\r\n\r\n /**\r\n * Sets selection end (right boundary of a selection)\r\n * @param {Number} index Index to set selection end to\r\n */\r\n setSelectionEnd(index: number) {\r\n index = Math.min(index, this.text.length);\r\n this._updateAndFire('selectionEnd', index);\r\n }\r\n\r\n /**\r\n * @private\r\n * @param {String} property 'selectionStart' or 'selectionEnd'\r\n * @param {Number} index new position of property\r\n */\r\n protected _updateAndFire(\r\n property: 'selectionStart' | 'selectionEnd',\r\n index: number,\r\n ) {\r\n if (this[property] !== index) {\r\n this._fireSelectionChanged();\r\n this[property] = index;\r\n }\r\n this._updateTextarea();\r\n }\r\n\r\n /**\r\n * Fires the even of selection changed\r\n * @private\r\n */\r\n _fireSelectionChanged() {\r\n this.fire('selection:changed');\r\n this.canvas && this.canvas.fire('text:selection:changed', { target: this });\r\n }\r\n\r\n /**\r\n * Initialize text dimensions. Render all text on given context\r\n * or on a offscreen canvas to get the text width with measureText.\r\n * Updates this.width and this.height with the proper values.\r\n * Does not return dimensions.\r\n * @private\r\n */\r\n initDimensions() {\r\n this.isEditing && this.initDelayedCursor();\r\n super.initDimensions();\r\n }\r\n\r\n /**\r\n * Gets style of a current selection/cursor (at the start position)\r\n * if startIndex or endIndex are not provided, selectionStart or selectionEnd will be used.\r\n * @param {Number} startIndex Start index to get styles at\r\n * @param {Number} endIndex End index to get styles at, if not specified selectionEnd or startIndex + 1\r\n * @param {Boolean} [complete] get full style or not\r\n * @return {Array} styles an array with one, zero or more Style objects\r\n */\r\n getSelectionStyles(\r\n startIndex: number = this.selectionStart || 0,\r\n endIndex: number = this.selectionEnd,\r\n complete?: boolean,\r\n ) {\r\n return super.getSelectionStyles(startIndex, endIndex, complete);\r\n }\r\n\r\n /**\r\n * Sets style of a current selection, if no selection exist, do not set anything.\r\n * @param {Object} [styles] Styles object\r\n * @param {Number} [startIndex] Start index to get styles at\r\n * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1\r\n */\r\n setSelectionStyles(\r\n styles: object,\r\n startIndex: number = this.selectionStart || 0,\r\n endIndex: number = this.selectionEnd,\r\n ) {\r\n return super.setSelectionStyles(styles, startIndex, endIndex);\r\n }\r\n\r\n /**\r\n * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)\r\n * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.\r\n * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. useful to manage styles.\r\n */\r\n get2DCursorLocation(\r\n selectionStart = this.selectionStart,\r\n skipWrapping?: boolean,\r\n ) {\r\n return super.get2DCursorLocation(selectionStart, skipWrapping);\r\n }\r\n\r\n /**\r\n * @private\r\n * @param {CanvasRenderingContext2D} ctx Context to render on\r\n */\r\n render(ctx: CanvasRenderingContext2D) {\r\n super.render(ctx);\r\n // clear the cursorOffsetCache, so we ensure to calculate once per renderCursor\r\n // the correct position but not at every cursor animation.\r\n this.cursorOffsetCache = {};\r\n // Clear visual positions cache on full render since dimensions may have changed\r\n this._clearVisualPositionsCache();\r\n this.renderCursorOrSelection();\r\n }\r\n\r\n /**\r\n * @override block cursor/selection logic while rendering the exported canvas\r\n * @todo this workaround should be replaced with a more robust solution\r\n */\r\n toCanvasElement(options?: ObjectToCanvasElementOptions): HTMLCanvasElement {\r\n const isEditing = this.isEditing;\r\n this.isEditing = false;\r\n const canvas = super.toCanvasElement(options);\r\n this.isEditing = isEditing;\r\n return canvas;\r\n }\r\n\r\n /**\r\n * Renders cursor or selection (depending on what exists)\r\n * it does on the contextTop. If contextTop is not available, do nothing.\r\n */\r\n renderCursorOrSelection() {\r\n if (!this.isEditing || !this.canvas) {\r\n return;\r\n }\r\n const ctx = this.clearContextTop(true);\r\n if (!ctx) {\r\n return;\r\n }\r\n // Clear cache to ensure fresh cursor position calculation\r\n // This is important during selection drag when positions change frequently\r\n this.cursorOffsetCache = {};\r\n const boundaries = this._getCursorBoundaries();\r\n\r\n const ancestors = this.findAncestorsWithClipPath();\r\n const hasAncestorsWithClipping = ancestors.length > 0;\r\n let drawingCtx: CanvasRenderingContext2D = ctx;\r\n let drawingCanvas: HTMLCanvasElement | undefined = undefined;\r\n if (hasAncestorsWithClipping) {\r\n // we have some clipPath, we need to draw the selection on an intermediate layer.\r\n drawingCanvas = createCanvasElementFor(ctx.canvas);\r\n drawingCtx = drawingCanvas.getContext('2d')!;\r\n applyCanvasTransform(drawingCtx, this.canvas);\r\n const m = this.calcTransformMatrix();\r\n drawingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\r\n }\r\n\r\n if (this.selectionStart === this.selectionEnd && !this.inCompositionMode) {\r\n this.renderCursor(drawingCtx, boundaries);\r\n } else {\r\n this.renderSelection(drawingCtx, boundaries);\r\n }\r\n\r\n if (hasAncestorsWithClipping) {\r\n // we need a neutral context.\r\n // this won't work for nested clippaths in which a clippath\r\n // has its own clippath\r\n for (const ancestor of ancestors) {\r\n const clipPath = ancestor.clipPath!;\r\n const clippingCanvas = createCanvasElementFor(ctx.canvas);\r\n const clippingCtx = clippingCanvas.getContext('2d')!;\r\n applyCanvasTransform(clippingCtx, this.canvas);\r\n // position the ctx in the center of the outer ancestor\r\n if (!clipPath.absolutePositioned) {\r\n const m = ancestor.calcTransformMatrix();\r\n clippingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\r\n }\r\n clipPath.transform(clippingCtx);\r\n // we assign an empty drawing context, we don't plan to have this working for nested clippaths for now\r\n clipPath.drawObject(clippingCtx, true, {});\r\n this.drawClipPathOnCache(drawingCtx, clipPath, clippingCanvas);\r\n }\r\n }\r\n\r\n if (hasAncestorsWithClipping) {\r\n ctx.setTransform(1, 0, 0, 1, 0, 0);\r\n ctx.drawImage(drawingCanvas!, 0, 0);\r\n }\r\n\r\n this.canvas.contextTopDirty = true;\r\n ctx.restore();\r\n }\r\n\r\n /**\r\n * Finds and returns an array of clip paths that are applied to the parent\r\n * group(s) of the current FabricObject instance. The object's hierarchy is\r\n * traversed upwards (from the current object towards the root of the canvas),\r\n * checking each parent object for the presence of a `clipPath` that is not\r\n * absolutely positioned.\r\n */\r\n findAncestorsWithClipPath(): FabricObject[] {\r\n const clipPathAncestors: FabricObject[] = [];\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n let obj: FabricObject | undefined = this;\r\n while (obj) {\r\n if (obj.clipPath) {\r\n clipPathAncestors.push(obj);\r\n }\r\n obj = obj.parent;\r\n }\r\n\r\n return clipPathAncestors;\r\n }\r\n\r\n /**\r\n * Returns cursor boundaries (left, top, leftOffset, topOffset)\r\n * left/top are left/top of entire text box\r\n * leftOffset/topOffset are offset from that left/top point of a text box\r\n * @private\r\n * @param {number} [index] index from start\r\n * @param {boolean} [skipCaching]\r\n */\r\n _getCursorBoundaries(\r\n index: number = this.selectionStart,\r\n skipCaching?: boolean,\r\n ): CursorBoundaries {\r\n // Always use original method which uses __charBounds directly\r\n // and has proper RTL handling built-in\r\n return this._getCursorBoundariesOriginal(index, skipCaching);\r\n }\r\n\r\n\r\n /**\r\n * Caches and returns cursor left/top offset relative to instance's center point\r\n * @private\r\n * @param {number} index index from start\r\n * @param {boolean} [skipCaching]\r\n */\r\n _getCursorBoundariesOffsets(\r\n index: number,\r\n skipCaching?: boolean,\r\n ): { left: number; top: number } {\r\n if (skipCaching) {\r\n return this.__getCursorBoundariesOffsets(index);\r\n }\r\n if (this.cursorOffsetCache && 'top' in this.cursorOffsetCache) {\r\n return this.cursorOffsetCache as { left: number; top: number };\r\n }\r\n return (this.cursorOffsetCache = this.__getCursorBoundariesOffsets(index));\r\n }\r\n\r\n /**\r\n * Enhanced cursor boundaries using advanced hit testing when available\r\n * @private\r\n */\r\n _getCursorBoundariesAdvanced(index: number): CursorBoundaries {\r\n if (!this.enableAdvancedLayout || !(this as any)._layoutTextAdvanced) {\r\n return this._getCursorBoundariesOriginal(index);\r\n }\r\n\r\n const layout = (this as any)._layoutTextAdvanced();\r\n const cursorRect = getCursorRect(index, layout, (this as any)._getAdvancedLayoutOptions());\r\n\r\n return {\r\n left: this._getLeftOffset(),\r\n top: this._getTopOffset(),\r\n leftOffset: cursorRect.x,\r\n topOffset: cursorRect.y,\r\n };\r\n }\r\n\r\n /**\r\n * Override selection to use measureText-based visual positions\r\n * This ensures hit testing matches actual browser BiDi rendering\r\n */\r\n getSelectionStartFromPointer(e: TPointerEvent): number {\r\n // Get mouse position in object-local coordinates (origin at center)\r\n const scenePoint = this.canvas!.getScenePoint(e);\r\n const localPoint = scenePoint.transform(invertTransform(this.calcTransformMatrix()));\r\n\r\n // Convert to top-left origin coordinates\r\n const mouseX = localPoint.x + this.width / 2;\r\n const mouseY = localPoint.y + this.height / 2;\r\n\r\n // Find the line based on Y position\r\n let height = 0, lineIndex = 0;\r\n for (let i = 0; i < this._textLines.length; i++) {\r\n const lineHeight = this.getHeightOfLine(i);\r\n if (mouseY >= height && mouseY < height + lineHeight) {\r\n lineIndex = i;\r\n break;\r\n }\r\n height += lineHeight;\r\n if (i === this._textLines.length - 1) {\r\n lineIndex = i;\r\n }\r\n }\r\n\r\n // Calculate line start index using ORIGINAL line lengths (without tatweels)\r\n // This ensures selection indices refer to the original text, not the display text\r\n let lineStartIndex = 0;\r\n for (let i = 0; i < lineIndex; i++) {\r\n const origLen = this._getOriginalLineLength(i);\r\n const newlineOffset = this.missingNewlineOffset(i);\r\n console.log(`📍 Line ${i}: origLen=${origLen}, displayLen=${this._textLines[i].length}, tatweels=${this._getTatweelCountForLine(i)}, newlineOffset=${newlineOffset}`);\r\n lineStartIndex += origLen + newlineOffset;\r\n }\r\n console.log(`📍 Click on line ${lineIndex}, lineStartIndex=${lineStartIndex}`);\r\n\r\n const line = this._textLines[lineIndex];\r\n const lineText = line.join('');\r\n const displayCharLength = line.length;\r\n const originalCharLength = this._getOriginalLineLength(lineIndex);\r\n\r\n if (displayCharLength === 0) {\r\n return lineStartIndex;\r\n }\r\n\r\n // Use measureText to get actual visual character positions\r\n // This matches exactly how the canvas renders BiDi text\r\n const visualPositions = this._measureVisualPositions(lineIndex, lineText);\r\n\r\n // Calculate line offset based on alignment\r\n const lineWidth = this.getLineWidth(lineIndex);\r\n let lineStartX = 0;\r\n\r\n if (this.textAlign === 'center' || this.textAlign === 'justify-center') {\r\n lineStartX = (this.width - lineWidth) / 2;\r\n } else if (this.textAlign === 'right' || this.textAlign === 'justify-right') {\r\n lineStartX = this.width - lineWidth;\r\n } else if (this.direction === 'rtl' && (this.textAlign === 'justify' || this.textAlign === 'left')) {\r\n // For RTL with left/justify, text starts from right\r\n lineStartX = this.width - lineWidth;\r\n }\r\n\r\n // Find which character was clicked based on visual position\r\n const clickX = mouseX - lineStartX;\r\n\r\n // Sort positions by visual X for hit testing\r\n const sortedPositions = [...visualPositions].sort((a, b) => a.visualX - b.visualX);\r\n\r\n // Handle click before first character\r\n if (sortedPositions.length > 0 && clickX < sortedPositions[0].visualX) {\r\n // Before first visual character - cursor at visual left edge\r\n // For RTL base direction, this means logical end of line\r\n return this.direction === 'rtl'\r\n ? lineStartIndex + originalCharLength\r\n : lineStartIndex;\r\n }\r\n\r\n // Handle click after last character\r\n if (sortedPositions.length > 0) {\r\n const lastPos = sortedPositions[sortedPositions.length - 1];\r\n if (clickX > lastPos.visualX + lastPos.width) {\r\n // After last visual character - cursor at visual right edge\r\n // For RTL base direction, this means logical start of line\r\n return this.direction === 'rtl'\r\n ? lineStartIndex\r\n : lineStartIndex + originalCharLength;\r\n }\r\n }\r\n\r\n // Find the character at click position\r\n for (let i = 0; i < sortedPositions.length; i++) {\r\n const pos = sortedPositions[i];\r\n const charEnd = pos.visualX + pos.width;\r\n\r\n if (clickX >= pos.visualX && clickX <= charEnd) {\r\n // Convert display index to original index\r\n // This also handles tatweels - they map to the character they extend\r\n const originalCharIndex = this._displayToOriginalIndex(lineIndex, pos.logicalIndex);\r\n\r\n // Check if this is a tatweel - if so, treat click as clicking on the extended character\r\n const isTatweel = this._isTatweelAtDisplayIndex(lineIndex, pos.logicalIndex);\r\n\r\n console.log(`📍 Hit char: displayIdx=${pos.logicalIndex}, origIdx=${originalCharIndex}, isTatweel=${isTatweel}, char=\"${this._textLines[lineIndex][pos.logicalIndex]}\"`);\r\n\r\n const charMiddle = pos.visualX + pos.width / 2;\r\n const clickedLeftHalf = clickX <= charMiddle;\r\n\r\n // For tatweels, clicking anywhere on it should place cursor after the extended character\r\n if (isTatweel) {\r\n // Tatweel extends the character before it, so cursor goes after that character\r\n // originalCharIndex from _displayToOriginalIndex already maps tatweel to char+1\r\n const result = lineStartIndex + originalCharIndex;\r\n console.log(`📍 Tatweel click result: ${result}`);\r\n return result;\r\n }\r\n\r\n // For RTL characters: left visual half means cursor AFTER (higher logical index)\r\n // For LTR characters: left visual half means cursor BEFORE (lower logical index)\r\n if (pos.isRtl) {\r\n // RTL character\r\n const result = lineStartIndex + (clickedLeftHalf ? originalCharIndex + 1 : originalCharIndex);\r\n console.log(`📍 RTL char result: ${result} (clickedLeftHalf=${clickedLeftHalf})`);\r\n return result;\r\n } else {\r\n // LTR character\r\n const result = lineStartIndex + (clickedLeftHalf ? originalCharIndex : originalCharIndex + 1);\r\n console.log(`📍 LTR char result: ${result} (clickedLeftHalf=${clickedLeftHalf})`);\r\n return result;\r\n }\r\n }\r\n }\r\n\r\n // console.log(`📍 No match, returning end: ${lineStartIndex + originalCharLength}`);\r\n return lineStartIndex + originalCharLength;\r\n }\r\n\r\n /**\r\n * Clear the visual positions cache\r\n * Should be called when text content or dimensions change\r\n */\r\n _clearVisualPositionsCache() {\r\n this._visualPositionsCache.clear();\r\n }\r\n\r\n /**\r\n * Measure visual character positions for hit testing using BiDi analysis\r\n * This properly handles mixed RTL/LTR text by analyzing BiDi runs\r\n * Results are cached per line for consistency during selection operations\r\n */\r\n _measureVisualPositions(lineIndex: number, lineText: string): Array<{\r\n logicalIndex: number;\r\n visualX: number;\r\n width: number;\r\n isRtl: boolean; // Direction of this character's run\r\n }> {\r\n // Check cache first\r\n if (this._visualPositionsCache.has(lineIndex)) {\r\n return this._visualPositionsCache.get(lineIndex)!;\r\n }\r\n\r\n const line = this._textLines[lineIndex];\r\n const positions: Array<{logicalIndex: number; visualX: number; width: number; isRtl: boolean}> = [];\r\n\r\n const chars = this.__charBounds[lineIndex];\r\n if (!chars || chars.length === 0) {\r\n this._visualPositionsCache.set(lineIndex, positions);\r\n return positions;\r\n }\r\n\r\n // For LTR direction, use logical positions directly\r\n if (this.direction !== 'rtl') {\r\n for (let i = 0; i < line.length; i++) {\r\n positions.push({\r\n logicalIndex: i,\r\n visualX: chars[i]?.left || 0,\r\n width: chars[i]?.kernedWidth || 0,\r\n isRtl: false,\r\n });\r\n }\r\n this._visualPositionsCache.set(lineIndex, positions);\r\n return positions;\r\n }\r\n\r\n // For RTL, use BiDi analysis to determine visual positions\r\n const runs = analyzeBiDi(lineText, 'rtl');\r\n\r\n // Build mapping from string position to grapheme index\r\n // This is needed because analyzeBiDi works on string positions (code points)\r\n // but we need grapheme indices for charBounds\r\n const stringPosToGrapheme: number[] = [];\r\n let strPos = 0;\r\n for (let gi = 0; gi < line.length; gi++) {\r\n const grapheme = line[gi];\r\n for (let j = 0; j < grapheme.length; j++) {\r\n stringPosToGrapheme[strPos + j] = gi;\r\n }\r\n strPos += grapheme.length;\r\n }\r\n\r\n // Calculate width for each run\r\n interface RunInfo {\r\n run: typeof runs[0];\r\n width: number;\r\n charIndices: number[];\r\n }\r\n\r\n const runInfos: RunInfo[] = [];\r\n\r\n for (const run of runs) {\r\n const runChars: number[] = [];\r\n let runWidth = 0;\r\n const seenGraphemes = new Set<number>();\r\n\r\n // Map string positions in this run to grapheme indices\r\n for (let sp = run.start; sp < run.end; sp++) {\r\n const gi = stringPosToGrapheme[sp];\r\n if (gi !== undefined && !seenGraphemes.has(gi)) {\r\n seenGraphemes.add(gi);\r\n runChars.push(gi);\r\n runWidth += chars[gi]?.kernedWidth || 0;\r\n }\r\n }\r\n\r\n runInfos.push({\r\n run,\r\n width: runWidth,\r\n charIndices: runChars,\r\n });\r\n }\r\n\r\n // For RTL base direction, runs are displayed right-to-left\r\n // So first run appears on the right, last run on the left\r\n const totalWidth = this.getLineWidth(lineIndex);\r\n let visualX = totalWidth; // Start from right edge\r\n\r\n for (const runInfo of runInfos) {\r\n visualX -= runInfo.width; // Move left by run width\r\n\r\n const isRtlRun = runInfo.run.direction === 'rtl';\r\n if (isRtlRun) {\r\n // RTL run: characters displayed right-to-left within run\r\n // First char of run at visual right of run, last at visual left\r\n let charX = visualX + runInfo.width;\r\n for (const idx of runInfo.charIndices) {\r\n const charWidth = chars[idx]?.kernedWidth || 0;\r\n charX -= charWidth;\r\n positions.push({\r\n logicalIndex: idx,\r\n visualX: charX,\r\n width: charWidth,\r\n isRtl: true,\r\n });\r\n }\r\n } else {\r\n // LTR run: characters displayed left-to-right within run\r\n // First char of run at visual left of run, last at visual right\r\n let charX = visualX;\r\n for (const idx of runInfo.charIndices) {\r\n const charWidth = chars[idx]?.kernedWidth || 0;\r\n positions.push({\r\n logicalIndex: idx,\r\n visualX: charX,\r\n width: charWidth,\r\n isRtl: false,\r\n });\r\n charX += charWidth;\r\n }\r\n }\r\n }\r\n\r\n // Cache the result\r\n this._visualPositionsCache.set(lineIndex, positions);\r\n return positions;\r\n }\r\n\r\n /**\r\n * Original cursor boundaries implementation\r\n * @private\r\n */\r\n _getCursorBoundariesOriginal(index: number, skipCaching?: boolean): CursorBoundaries {\r\n const left = this._getLeftOffset(),\r\n top = this._getTopOffset(),\r\n offsets = this._getCursorBoundariesOffsets(index, skipCaching);\r\n return {\r\n left: left,\r\n top: top,\r\n leftOffset: offsets.left,\r\n topOffset: offsets.top,\r\n };\r\n }\r\n\r\n /**\r\n * Calculates cursor left/top offset relative to _getLeftOffset()\r\n * Uses visual positions for BiDi text support\r\n * Handles kashida by converting original indices to display indices\r\n * @private\r\n * @param {number} index index from start (in original text space, without tatweels)\r\n */\r\n __getCursorBoundariesOffsets(index: number) {\r\n let topOffset = 0;\r\n\r\n // Find line index and original char index using original line lengths\r\n let lineIndex = 0;\r\n let originalCharIndex = index;\r\n\r\n for (let i = 0; i < this._textLines.length; i++) {\r\n const originalLineLength = this._getOriginalLineLength(i);\r\n if (originalCharIndex <= originalLineLength) {\r\n lineIndex = i;\r\n break;\r\n }\r\n originalCharIndex -= originalLineLength + this.missingNewlineOffset(i);\r\n lineIndex = i + 1;\r\n }\r\n\r\n // Clamp lineIndex to valid range\r\n if (lineIndex >= this._textLines.length) {\r\n lineIndex = this._textLines.length - 1;\r\n originalCharIndex = this._getOriginalLineLength(lineIndex);\r\n }\r\n\r\n for (let i = 0; i < lineIndex; i++) {\r\n topOffset += this.getHeightOfLine(i);\r\n }\r\n\r\n // Convert original char index to display char index for visual lookup\r\n const displayCharIndex = this._originalToDisplayIndex(lineIndex, originalCharIndex);\r\n\r\n // Get visual positions for cursor placement\r\n const lineText = this._textLines[lineIndex].join('');\r\n const visualPositions = this._measureVisualPositions(lineIndex, lineText);\r\n const lineWidth = this.getLineWidth(lineIndex);\r\n const displayLineLength = this._textLines[lineIndex].length;\r\n const originalLineLength = this._getOriginalLineLength(lineIndex);\r\n\r\n // Find visual X position for cursor (0 to lineWidth, from visual left)\r\n let visualX = 0;\r\n\r\n if (visualPositions.length === 0) {\r\n // Fallback for empty line\r\n return { top: topOffset, left: 0 };\r\n }\r\n\r\n if (originalCharIndex === 0) {\r\n // Cursor at logical start\r\n // For RTL base direction, logical start is at visual right\r\n if (this.direction === 'rtl') {\r\n visualX = lineWidth; // Right edge\r\n } else {\r\n visualX = 0; // Left edge\r\n }\r\n } else if (originalCharIndex >= originalLineLength) {\r\n // Cursor at logical end\r\n // For RTL base direction, logical end is at visual left\r\n if (this.direction === 'rtl') {\r\n visualX = 0; // Left edge\r\n } else {\r\n visualX = lineWidth; // Right edge\r\n }\r\n } else {\r\n // Cursor between characters - find visual position of character at displayCharIndex\r\n const charPos = visualPositions.find(p => p.logicalIndex === displayCharIndex);\r\n if (charPos) {\r\n // Use character's direction to determine cursor position\r\n // For RTL char: cursor \"before\" it appears at its right visual edge\r\n // For LTR char: cursor \"before\" it appears at its left visual edge\r\n if (charPos.isRtl) {\r\n visualX = charPos.visualX + charPos.width;\r\n } else {\r\n visualX = charPos.visualX;\r\n }\r\n } else {\r\n // Fallback - try the previous character in display space\r\n const prevDisplayIndex = displayCharIndex > 0 ? displayCharIndex - 1 : 0;\r\n const prevCharPos = visualPositions.find(p => p.logicalIndex === prevDisplayIndex);\r\n if (prevCharPos) {\r\n // Cursor after previous character\r\n if (prevCharPos.isRtl) {\r\n visualX = prevCharPos.visualX;\r\n } else {\r\n visualX = prevCharPos.visualX + prevCharPos.width;\r\n }\r\n } else {\r\n // Ultimate fallback\r\n const bound = this.__charBounds[lineIndex][displayCharIndex];\r\n visualX = bound?.left || 0;\r\n }\r\n }\r\n }\r\n\r\n // Calculate alignment offset (how much line is shifted from left edge)\r\n let alignOffset = 0;\r\n if (this.textAlign === 'center' || this.textAlign === 'justify-center') {\r\n alignOffset = (this.width - lineWidth) / 2;\r\n } else if (this.textAlign === 'right' || this.textAlign === 'justify-right') {\r\n alignOffset = this.width - lineWidth;\r\n } else if (this.direction === 'rtl' && (this.textAlign === 'justify' || this.textAlign === 'left')) {\r\n alignOffset = this.width - lineWidth;\r\n }\r\n\r\n // The returned left value is added to _getLeftOffset() in _getCursorBoundaries\r\n // _getLeftOffset() returns -width/2 for LTR, +width/2 for RTL\r\n // Final cursor X = _getLeftOffset() + leftOffset\r\n //\r\n // For LTR: cursor X = -width/2 + (alignOffset + visualX)\r\n // For RTL: cursor X = +width/2 + leftOffset\r\n // We want cursor at: -width/2 + alignOffset + visualX\r\n // So leftOffset = -width/2 + alignOffset + visualX - width/2 = alignOffset + visualX - width\r\n\r\n let leftOffset: number;\r\n if (this.direction === 'rtl') {\r\n // For RTL, _getLeftOffset() = +width/2\r\n // We want final X = -width/2 + alignOffset + visualX\r\n // So: +width/2 + leftOffset = -width/2 + alignOffset + visualX\r\n // leftOffset = -width + alignOffset + visualX\r\n leftOffset = -this.width + alignOffset + visualX;\r\n } else {\r\n // For LTR, _getLeftOffset() = -width/2\r\n // We want final X = -width/2 + alignOffset + visualX\r\n // So: -width/2 + leftOffset = -width/2 + alignOffset + visualX\r\n // leftOffset = alignOffset + visualX\r\n leftOffset = alignOffset + visualX;\r\n }\r\n\r\n return {\r\n top: topOffset,\r\n left: leftOffset,\r\n };\r\n }\r\n\r\n /**\r\n * Renders cursor on context Top, outside the animation cycle, on request\r\n * Used for the drag/drop effect.\r\n * If contextTop is not available, do nothing.\r\n */\r\n renderCursorAt(selectionStart: number) {\r\n this._renderCursor(\r\n this.canvas!.contextTop,\r\n this._getCursorBoundaries(selectionStart, true),\r\n selectionStart,\r\n );\r\n }\r\n\r\n /**\r\n * Renders cursor\r\n * @param {Object} boundaries\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n renderCursor(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\r\n this._renderCursor(ctx, boundaries, this.selectionStart);\r\n }\r\n\r\n /**\r\n * Return the data needed to render the cursor for given selection start\r\n * The left,top are relative to the object, while width and height are prescaled\r\n * to look think with canvas zoom and object scaling,\r\n * so they depend on canvas and object scaling\r\n */\r\n getCursorRenderingData(\r\n selectionStart: number = this.selectionStart,\r\n boundaries: CursorBoundaries = this._getCursorBoundaries(selectionStart),\r\n ): CursorRenderingData {\r\n const cursorLocation = this.get2DCursorLocation(selectionStart),\r\n lineIndex = cursorLocation.lineIndex,\r\n charIndex =\r\n cursorLocation.charIndex > 0 ? cursorLocation.charIndex - 1 : 0,\r\n charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize'),\r\n multiplier = this.getObjectScaling().x * this.canvas!.getZoom(),\r\n cursorWidth = this.cursorWidth / multiplier,\r\n dy = this.getValueOfPropertyAt(lineIndex, charIndex, 'deltaY'),\r\n topOffset =\r\n boundaries.topOffset +\r\n ((1 - this._fontSizeFraction) * this.getHeightOfLine(lineIndex)) /\r\n this.lineHeight -\r\n charHeight * (1 - this._fontSizeFraction);\r\n\r\n return {\r\n color:\r\n this.cursorColor ||\r\n (this.getValueOfPropertyAt(lineIndex, charIndex, 'fill') as string),\r\n opacity: this._currentCursorOpacity,\r\n left: boundaries.left + boundaries.leftOffset - cursorWidth / 2,\r\n top: topOffset + boundaries.top + dy,\r\n width: cursorWidth,\r\n height: charHeight,\r\n };\r\n }\r\n\r\n /**\r\n * Render the cursor at the given selectionStart.\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n _renderCursor(\r\n ctx: CanvasRenderingContext2D,\r\n boundaries: CursorBoundaries,\r\n selectionStart: number,\r\n ) {\r\n const { color, opacity, left, top, width, height } =\r\n this.getCursorRenderingData(selectionStart, boundaries);\r\n ctx.fillStyle = color;\r\n ctx.globalAlpha = opacity;\r\n ctx.fillRect(left, top, width, height);\r\n }\r\n\r\n /**\r\n * Renders text selection\r\n * @param {Object} boundaries Object with left/top/leftOffset/topOffset\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n renderSelection(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\r\n const selection = {\r\n selectionStart: this.inCompositionMode\r\n ? this.hiddenTextarea!.selectionStart\r\n : this.selectionStart,\r\n selectionEnd: this.inCompositionMode\r\n ? this.hiddenTextarea!.selectionEnd\r\n : this.selectionEnd,\r\n };\r\n this._renderSelection(ctx, selection, boundaries);\r\n }\r\n\r\n /**\r\n * Renders drag start text selection\r\n */\r\n renderDragSourceEffect() {\r\n const dragStartSelection =\r\n this.draggableTextDelegate.getDragStartSelection()!;\r\n this._renderSelection(\r\n this.canvas!.contextTop,\r\n dragStartSelection,\r\n this._getCursorBoundaries(dragStartSelection.selectionStart, true),\r\n );\r\n }\r\n\r\n renderDropTargetEffect(e: DragEvent) {\r\n const dragSelection = this.getSelectionStartFromPointer(e);\r\n this.renderCursorAt(dragSelection);\r\n }\r\n\r\n /**\r\n * Renders text selection using visual positions for BiDi support\r\n * Handles kashida by converting original indices to display indices\r\n * @private\r\n * @param {{ selectionStart: number, selectionEnd: number }} selection (in original text space)\r\n * @param {Object} boundaries Object with left/top/leftOffset/topOffset\r\n * @param {CanvasRenderingContext2D} ctx transformed context to draw on\r\n */\r\n _renderSelection(\r\n ctx: CanvasRenderingContext2D,\r\n selection: { selectionStart: number; selectionEnd: number },\r\n boundaries: CursorBoundaries,\r\n ) {\r\n const selectionStart = selection.selectionStart,\r\n selectionEnd = selection.selectionEnd,\r\n isJustify = this.textAlign.includes(JUSTIFY);\r\n\r\n // Convert selection indices to line/char using original text space\r\n // This handles kashida properly since selection indices don't include tatweels\r\n let startLine = 0, endLine = 0;\r\n let originalStartChar = selectionStart, originalEndChar = selectionEnd;\r\n\r\n // Find start line and char\r\n let charCount = 0;\r\n for (let i = 0; i < this._textLines.length; i++) {\r\n const originalLineLength = this._getOriginalLineLength(i);\r\n if (charCount + originalLineLength >= selectionStart) {\r\n startLine = i;\r\n originalStartChar = selectionStart - charCount;\r\n break;\r\n }\r\n charCount += originalLineLength + this.missingNewlineOffset(i);\r\n }\r\n\r\n // Find end line and char\r\n charCount = 0;\r\n for (let i = 0; i < this._textLines.length; i++) {\r\n const originalLineLength = this._getOriginalLineLength(i);\r\n if (charCount + originalLineLength >= selectionEnd) {\r\n endLine = i;\r\n originalEndChar = selectionEnd - charCount;\r\n break;\r\n }\r\n charCount += originalLineLength + this.missingNewlineOffset(i);\r\n if (i === this._textLines.length - 1) {\r\n endLine = i;\r\n originalEndChar = originalLineLength;\r\n }\r\n }\r\n\r\n for (let i = startLine; i <= endLine; i++) {\r\n let lineHeight = this.getHeightOfLine(i),\r\n realLineHeight = 0;\r\n\r\n // Get visual positions for this line\r\n const lineText = this._textLines[i].join('');\r\n const visualPositions = this._measureVisualPositions(i, lineText);\r\n const displayLineLength = this._textLines[i].length;\r\n const originalLineLength = this._getOriginalLineLength(i);\r\n\r\n // Calculate selection bounds in original space, then convert to display\r\n let originalLineStartChar = 0;\r\n let originalLineEndChar = originalLineLength;\r\n\r\n if (i === startLine) {\r\n originalLineStartChar = originalStartChar;\r\n }\r\n if (i === endLine) {\r\n originalLineEndChar = originalEndChar;\r\n }\r\n\r\n // Convert original char indices to display indices for visual lookup\r\n const displayLineStartChar = this._originalToDisplayIndex(i, originalLineStartChar);\r\n const displayLineEndChar = this._originalToDisplayIndex(i, originalLineEndChar);\r\n\r\n // Get visual X positions for selection range\r\n let minVisualX = Infinity;\r\n let maxVisualX = -Infinity;\r\n\r\n for (const pos of visualPositions) {\r\n if (pos.logicalIndex >= displayLineStartChar && pos.logicalIndex < displayLineEndChar) {\r\n minVisualX = Math.min(minVisualX, pos.visualX);\r\n maxVisualX = Math.max(maxVisualX, pos.visualX + pos.width);\r\n }\r\n }\r\n\r\n // Handle edge cases\r\n if (minVisualX === Infinity || maxVisualX === -Infinity) {\r\n if (i >= startLine && i < endLine) {\r\n // Full line selection\r\n minVisualX = 0;\r\n maxVisualX = isJustify && !this.isEndOfWrapping(i)\r\n ? this.width\r\n : this.getLineWidth(i) || 5;\r\n } else {\r\n continue; // No selection on this line\r\n }\r\n }\r\n\r\n realLineHeight = lineHeight;\r\n if (this.lineHeight < 1 || (i === endLine && this.lineHeight > 1)) {\r\n lineHeight /= this.lineHeight;\r\n }\r\n\r\n // Calculate draw position\r\n // Visual positions are relative to line start (0 to lineWidth)\r\n // Need to add alignment offset\r\n const lineWidth = this.getLineWidth(i);\r\n let alignOffset = 0;\r\n\r\n if (this.textAlign === 'center' || this.textAlign === 'justify-center') {\r\n alignOffset = (this.width - lineWidth) / 2;\r\n } else if (this.textAlign === 'right' || this.textAlign === 'justify-right') {\r\n alignOffset = this.width - lineWidth;\r\n } else if (this.direction === 'rtl' && (this.textAlign === 'justify' || this.textAlign === 'left')) {\r\n alignOffset = this.width - lineWidth;\r\n }\r\n\r\n // Draw from center origin (-width/2 to width/2)\r\n const drawStart = -this.width / 2 + alignOffset + minVisualX;\r\n const drawWidth = maxVisualX - minVisualX;\r\n let drawHeight = lineHeight;\r\n let extraTop = 0;\r\n\r\n if (this.inCompositionMode) {\r\n ctx.fillStyle = this.compositionColor || 'black';\r\n drawHeight = 1;\r\n extraTop = lineHeight;\r\n } else {\r\n ctx.fillStyle = this.selectionColor;\r\n }\r\n\r\n ctx.fillRect(\r\n drawStart,\r\n boundaries.top + boundaries.topOffset + extraTop,\r\n drawWidth,\r\n drawHeight,\r\n );\r\n boundaries.topOffset += realLineHeight;\r\n }\r\n }\r\n\r\n /**\r\n * High level function to know the height of the cursor.\r\n * the currentChar is the one that precedes the cursor\r\n * Returns fontSize of char at the current cursor\r\n * Unused from the library, is for the end user\r\n * @return {Number} Character font size\r\n */\r\n getCurrentCharFontSize(): number {\r\n const cp = this._getCurrentCharIndex();\r\n return this.getValueOfPropertyAt(cp.l, cp.c, 'fontSize');\r\n }\r\n\r\n /**\r\n * High level function to know the color of the cursor.\r\n * the currentChar is the one that precedes the cursor\r\n * Returns color (fill) of char at the current cursor\r\n * if the text object has a pattern or gradient for filler, it will return that.\r\n * Unused by the library, is for the end user\r\n * @return {String | TFiller} Character color (fill)\r\n */\r\n getCurrentCharColor(): string | TFiller | null {\r\n const cp = this._getCurrentCharIndex();\r\n return this.getValueOfPropertyAt(cp.l, cp.c, FILL);\r\n }\r\n\r\n /**\r\n * Returns the cursor position for the getCurrent.. functions\r\n * @private\r\n */\r\n _getCurrentCharIndex() {\r\n const cursorPosition = this.get2DCursorLocation(this.selectionStart, true),\r\n charIndex =\r\n cursorPosition.charIndex > 0 ? cursorPosition.charIndex - 1 : 0;\r\n return { l: cursorPosition.lineIndex, c: charIndex };\r\n }\r\n\r\n dispose() {\r\n this.exitEditingImpl();\r\n this.draggableTextDelegate.dispose();\r\n super.dispose();\r\n }\r\n}\r\n\r\nclassRegistry.setClass(IText);\r\n// legacy\r\nclassRegistry.setClass(IText, 'i-text');\r\n"],"names":["iTextDefaultValues","selectionStart","selectionEnd","selectionColor","isEditing","editable","editingBorderColor","cursorWidth","cursorColor","cursorDelay","cursorDuration","caching","hiddenTextareaContainer","keysMap","keysMapRtl","ctrlKeysMapDown","ctrlKeysMapUp","_selectionDirection","_reSpace","inCompositionMode","IText","ITextClickBehavior","getDefaults","super","ownDefaults","type","constructor","text","options","_defineProperty","this","Map","initBehavior","_set","key","value","_savedProps","canvas","Canvas","textEditingManager","remove","add","setSelectionStart","index","Math","max","_updateAndFire","setSelectionEnd","min","length","property","_fireSelectionChanged","_updateTextarea","fire","target","initDimensions","initDelayedCursor","getSelectionStyles","startIndex","arguments","undefined","endIndex","complete","setSelectionStyles","styles","get2DCursorLocation","skipWrapping","render","ctx","cursorOffsetCache","_clearVisualPositionsCache","renderCursorOrSelection","toCanvasElement","clearContextTop","boundaries","_getCursorBoundaries","ancestors","findAncestorsWithClipPath","hasAncestorsWithClipping","drawingCanvas","drawingCtx","createCanvasElementFor","getContext","applyCanvasTransform","m","calcTransformMatrix","transform","renderSelection","renderCursor","ancestor","clipPath","clippingCanvas","clippingCtx","absolutePositioned","drawObject","drawClipPathOnCache","setTransform","drawImage","contextTopDirty","restore","clipPathAncestors","obj","push","parent","skipCaching","_getCursorBoundariesOriginal","_getCursorBoundariesOffsets","__getCursorBoundariesOffsets","_getCursorBoundariesAdvanced","enableAdvancedLayout","_layoutTextAdvanced","layout","cursorRect","getCursorRect","_getAdvancedLayoutOptions","left","_getLeftOffset","top","_getTopOffset","leftOffset","x","topOffset","y","getSelectionStartFromPointer","e","localPoint","getScenePoint","invertTransform","mouseX","width","mouseY","height","lineIndex","i","_textLines","lineHeight","getHeightOfLine","lineStartIndex","origLen","_getOriginalLineLength","newlineOffset","missingNewlineOffset","console","log","_getTatweelCountForLine","line","lineText","join","displayCharLength","originalCharLength","visualPositions","_measureVisualPositions","lineWidth","getLineWidth","lineStartX","textAlign","direction","clickX","sortedPositions","sort","a","b","visualX","lastPos","pos","charEnd","originalCharIndex","_displayToOriginalIndex","logicalIndex","isTatweel","_isTatweelAtDisplayIndex","clickedLeftHalf","result","isRtl","_visualPositionsCache","clear","has","get","positions","chars","__charBounds","set","_chars$i","_chars$i2","kernedWidth","runs","analyzeBiDi","stringPosToGrapheme","strPos","gi","grapheme","j","runInfos","run","runChars","runWidth","seenGraphemes","Set","sp","start","end","_chars$gi","charIndices","runInfo","charX","idx","_chars$idx","charWidth","_chars$idx2","offsets","originalLineLength","displayCharIndex","_originalToDisplayIndex","charPos","find","p","prevDisplayIndex","prevCharPos","bound","alignOffset","renderCursorAt","_renderCursor","contextTop","getCursorRenderingData","cursorLocation","charIndex","charHeight","getValueOfPropertyAt","multiplier","getObjectScaling","getZoom","dy","_fontSizeFraction","color","opacity","_currentCursorOpacity","fillStyle","globalAlpha","fillRect","selection","hiddenTextarea","_renderSelection","renderDragSourceEffect","dragStartSelection","draggableTextDelegate","getDragStartSelection","renderDropTargetEffect","dragSelection","isJustify","includes","JUSTIFY","startLine","endLine","originalStartChar","originalEndChar","charCount","realLineHeight","originalLineStartChar","originalLineEndChar","displayLineStartChar","displayLineEndChar","minVisualX","Infinity","maxVisualX","isEndOfWrapping","drawStart","drawWidth","drawHeight","extraTop","compositionColor","getCurrentCharFontSize","cp","_getCurrentCharIndex","l","c","getCurrentCharColor","FILL","cursorPosition","dispose","exitEditingImpl","classRegistry","setClass"],"mappings":"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"}
|