2018-09-13 12:53:19 +08:00
|
|
|
// TODO: This code should be moved to lib, it was heavily modified by us over the years, and mostly written by us
|
|
|
|
// except for the little snippet from StackOverflow
|
|
|
|
//
|
2013-02-23 04:41:12 +08:00
|
|
|
// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
|
|
|
|
var clone, getCaret;
|
|
|
|
getCaret = function(el) {
|
|
|
|
var r, rc, re;
|
|
|
|
if (el.selectionStart) {
|
|
|
|
return el.selectionStart;
|
|
|
|
} else if (document.selection) {
|
|
|
|
el.focus();
|
|
|
|
r = document.selection.createRange();
|
|
|
|
if (!r) return 0;
|
|
|
|
re = el.createTextRange();
|
|
|
|
rc = re.duplicate();
|
|
|
|
re.moveToBookmark(r.getBookmark());
|
|
|
|
rc.setEndPoint("EndToStart", re);
|
|
|
|
return rc.text.length;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
2013-02-21 02:15:50 +08:00
|
|
|
|
2013-02-23 04:41:12 +08:00
|
|
|
clone = null;
|
|
|
|
|
2018-09-13 12:53:19 +08:00
|
|
|
$.fn.caret = function() {
|
2014-06-18 13:17:32 +08:00
|
|
|
return getCaret(this[0]);
|
|
|
|
};
|
|
|
|
|
2013-02-23 04:41:12 +08:00
|
|
|
/**
|
|
|
|
This is a jQuery plugin to retrieve the caret position in a textarea
|
|
|
|
|
|
|
|
@module $.fn.caretPosition
|
|
|
|
**/
|
|
|
|
$.fn.caretPosition = function(options) {
|
2018-09-13 12:53:19 +08:00
|
|
|
var after,
|
|
|
|
before,
|
|
|
|
getStyles,
|
|
|
|
guard,
|
|
|
|
html,
|
|
|
|
important,
|
|
|
|
insertSpaceAfterBefore,
|
|
|
|
letter,
|
|
|
|
makeCursor,
|
|
|
|
p,
|
|
|
|
pPos,
|
|
|
|
pos,
|
|
|
|
span,
|
|
|
|
styles,
|
|
|
|
textarea,
|
|
|
|
val;
|
2013-02-23 04:41:12 +08:00
|
|
|
if (clone) {
|
|
|
|
clone.remove();
|
|
|
|
}
|
|
|
|
span = $("#pos span");
|
|
|
|
textarea = $(this);
|
|
|
|
|
2013-12-31 02:29:52 +08:00
|
|
|
getStyles = function(el) {
|
2013-02-23 04:41:12 +08:00
|
|
|
if (el.currentStyle) {
|
|
|
|
return el.currentStyle;
|
|
|
|
} else {
|
|
|
|
return document.defaultView.getComputedStyle(el, "");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
important = function(prop) {
|
|
|
|
return styles.getPropertyValue(prop);
|
|
|
|
};
|
|
|
|
|
2018-09-13 12:53:19 +08:00
|
|
|
styles = getStyles(textarea[0]);
|
|
|
|
clone = $("<div><p></p></div>").appendTo("body");
|
|
|
|
p = clone.find("p");
|
|
|
|
|
|
|
|
const isRTL = $("html").hasClass("rtl");
|
2013-02-23 04:41:12 +08:00
|
|
|
clone.css({
|
|
|
|
border: "1px solid black",
|
|
|
|
padding: important("padding"),
|
|
|
|
resize: important("resize"),
|
|
|
|
"max-height": textarea.height() + "px",
|
|
|
|
"overflow-y": "auto",
|
|
|
|
"word-wrap": "break-word",
|
|
|
|
position: "absolute",
|
2018-01-18 04:09:27 +08:00
|
|
|
left: isRTL ? "auto" : "-7000px",
|
|
|
|
right: isRTL ? "-7000px" : "auto"
|
2013-02-23 04:41:12 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
p.css({
|
|
|
|
margin: 0,
|
|
|
|
padding: 0,
|
|
|
|
"word-wrap": "break-word",
|
|
|
|
"letter-spacing": important("letter-spacing"),
|
|
|
|
"font-family": important("font-family"),
|
|
|
|
"font-size": important("font-size"),
|
|
|
|
"line-height": important("line-height")
|
|
|
|
});
|
|
|
|
|
2018-09-13 12:53:19 +08:00
|
|
|
clone.width(textarea.width());
|
|
|
|
clone.height(textarea.height());
|
|
|
|
|
|
|
|
pos =
|
|
|
|
options && (options.pos || options.pos === 0)
|
|
|
|
? options.pos
|
|
|
|
: getCaret(textarea[0]);
|
|
|
|
|
2013-02-23 04:41:12 +08:00
|
|
|
val = textarea.val().replace("\r", "");
|
|
|
|
if (options && options.key) {
|
|
|
|
val = val.substring(0, pos) + options.key + val.substring(pos);
|
|
|
|
}
|
|
|
|
before = pos - 1;
|
|
|
|
after = pos;
|
|
|
|
insertSpaceAfterBefore = false;
|
|
|
|
|
|
|
|
// if before and after are \n insert a space
|
|
|
|
if (val[before] === "\n" && val[after] === "\n") {
|
|
|
|
insertSpaceAfterBefore = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
guard = function(v) {
|
|
|
|
var buf;
|
|
|
|
buf = v.replace(/</g, "<");
|
|
|
|
buf = buf.replace(/>/g, ">");
|
|
|
|
buf = buf.replace(/[ ]/g, "​ ​");
|
|
|
|
return buf.replace(/\n/g, "<br />");
|
|
|
|
};
|
|
|
|
|
|
|
|
makeCursor = function(pos, klass, color) {
|
|
|
|
var l;
|
|
|
|
l = val.substring(pos, pos + 1);
|
|
|
|
if (l === "\n") return "<br>";
|
2018-09-13 12:53:19 +08:00
|
|
|
return (
|
|
|
|
"<span class='" +
|
|
|
|
klass +
|
|
|
|
"' style='background-color:" +
|
|
|
|
color +
|
|
|
|
"; margin:0; padding: 0'>" +
|
|
|
|
guard(l) +
|
|
|
|
"</span>"
|
|
|
|
);
|
2013-02-23 04:41:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
html = "";
|
2018-09-13 12:53:19 +08:00
|
|
|
|
2013-02-23 04:41:12 +08:00
|
|
|
if (before >= 0) {
|
2018-09-13 12:53:19 +08:00
|
|
|
html +=
|
|
|
|
guard(val.substring(0, pos - 1)) +
|
|
|
|
makeCursor(before, "before", "#d0ffff");
|
2013-02-23 04:41:12 +08:00
|
|
|
if (insertSpaceAfterBefore) {
|
|
|
|
html += makeCursor(0, "post-before", "#d0ffff");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (after >= 0) {
|
|
|
|
html += makeCursor(after, "after", "#ffd0ff");
|
|
|
|
if (after - 1 < val.length) {
|
|
|
|
html += guard(val.substring(after + 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p.html(html);
|
|
|
|
clone.scrollTop(textarea.scrollTop());
|
|
|
|
letter = p.find("span:first");
|
|
|
|
pos = letter.offset();
|
|
|
|
if (letter.hasClass("before")) {
|
|
|
|
pos.left = pos.left + letter.width();
|
|
|
|
}
|
|
|
|
|
|
|
|
pPos = p.offset();
|
2018-09-13 12:53:19 +08:00
|
|
|
let position = {
|
2013-02-23 04:41:12 +08:00
|
|
|
left: pos.left - pPos.left,
|
2018-09-13 12:53:19 +08:00
|
|
|
top: pos.top - pPos.top - clone.scrollTop()
|
2013-02-23 04:41:12 +08:00
|
|
|
};
|
|
|
|
|
2018-09-13 12:53:19 +08:00
|
|
|
clone.remove();
|
|
|
|
return position;
|
2013-02-23 04:41:12 +08:00
|
|
|
};
|