(function(){ var html var textarea var dom = {} var style = {} var pos = {} // i use this because a plain
collapses vertically sometimes... var zero_width_space = '' var off = function(){ dom.highlight.style.display = 'none' } var height_until_error = 0; var height_with_error = 0; var on = function(line_num){ pos = textarea.getBoundingClientRect() var text = textarea.value; var lines = text.split('\n') var lines_until_error = lines.slice(0, line_num) var line_with_error = lines[line_num] if (lines_until_error.length === 1) dom.textmeasure.innerHTML = lines_until_error + zero_width_space else dom.textmeasure.innerHTML = lines_until_error.join('
' + zero_width_space) height_until_error = dom.textmeasure.offsetHeight dom.textmeasure.innerHTML = line_with_error + zero_width_space height_with_error = dom.textmeasure.offsetHeight reposition_highlight() }; var reposition_highlight = function(){ dom.highlight.style.display = 'block' var bounds_bottom = pos.top + pos.height if (textarea.scrollHeight > textarea.clientHeight) // scrollbar exists dom.highlight.style.width = pos.width - scrollbar_width + "px" else dom.highlight.style.width = pos.width + "px" dom.highlight.style.left = pos.left + "px" var y_pos = pos.top + height_until_error - textarea.scrollTop dom.highlight.style.top = y_pos + html.scrollTop + "px" var height_of_highlight = height_with_error; // nice clip on bottom if (y_pos + height_of_highlight > bounds_bottom) height_of_highlight = Math.max(0, bounds_bottom - y_pos) // crap clip on top if (y_pos < pos.top) height_of_highlight = 0 dom.highlight.style.height = height_of_highlight + "px" } var calc_textarea_style = function(){ var $textarea = $("#shader") textarea = $textarea[0] // GG = textarea var props = ['lineHeight', 'fontFamily', 'fontSize', 'padding', 'margin', 'borderWidth', 'width'] for (var i=0, p; p=props[i]; i++){ style[p] = $textarea.css(p) } console.log(style) } var calc_scrollbar_width = function() { var outer = document.createElement("div"); outer.style.visibility = "hidden"; outer.style.width = "100px"; document.body.appendChild(outer); var width_no_scroll = outer.offsetWidth; // force scrollbars outer.style.overflow = "scroll"; // add innerdiv var inner = document.createElement("div"); inner.style.width = "100%"; outer.appendChild(inner); var width_with_scroll = inner.offsetWidth; // remove divs outer.parentNode.removeChild(outer); return width_no_scroll - width_with_scroll; } var create_el_textmeasure = function(){ var el = dom.textmeasure = document.createElement('div') el.id = 'textmeasure' var s = el.style for (var key in style) s[key] = style[key] s.wordWrap = 'break-word' s.wordBreak = 'break-all' s.border = '1px solid red' s.padding = '0' s.display = 'block' s.position = 'absolute' s.left = "-5000px" document.body.appendChild(el) } var create_el_highlight = function(){ var el = dom.highlight = document.createElement('div') var s = el.style for (var key in style) s[key] = style[key] s.pointerEvents = 'none' s.opacity = '0.2' s.backgroundColor = '#f00' s.position = 'absolute' s.lineHeight = '0' s.fontSize = '0' s.padding = '0' s.borderWidth = '0' s.display = 'block' document.body.appendChild(el) } var scrollbar_width = 0; var init = function(){ calc_textarea_style() create_el_highlight() create_el_textmeasure() scrollbar_width = calc_scrollbar_width() textarea.addEventListener('scroll', reposition_highlight) html = document.querySelector('html') } error_highlight = { init: init, on: on, off: off } })(); error_highlight.init()