| 
									
										
										
										
											2023-09-27 15:05:34 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 14:37:14 +03:00
										 |  |  | 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) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 03:51:33 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX should these be here???
 | 
					
						
							|  |  |  | HTMLElement.encode = function(str){ | 
					
						
							|  |  |  | 	var span = document.createElement('span') | 
					
						
							| 
									
										
										
										
											2023-10-31 17:38:55 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	return str | 
					
						
							|  |  |  | 		.replace(/&/g, '&')  | 
					
						
							|  |  |  | 		.replace(/</g, '<') | 
					
						
							|  |  |  | 		.replace(/>/g, '>') } | 
					
						
							|  |  |  | 	/*/ | 
					
						
							| 
									
										
										
										
											2023-10-30 03:51:33 +03:00
										 |  |  | 	span.innerText = str | 
					
						
							|  |  |  | 	return span.innerHTML } | 
					
						
							| 
									
										
										
										
											2023-10-31 17:38:55 +03:00
										 |  |  | 	//*/
 | 
					
						
							|  |  |  | // XXX this does not convert <br> back to \n...
 | 
					
						
							| 
									
										
										
										
											2023-10-30 03:51:33 +03:00
										 |  |  | HTMLElement.decode = function(str){ | 
					
						
							|  |  |  | 	var span = document.createElement('span') | 
					
						
							| 
									
										
										
										
											2023-10-31 17:38:55 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	return str | 
					
						
							| 
									
										
										
										
											2023-11-01 19:07:37 +03:00
										 |  |  | 		.replace(/</g, '<') | 
					
						
							| 
									
										
										
										
											2023-10-31 17:38:55 +03:00
										 |  |  | 		.replace(/>/g, '>')  | 
					
						
							|  |  |  | 		.replace(/&/g, '&') } | 
					
						
							|  |  |  | 	/*/ | 
					
						
							| 
									
										
										
										
											2023-10-30 03:51:33 +03:00
										 |  |  | 	span.innerHTML = str | 
					
						
							|  |  |  | 	return span.innerText } | 
					
						
							| 
									
										
										
										
											2023-10-31 17:38:55 +03:00
										 |  |  | 	//*/
 | 
					
						
							| 
									
										
										
										
											2023-10-30 03:51:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 14:37:14 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 15:05:34 +03:00
										 |  |  | HTMLTextAreaElement.prototype.updateSize = function(){ | 
					
						
							|  |  |  | 	this.style.height = '' | 
					
						
							|  |  |  | 	this.style.height = this.scrollHeight + 'px'  | 
					
						
							|  |  |  | 	return this } | 
					
						
							|  |  |  | HTMLTextAreaElement.prototype.autoUpdateSize = function(){ | 
					
						
							|  |  |  | 	var that = this | 
					
						
							|  |  |  | 	this.addEventListener('input',  | 
					
						
							|  |  |  | 		function(evt){ | 
					
						
							|  |  |  | 			that.updateSize() })  | 
					
						
							|  |  |  | 	return this } | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | var cloneAsOffscreenSpan = function(elem){ | 
					
						
							|  |  |  | 	var style = getComputedStyle(elem) | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 	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', | 
					
						
							| 
									
										
										
										
											2023-10-30 03:46:07 +03:00
										 |  |  | 		/* DEBUG... | 
					
						
							|  |  |  | 		top: '0px', | 
					
						
							|  |  |  | 		left: '0px', | 
					
						
							|  |  |  | 		/*/ | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 		top: '-100%', | 
					
						
							|  |  |  | 		left: '-100%', | 
					
						
							| 
									
										
										
										
											2023-10-30 03:46:07 +03:00
										 |  |  | 		//*/
 | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 		width: style.width, | 
					
						
							|  |  |  | 		height: style.height, | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		padding: style.padding, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 		boxSizing: style.boxSizing, | 
					
						
							| 
									
										
										
										
											2023-10-30 03:46:07 +03:00
										 |  |  | 		whiteSpace: style.whiteSpace, | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 		outline: 'solid 1px red', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pointerEvents: 'none', | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 	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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 15:21:38 +03:00
										 |  |  | 	var caret = document.createElement('span') | 
					
						
							|  |  |  | 	caret.innerText = '|'  | 
					
						
							|  |  |  | 	caret.style.margin = '0px' | 
					
						
							|  |  |  | 	caret.style.padding = '0px' | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var span = cloneAsOffscreenSpan(this) | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 	span.append( | 
					
						
							|  |  |  | 		text.slice(0, offset), | 
					
						
							| 
									
										
										
										
											2023-11-10 15:21:38 +03:00
										 |  |  | 		caret, | 
					
						
							|  |  |  | 		// NOTE: wee need the rest of the text for the caret to be typeset
 | 
					
						
							| 
									
										
										
										
											2023-10-23 17:43:25 +03:00
										 |  |  | 		// 		to the correct line...
 | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 		text.slice(offset)) | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 	document.body.append(span) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var res = { | 
					
						
							|  |  |  | 		length: text.length, | 
					
						
							| 
									
										
										
										
											2023-11-10 15:21:38 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 16:47:19 +03:00
										 |  |  | 		lines: Math.floor( | 
					
						
							| 
									
										
										
										
											2023-10-23 17:37:53 +03:00
										 |  |  | 			(this.offsetHeight - paddingV)  | 
					
						
							| 
									
										
										
										
											2023-11-10 15:21:38 +03:00
										 |  |  | 				/ caret.offsetHeight), | 
					
						
							|  |  |  | 		line: Math.floor(caret.offsetTop / caret.offsetHeight), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		caretHeight: caret.offsetHeight, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 		offset: offset, | 
					
						
							| 
									
										
										
										
											2023-11-10 15:21:38 +03:00
										 |  |  | 		offsetLeft: caret.offsetLeft, | 
					
						
							|  |  |  | 		offsetTop: caret.offsetTop, | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 15:22:32 +03:00
										 |  |  | 	if(typeof(func) == 'function'){ | 
					
						
							|  |  |  | 		res = func(res, span) } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-14 22:49:02 +03:00
										 |  |  | 	span.remove() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res } | 
					
						
							| 
									
										
										
										
											2023-09-27 15:05:34 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 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){ | 
					
						
							| 
									
										
										
										
											2023-11-04 23:15:55 +03:00
										 |  |  | 			col = (ox > 0  | 
					
						
							|  |  |  | 					|| i == 0) ? | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 				i | 
					
						
							|  |  |  | 				: i - 1  | 
					
						
							|  |  |  | 			if(cursor_line){ | 
					
						
							|  |  |  | 				return col } } } | 
					
						
							| 
									
										
										
										
											2023-11-04 23:15:55 +03:00
										 |  |  | 	// below or right of the block -> return last col or last char...
 | 
					
						
							| 
									
										
										
										
											2023-11-04 22:49:13 +03:00
										 |  |  | 	return col  | 
					
						
							|  |  |  | 		?? i } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-07 07:04:58 +03:00
										 |  |  | // 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...
 | 
					
						
							| 
									
										
										
										
											2023-09-28 20:49:06 +03:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											2023-10-07 07:04:58 +03:00
										 |  |  | 			: 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, | 
					
						
							|  |  |  | 		} }, | 
					
						
							| 
									
										
										
										
											2023-09-28 20:49:06 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 15:05:34 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 15:41:29 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-07 07:04:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 15:05:34 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                                                */ |