|
|
|
|
|
|
|
|
|
|
|
|
|
var properties = [ |
|
'direction', |
|
'boxSizing', |
|
'width', |
|
'height', |
|
'overflowX', |
|
'overflowY', |
|
|
|
'borderTopWidth', |
|
'borderRightWidth', |
|
'borderBottomWidth', |
|
'borderLeftWidth', |
|
'borderStyle', |
|
|
|
'paddingTop', |
|
'paddingRight', |
|
'paddingBottom', |
|
'paddingLeft', |
|
|
|
|
|
'fontStyle', |
|
'fontVariant', |
|
'fontWeight', |
|
'fontStretch', |
|
'fontSize', |
|
'fontSizeAdjust', |
|
'lineHeight', |
|
'fontFamily', |
|
|
|
'textAlign', |
|
'textTransform', |
|
'textIndent', |
|
'textDecoration', |
|
|
|
'letterSpacing', |
|
'wordSpacing', |
|
|
|
'tabSize', |
|
'MozTabSize' |
|
|
|
]; |
|
|
|
var isBrowser = (typeof window !== 'undefined'); |
|
var isFirefox = (isBrowser && window.mozInnerScreenX != null); |
|
|
|
function getCaretCoordinates(element, position, options) { |
|
if (!isBrowser) { |
|
throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); |
|
} |
|
|
|
var debug = options && options.debug || false; |
|
if (debug) { |
|
var el = document.querySelector('#input-textarea-caret-position-mirror-div'); |
|
if (el) el.parentNode.removeChild(el); |
|
} |
|
|
|
|
|
var div = document.createElement('div'); |
|
div.id = 'input-textarea-caret-position-mirror-div'; |
|
document.body.appendChild(div); |
|
|
|
var style = div.style; |
|
var computed = window.getComputedStyle ? window.getComputedStyle(element) : element.currentStyle; |
|
var isInput = element.nodeName === 'INPUT'; |
|
|
|
|
|
style.whiteSpace = 'pre-wrap'; |
|
if (!isInput) |
|
style.wordWrap = 'break-word'; |
|
|
|
|
|
style.position = 'absolute'; |
|
if (!debug) |
|
style.visibility = 'hidden'; |
|
|
|
|
|
properties.forEach(function (prop) { |
|
if (isInput && prop === 'lineHeight') { |
|
|
|
if (computed.boxSizing === "border-box") { |
|
var height = parseInt(computed.height); |
|
var outerHeight = |
|
parseInt(computed.paddingTop) + |
|
parseInt(computed.paddingBottom) + |
|
parseInt(computed.borderTopWidth) + |
|
parseInt(computed.borderBottomWidth); |
|
var targetHeight = outerHeight + parseInt(computed.lineHeight); |
|
if (height > targetHeight) { |
|
style.lineHeight = height - outerHeight + "px"; |
|
} else if (height === targetHeight) { |
|
style.lineHeight = computed.lineHeight; |
|
} else { |
|
style.lineHeight = 0; |
|
} |
|
} else { |
|
style.lineHeight = computed.height; |
|
} |
|
} else { |
|
style[prop] = computed[prop]; |
|
} |
|
}); |
|
|
|
if (isFirefox) { |
|
|
|
if (element.scrollHeight > parseInt(computed.height)) |
|
style.overflowY = 'scroll'; |
|
} else { |
|
style.overflow = 'hidden'; |
|
} |
|
|
|
div.textContent = element.value.substring(0, position); |
|
|
|
|
|
if (isInput) |
|
div.textContent = div.textContent.replace(/\s/g, '\u00a0'); |
|
|
|
var span = document.createElement('span'); |
|
|
|
|
|
|
|
|
|
|
|
span.textContent = element.value.substring(position) || '.'; |
|
div.appendChild(span); |
|
|
|
var coordinates = { |
|
top: span.offsetTop + parseInt(computed['borderTopWidth']), |
|
left: span.offsetLeft + parseInt(computed['borderLeftWidth']), |
|
height: parseInt(computed['lineHeight']) |
|
}; |
|
|
|
if (debug) { |
|
span.style.backgroundColor = '#aaa'; |
|
} else { |
|
document.body.removeChild(div); |
|
} |
|
|
|
return coordinates; |
|
} |
|
|