From ada5c336654868c5dea6a663611e758cd1d79814 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Thu, 6 Feb 2025 15:52:06 +0300 Subject: [PATCH] win -> unix Signed-off-by: Alex A. Naanou --- experiments/outline-editor/generic.js | 532 +++++++++++++------------- 1 file changed, 266 insertions(+), 266 deletions(-) diff --git a/experiments/outline-editor/generic.js b/experiments/outline-editor/generic.js index ee39aeb..dccde87 100755 --- a/experiments/outline-editor/generic.js +++ b/experiments/outline-editor/generic.js @@ -1,266 +1,266 @@ -/********************************************************************** -* -* -* -**********************************************************************/ - -// XXX add something like .closeToViewport(..) -Element.prototype.visibleInViewport = function(partial=false){ - var { top, left, bottom, right } = this.getBoundingClientRect() - var { innerHeight, innerWidth } = window - return partial - ? ((top > 0 - && top < innerHeight) - || (bottom > 0 - && bottom < innerHeight)) - && ((left > 0 - && left < innerWidth) - || (right > 0 - && right < innerWidth)) - : (top >= 0 - && left >= 0 - && bottom <= innerHeight - && right <= innerWidth) } - - -//--------------------------------------------------------------------- - -// XXX should these be here??? -HTMLElement.encode = function(str){ - var span = document.createElement('span') - // XXX - return str - .replace(/&/g, '&') - .replace(//g, '>') } - /*/ - span.innerText = str - return span.innerHTML } - //*/ -// XXX this does not convert
back to \n... -HTMLElement.decode = function(str){ - var span = document.createElement('span') - // XXX - return str - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/&/g, '&') } - /*/ - span.innerHTML = str - return span.innerText } - //*/ - - - -//--------------------------------------------------------------------- - -HTMLTextAreaElement.prototype.updateSize = function(){ - // NOTE: this is set to 0px to negate the effects of external/inherited - // height settings... - this.style.height = '0px' - this.style.height = this.scrollHeight + 'px' - return this } -HTMLTextAreaElement.prototype.autoUpdateSize = function(){ - var that = this - this.addEventListener('input', - function(evt){ - that.updateSize() }) - return this } - - -var cloneAsOffscreenSpan = function(elem){ - var style = getComputedStyle(elem) - var s = {} - for(var i=0; i < style.length; i++){ - var k = style[i] - if(k.startsWith('font') - || k.startsWith('line') - || k.startsWith('white-space')){ - s[k] = style[k] } } - var span = document.createElement('span') - Object.assign(span.style, { - ...s, - - position: 'fixed', - display: 'block', - /* DEBUG... - top: '0px', - left: '0px', - /*/ - top: '-100%', - left: '-100%', - //*/ - width: style.width, - height: style.height, - - padding: style.padding, - - boxSizing: style.boxSizing, - whiteSpace: style.whiteSpace, - - outline: 'solid 1px red', - - pointerEvents: 'none', - }) - return span } - -HTMLTextAreaElement.prototype.getTextGeometry = function(func){ - var offset = this.selectionStart - var text = this.value - - // get the relevant styles... - var style = getComputedStyle(this) - var paddingV = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom) - - var caret = document.createElement('span') - caret.innerText = '|' - caret.style.margin = '0px' - caret.style.padding = '0px' - - var span = cloneAsOffscreenSpan(this) - span.append( - text.slice(0, offset), - caret, - // NOTE: wee need the rest of the text for the caret to be typeset - // to the correct line... - text.slice(offset)) - document.body.append(span) - - var res = { - length: text.length, - - lines: Math.floor( - (this.offsetHeight - paddingV) - / caret.offsetHeight), - line: Math.floor(caret.offsetTop / caret.offsetHeight), - - caretHeight: caret.offsetHeight, - - offset: offset, - offsetLeft: caret.offsetLeft, - offsetTop: caret.offsetTop, - } - - if(typeof(func) == 'function'){ - res = func(res, span) } - - span.remove() - - return res } - -HTMLTextAreaElement.prototype.getTextOffsetAt = function(x, y){ - var that = this - var text = this.value - - // cleanup cached span... - this.__getTextOffsetAt_timeout - && clearTimeout(this.__getTextOffsetAt_timeout) - this.__getTextOffsetAt_timeout = setTimeout(function(){ - delete that.__getTextOffsetAt_timeout - that.__getTextOffsetAt_clone.remove() - delete that.__getTextOffsetAt_clone }, 50) - - // create/get clone span... - if(this.__getTextOffsetAt_clone == null){ - var span = - this.__getTextOffsetAt_clone = - cloneAsOffscreenSpan(this) - span.append(text) - document.body.append(span) - } else { - var span = this.__getTextOffsetAt_clone } - - var r = document.createRange() - var t = span.firstChild - - var clone = span.getBoundingClientRect() - var target = this.getBoundingClientRect() - - var ox = x - target.x - var oy = y - target.y - - var rect, prev - var cursor_line - var col - for(var i=0; i <= t.length; i++){ - r.setStart(t, i) - r.setEnd(t, i) - prev = rect - rect = r.getBoundingClientRect() - // line change... - if(prev && prev.y != rect.y){ - // went off the cursor line - if(cursor_line - // cursor above block - || oy <= prev.y - clone.y){ - // end of prev line... - return col - ?? i - 1 } - // reset col - col = undefined } - // cursor line... - cursor_line = - oy >= rect.y - clone.y - && oy <= rect.bottom - clone.y - // cursor col -- set once per line... - if(col == null - && ox <= rect.x - clone.x){ - col = (ox > 0 - || i == 0) ? - i - : i - 1 - if(cursor_line){ - return col } } } - // below or right of the block -> return last col or last char... - return col - ?? i } - -// calculate number of lines in text area (both wrapped and actual lines) -Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', { - enumerable: false, - get: function(){ - var style = getComputedStyle(this) - return Math.floor( - (this.scrollHeight - - parseFloat(style.paddingTop) - - parseFloat(style.paddingBottom)) - / (parseFloat(style.lineHeight) - || parseFloat(style.fontSize))) }, }) -Object.defineProperty(HTMLTextAreaElement.prototype, 'lines', { - enumerable: false, - get: function(){ - return this.value - .split(/\n/g) - .length }, }) -// XXX this does not account for wrapping... -Object.defineProperty(HTMLTextAreaElement.prototype, 'caretLine', { - enumerable: false, - get: function(){ - var offset = this.selectionStart - return offset != null ? - this.value - .slice(0, offset) - .split(/\n/g) - .length - : undefined }, }) - - -Object.defineProperty(HTMLTextAreaElement.prototype, 'caretOffset', { - enumerable: false, - get: function(){ - var offset = this.selectionStart - var r = document.createRange() - r.setStart(this, offset) - r.setEnd(this, offset) - var rect = r.getBoundingClientRect() - return { - top: rect.top, - left: rect.left, - } }, -}) - - - - -/********************************************************************** -* vim:set ts=4 sw=4 : */ +/********************************************************************** +* +* +* +**********************************************************************/ + +// XXX add something like .closeToViewport(..) +Element.prototype.visibleInViewport = function(partial=false){ + var { top, left, bottom, right } = this.getBoundingClientRect() + var { innerHeight, innerWidth } = window + return partial + ? ((top > 0 + && top < innerHeight) + || (bottom > 0 + && bottom < innerHeight)) + && ((left > 0 + && left < innerWidth) + || (right > 0 + && right < innerWidth)) + : (top >= 0 + && left >= 0 + && bottom <= innerHeight + && right <= innerWidth) } + + +//--------------------------------------------------------------------- + +// XXX should these be here??? +HTMLElement.encode = function(str){ + var span = document.createElement('span') + // XXX + return str + .replace(/&/g, '&') + .replace(//g, '>') } + /*/ + span.innerText = str + return span.innerHTML } + //*/ +// XXX this does not convert
back to \n... +HTMLElement.decode = function(str){ + var span = document.createElement('span') + // XXX + return str + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&') } + /*/ + span.innerHTML = str + return span.innerText } + //*/ + + + +//--------------------------------------------------------------------- + +HTMLTextAreaElement.prototype.updateSize = function(){ + // NOTE: this is set to 0px to negate the effects of external/inherited + // height settings... + this.style.height = '0px' + this.style.height = this.scrollHeight + 'px' + return this } +HTMLTextAreaElement.prototype.autoUpdateSize = function(){ + var that = this + this.addEventListener('input', + function(evt){ + that.updateSize() }) + return this } + + +var cloneAsOffscreenSpan = function(elem){ + var style = getComputedStyle(elem) + var s = {} + for(var i=0; i < style.length; i++){ + var k = style[i] + if(k.startsWith('font') + || k.startsWith('line') + || k.startsWith('white-space')){ + s[k] = style[k] } } + var span = document.createElement('span') + Object.assign(span.style, { + ...s, + + position: 'fixed', + display: 'block', + /* DEBUG... + top: '0px', + left: '0px', + /*/ + top: '-100%', + left: '-100%', + //*/ + width: style.width, + height: style.height, + + padding: style.padding, + + boxSizing: style.boxSizing, + whiteSpace: style.whiteSpace, + + outline: 'solid 1px red', + + pointerEvents: 'none', + }) + return span } + +HTMLTextAreaElement.prototype.getTextGeometry = function(func){ + var offset = this.selectionStart + var text = this.value + + // get the relevant styles... + var style = getComputedStyle(this) + var paddingV = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom) + + var caret = document.createElement('span') + caret.innerText = '|' + caret.style.margin = '0px' + caret.style.padding = '0px' + + var span = cloneAsOffscreenSpan(this) + span.append( + text.slice(0, offset), + caret, + // NOTE: wee need the rest of the text for the caret to be typeset + // to the correct line... + text.slice(offset)) + document.body.append(span) + + var res = { + length: text.length, + + lines: Math.floor( + (this.offsetHeight - paddingV) + / caret.offsetHeight), + line: Math.floor(caret.offsetTop / caret.offsetHeight), + + caretHeight: caret.offsetHeight, + + offset: offset, + offsetLeft: caret.offsetLeft, + offsetTop: caret.offsetTop, + } + + if(typeof(func) == 'function'){ + res = func(res, span) } + + span.remove() + + return res } + +HTMLTextAreaElement.prototype.getTextOffsetAt = function(x, y){ + var that = this + var text = this.value + + // cleanup cached span... + this.__getTextOffsetAt_timeout + && clearTimeout(this.__getTextOffsetAt_timeout) + this.__getTextOffsetAt_timeout = setTimeout(function(){ + delete that.__getTextOffsetAt_timeout + that.__getTextOffsetAt_clone.remove() + delete that.__getTextOffsetAt_clone }, 50) + + // create/get clone span... + if(this.__getTextOffsetAt_clone == null){ + var span = + this.__getTextOffsetAt_clone = + cloneAsOffscreenSpan(this) + span.append(text) + document.body.append(span) + } else { + var span = this.__getTextOffsetAt_clone } + + var r = document.createRange() + var t = span.firstChild + + var clone = span.getBoundingClientRect() + var target = this.getBoundingClientRect() + + var ox = x - target.x + var oy = y - target.y + + var rect, prev + var cursor_line + var col + for(var i=0; i <= t.length; i++){ + r.setStart(t, i) + r.setEnd(t, i) + prev = rect + rect = r.getBoundingClientRect() + // line change... + if(prev && prev.y != rect.y){ + // went off the cursor line + if(cursor_line + // cursor above block + || oy <= prev.y - clone.y){ + // end of prev line... + return col + ?? i - 1 } + // reset col + col = undefined } + // cursor line... + cursor_line = + oy >= rect.y - clone.y + && oy <= rect.bottom - clone.y + // cursor col -- set once per line... + if(col == null + && ox <= rect.x - clone.x){ + col = (ox > 0 + || i == 0) ? + i + : i - 1 + if(cursor_line){ + return col } } } + // below or right of the block -> return last col or last char... + return col + ?? i } + +// calculate number of lines in text area (both wrapped and actual lines) +Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', { + enumerable: false, + get: function(){ + var style = getComputedStyle(this) + return Math.floor( + (this.scrollHeight + - parseFloat(style.paddingTop) + - parseFloat(style.paddingBottom)) + / (parseFloat(style.lineHeight) + || parseFloat(style.fontSize))) }, }) +Object.defineProperty(HTMLTextAreaElement.prototype, 'lines', { + enumerable: false, + get: function(){ + return this.value + .split(/\n/g) + .length }, }) +// XXX this does not account for wrapping... +Object.defineProperty(HTMLTextAreaElement.prototype, 'caretLine', { + enumerable: false, + get: function(){ + var offset = this.selectionStart + return offset != null ? + this.value + .slice(0, offset) + .split(/\n/g) + .length + : undefined }, }) + + +Object.defineProperty(HTMLTextAreaElement.prototype, 'caretOffset', { + enumerable: false, + get: function(){ + var offset = this.selectionStart + var r = document.createRange() + r.setStart(this, offset) + r.setEnd(this, offset) + var rect = r.getBoundingClientRect() + return { + top: rect.top, + left: rect.left, + } }, +}) + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */