src/js/jquery.caretposition.custom.js
/**
* jQuery plugin for getting position of cursor in textarea
* @license under GNU license
* @author Bevis Zhao (i@bevis.me, http://bevis.me)
*/
$(function() {
//Mods to eliminate $.browser errors
$.browser = {};
$.browser.msie = false;
$.browser.mozilla = false;
var calculator = {
// key styles
primaryStyles: ['fontFamily', 'fontSize', 'fontWeight', 'fontVariant', 'fontStyle',
'paddingLeft', 'paddingTop', 'paddingBottom', 'paddingRight',
'marginLeft', 'marginTop', 'marginBottom', 'marginRight',
'borderLeftColor', 'borderTopColor', 'borderBottomColor', 'borderRightColor',
'borderLeftStyle', 'borderTopStyle', 'borderBottomStyle', 'borderRightStyle',
'borderLeftWidth', 'borderTopWidth', 'borderBottomWidth', 'borderRightWidth',
'line-height', 'outline', 'text-align'],
specificStyle: {
'word-wrap': 'break-word',
'overflow-x': 'hidden',
'overflow-y': 'auto'
},
simulator : $('<div id="textarea_simulator"/>').css({
position: 'absolute',
top: 0,
left: 0,
visibility: 'hidden'
}).appendTo(document.body),
toHtml : function(text) {
return text.replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>')
.split(' ').join('<span style="white-space:prev-wrap"> </span>');
},
// calculate position
getCaretPosition: function() {
var cal = calculator, self = this, element = self[0], elementOffset = self.offset();
// IE has easy way to get caret offset position
if ($.browser.msie && $.browser.version <= 9) {
// must get focus first
element.focus();
var range = document.selection.createRange();
$('#hskeywords').val(element.scrollTop);
return {
left: range.boundingLeft - elementOffset.left,
top: parseInt(range.boundingTop) - elementOffset.top + element.scrollTop
+ document.documentElement.scrollTop + parseInt(self.getComputedStyle("fontSize"))
};
}
cal.simulator.empty();
// clone primary styles to imitate textarea
$.each(cal.primaryStyles, function(index, styleName) {
self.cloneStyle(cal.simulator, styleName);
});
// caculate width and height
cal.simulator.css($.extend({
'width': self.width(),
'height': self.height()
}, cal.specificStyle));
var value = (self.val() || self.text()), cursorPosition = self.getCursorPosition();
var beforeText = value.substring(0, cursorPosition),
afterText = value.substring(cursorPosition);
var before = $('<span class="before"/>').html(cal.toHtml(beforeText)),
focus = $('<span class="focus"/>'),
after = $('<span class="after"/>').html(cal.toHtml(afterText));
cal.simulator.append(before).append(focus).append(after);
var focusOffset = focus.offset(), simulatorOffset = cal.simulator.offset();
// alert(focusOffset.left + ',' + simulatorOffset.left + ',' + element.scrollLeft);
return {
top: focusOffset.top - simulatorOffset.top - element.scrollTop
// calculate and add the font height except Firefox
+ ($.browser.mozilla ? 0 : parseInt(self.getComputedStyle("fontSize"))),
left: focus[0].offsetLeft - cal.simulator[0].offsetLeft - element.scrollLeft
};
}
};
$.fn.extend({
setCursorPosition : function(position){
if(this.length == 0) return this;
return $(this).setSelection(position, position);
},
setSelection: function(selectionStart, selectionEnd) {
if(this.length == 0) return this;
input = this[0];
if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', selectionEnd);
range.moveStart('character', selectionStart);
range.select();
} else if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
} else {
var el = this.get(0);
var range = document.createRange();
range.collapse(true);
range.setStart(el.childNodes[0], selectionStart);
range.setEnd(el.childNodes[0], selectionEnd);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
return this;
},
getComputedStyle: function(styleName) {
if (this.length == 0) return;
var thiz = this[0];
var result = this.css(styleName);
result = result || ($.browser.msie ?
thiz.currentStyle[styleName]:
document.defaultView.getComputedStyle(thiz, null)[styleName]);
return result;
},
// easy clone method
cloneStyle: function(target, styleName) {
var styleVal = this.getComputedStyle(styleName);
if (!!styleVal) {
$(target).css(styleName, styleVal);
}
},
cloneAllStyle: function(target, style) {
var thiz = this[0];
for (var styleName in thiz.style) {
var val = thiz.style[styleName];
typeof val == 'string' || typeof val == 'number'
? this.cloneStyle(target, styleName)
: NaN;
}
},
getCursorPosition : function() {
var element = input = this[0];
var value = (input.value || input.innerText)
if(!this.data("lastCursorPosition")){
this.data("lastCursorPosition",0);
}
var lastCursorPosition = this.data("lastCursorPosition");
if (document.selection) {
input.focus();
var sel = document.selection.createRange();
var selLen = document.selection.createRange().text.length;
sel.moveStart('character', -value.length);
lastCursorPosition = sel.text.length - selLen;
} else if (input.selectionStart || input.selectionStart == '0') {
return input.selectionStart;
} else if (typeof window.getSelection != "undefined" && window.getSelection().rangeCount>0) {
try{
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
lastCursorPosition = preCaretRange.toString().length;
}catch(e){
lastCursorPosition = this.data("lastCursorPosition");
}
} else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
var textRange = document.selection.createRange();
var preCaretTextRange = document.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
lastCursorPosition = preCaretTextRange.text.length;
}
this.data("lastCursorPosition",lastCursorPosition);
return lastCursorPosition;
},
getCaretPosition: calculator.getCaretPosition
});
});