ichord-Caret.js

Get caret position or offset from inputor

目前为 2016-07-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/16936/138639/ichord-Caretjs.js

  1. /*! jquery.caret v0.3.1 2016-02-27 */
  2. (function (root, factory) {
  3. if (typeof define === 'function' && define.amd) {
  4. // AMD. Register as an anonymous module.
  5. define(["jquery"], function ($) {
  6. return (root.returnExportsGlobal = factory($));
  7. });
  8. } else if (typeof exports === 'object') {
  9. // Node. Does not work with strict CommonJS, but
  10. // only CommonJS-like enviroments that support module.exports,
  11. // like Node.
  12. module.exports = factory(require("jquery"));
  13. } else {
  14. factory(jQuery);
  15. }
  16. }(this, function ($) {
  17.  
  18. /*
  19. Implement Github like autocomplete mentions
  20. http://ichord.github.com/At.js
  21.  
  22. Copyright (c) 2013 chord.luo@gmail.com
  23. Licensed under the MIT license.
  24. */
  25.  
  26. /*
  27. 本插件操作 textarea 或者 input 内的插入符
  28. 只实现了获得插入符在文本框中的位置,我设置
  29. 插入符的位置.
  30. */
  31.  
  32. "use strict";
  33. var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
  34.  
  35. pluginName = 'caret';
  36.  
  37. EditableCaret = (function() {
  38. function EditableCaret($inputor) {
  39. this.$inputor = $inputor;
  40. this.domInputor = this.$inputor[0];
  41. }
  42.  
  43. EditableCaret.prototype.setPos = function(pos) {
  44. var fn, found, offset, sel;
  45. if (sel = oWindow.getSelection()) {
  46. offset = 0;
  47. found = false;
  48. (fn = function(pos, parent) {
  49. var node, range, _i, _len, _ref, _results;
  50. _ref = parent.childNodes;
  51. _results = [];
  52. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  53. node = _ref[_i];
  54. if (found) {
  55. break;
  56. }
  57. if (node.nodeType === 3) {
  58. if (offset + node.length >= pos) {
  59. found = true;
  60. range = oDocument.createRange();
  61. range.setStart(node, pos - offset);
  62. sel.removeAllRanges();
  63. sel.addRange(range);
  64. break;
  65. } else {
  66. _results.push(offset += node.length);
  67. }
  68. } else {
  69. _results.push(fn(pos, node));
  70. }
  71. }
  72. return _results;
  73. })(pos, this.domInputor);
  74. }
  75. return this.domInputor;
  76. };
  77.  
  78. EditableCaret.prototype.getIEPosition = function() {
  79. return this.getPosition();
  80. };
  81.  
  82. EditableCaret.prototype.getPosition = function() {
  83. var inputor_offset, offset;
  84. offset = this.getOffset();
  85. inputor_offset = this.$inputor.offset();
  86. offset.left -= inputor_offset.left;
  87. offset.top -= inputor_offset.top;
  88. return offset;
  89. };
  90.  
  91. EditableCaret.prototype.getOldIEPos = function() {
  92. var preCaretTextRange, textRange;
  93. textRange = oDocument.selection.createRange();
  94. preCaretTextRange = oDocument.body.createTextRange();
  95. preCaretTextRange.moveToElementText(this.domInputor);
  96. preCaretTextRange.setEndPoint("EndToEnd", textRange);
  97. return preCaretTextRange.text.length;
  98. };
  99.  
  100. EditableCaret.prototype.getPos = function() {
  101. var clonedRange, pos, range;
  102. if (range = this.range()) {
  103. clonedRange = range.cloneRange();
  104. clonedRange.selectNodeContents(this.domInputor);
  105. clonedRange.setEnd(range.endContainer, range.endOffset);
  106. pos = clonedRange.toString().length;
  107. clonedRange.detach();
  108. return pos;
  109. } else if (oDocument.selection) {
  110. return this.getOldIEPos();
  111. }
  112. };
  113.  
  114. EditableCaret.prototype.getOldIEOffset = function() {
  115. var range, rect;
  116. range = oDocument.selection.createRange().duplicate();
  117. range.moveStart("character", -1);
  118. rect = range.getBoundingClientRect();
  119. return {
  120. height: rect.bottom - rect.top,
  121. left: rect.left,
  122. top: rect.top
  123. };
  124. };
  125.  
  126. EditableCaret.prototype.getOffset = function(pos) {
  127. var clonedRange, offset, range, rect, shadowCaret;
  128. if (oWindow.getSelection && (range = this.range())) {
  129. if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
  130. clonedRange = range.cloneRange();
  131. clonedRange.setStart(range.endContainer, range.endOffset - 1);
  132. clonedRange.setEnd(range.endContainer, range.endOffset);
  133. rect = clonedRange.getBoundingClientRect();
  134. offset = {
  135. height: rect.height,
  136. left: rect.left + rect.width,
  137. top: rect.top
  138. };
  139. clonedRange.detach();
  140. }
  141. if (!offset || (offset != null ? offset.height : void 0) === 0) {
  142. clonedRange = range.cloneRange();
  143. shadowCaret = $(oDocument.createTextNode("|"));
  144. clonedRange.insertNode(shadowCaret[0]);
  145. clonedRange.selectNode(shadowCaret[0]);
  146. rect = clonedRange.getBoundingClientRect();
  147. offset = {
  148. height: rect.height,
  149. left: rect.left,
  150. top: rect.top
  151. };
  152. shadowCaret.remove();
  153. clonedRange.detach();
  154. }
  155. } else if (oDocument.selection) {
  156. offset = this.getOldIEOffset();
  157. }
  158. if (offset) {
  159. offset.top += $(oWindow).scrollTop();
  160. offset.left += $(oWindow).scrollLeft();
  161. }
  162. return offset;
  163. };
  164.  
  165. EditableCaret.prototype.range = function() {
  166. var sel;
  167. if (!oWindow.getSelection) {
  168. return;
  169. }
  170. sel = oWindow.getSelection();
  171. if (sel.rangeCount > 0) {
  172. return sel.getRangeAt(0);
  173. } else {
  174. return null;
  175. }
  176. };
  177.  
  178. return EditableCaret;
  179.  
  180. })();
  181.  
  182. InputCaret = (function() {
  183. function InputCaret($inputor) {
  184. this.$inputor = $inputor;
  185. this.domInputor = this.$inputor[0];
  186. }
  187.  
  188. InputCaret.prototype.getIEPos = function() {
  189. var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
  190. inputor = this.domInputor;
  191. range = oDocument.selection.createRange();
  192. pos = 0;
  193. if (range && range.parentElement() === inputor) {
  194. normalizedValue = inputor.value.replace(/\r\n/g, "\n");
  195. len = normalizedValue.length;
  196. textInputRange = inputor.createTextRange();
  197. textInputRange.moveToBookmark(range.getBookmark());
  198. endRange = inputor.createTextRange();
  199. endRange.collapse(false);
  200. if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
  201. pos = len;
  202. } else {
  203. pos = -textInputRange.moveStart("character", -len);
  204. }
  205. }
  206. return pos;
  207. };
  208.  
  209. InputCaret.prototype.getPos = function() {
  210. if (oDocument.selection) {
  211. return this.getIEPos();
  212. } else {
  213. return this.domInputor.selectionStart;
  214. }
  215. };
  216.  
  217. InputCaret.prototype.setPos = function(pos) {
  218. var inputor, range;
  219. inputor = this.domInputor;
  220. if (oDocument.selection) {
  221. range = inputor.createTextRange();
  222. range.move("character", pos);
  223. range.select();
  224. } else if (inputor.setSelectionRange) {
  225. inputor.setSelectionRange(pos, pos);
  226. }
  227. return inputor;
  228. };
  229.  
  230. InputCaret.prototype.getIEOffset = function(pos) {
  231. var h, textRange, x, y;
  232. textRange = this.domInputor.createTextRange();
  233. pos || (pos = this.getPos());
  234. textRange.move('character', pos);
  235. x = textRange.boundingLeft;
  236. y = textRange.boundingTop;
  237. h = textRange.boundingHeight;
  238. return {
  239. left: x,
  240. top: y,
  241. height: h
  242. };
  243. };
  244.  
  245. InputCaret.prototype.getOffset = function(pos) {
  246. var $inputor, offset, position;
  247. $inputor = this.$inputor;
  248. if (oDocument.selection) {
  249. offset = this.getIEOffset(pos);
  250. offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
  251. offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
  252. return offset;
  253. } else {
  254. offset = $inputor.offset();
  255. position = this.getPosition(pos);
  256. return offset = {
  257. left: offset.left + position.left - $inputor.scrollLeft(),
  258. top: offset.top + position.top - $inputor.scrollTop(),
  259. height: position.height
  260. };
  261. }
  262. };
  263.  
  264. InputCaret.prototype.getPosition = function(pos) {
  265. var $inputor, at_rect, end_range, format, html, mirror, start_range;
  266. $inputor = this.$inputor;
  267. format = function(value) {
  268. value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
  269. if (/firefox/i.test(navigator.userAgent)) {
  270. value = value.replace(/\s/g, '&nbsp;');
  271. }
  272. return value;
  273. };
  274. if (pos === void 0) {
  275. pos = this.getPos();
  276. }
  277. start_range = $inputor.val().slice(0, pos);
  278. end_range = $inputor.val().slice(pos);
  279. html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
  280. html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
  281. html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
  282. mirror = new Mirror($inputor);
  283. return at_rect = mirror.create(html).rect();
  284. };
  285.  
  286. InputCaret.prototype.getIEPosition = function(pos) {
  287. var h, inputorOffset, offset, x, y;
  288. offset = this.getIEOffset(pos);
  289. inputorOffset = this.$inputor.offset();
  290. x = offset.left - inputorOffset.left;
  291. y = offset.top - inputorOffset.top;
  292. h = offset.height;
  293. return {
  294. left: x,
  295. top: y,
  296. height: h
  297. };
  298. };
  299.  
  300. return InputCaret;
  301.  
  302. })();
  303.  
  304. Mirror = (function() {
  305. Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];
  306.  
  307. function Mirror($inputor) {
  308. this.$inputor = $inputor;
  309. }
  310.  
  311. Mirror.prototype.mirrorCss = function() {
  312. var css,
  313. _this = this;
  314. css = {
  315. position: 'absolute',
  316. left: -9999,
  317. top: 0,
  318. zIndex: -20000
  319. };
  320. if (this.$inputor.prop('tagName') === 'TEXTAREA') {
  321. this.css_attr.push('width');
  322. }
  323. $.each(this.css_attr, function(i, p) {
  324. return css[p] = _this.$inputor.css(p);
  325. });
  326. return css;
  327. };
  328.  
  329. Mirror.prototype.create = function(html) {
  330. this.$mirror = $('<div></div>');
  331. this.$mirror.css(this.mirrorCss());
  332. this.$mirror.html(html);
  333. this.$inputor.after(this.$mirror);
  334. return this;
  335. };
  336.  
  337. Mirror.prototype.rect = function() {
  338. var $flag, pos, rect;
  339. $flag = this.$mirror.find("#caret");
  340. pos = $flag.position();
  341. rect = {
  342. left: pos.left,
  343. top: pos.top,
  344. height: $flag.height()
  345. };
  346. this.$mirror.remove();
  347. return rect;
  348. };
  349.  
  350. return Mirror;
  351.  
  352. })();
  353.  
  354. Utils = {
  355. contentEditable: function($inputor) {
  356. return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
  357. }
  358. };
  359.  
  360. methods = {
  361. pos: function(pos) {
  362. if (pos || pos === 0) {
  363. return this.setPos(pos);
  364. } else {
  365. return this.getPos();
  366. }
  367. },
  368. position: function(pos) {
  369. if (oDocument.selection) {
  370. return this.getIEPosition(pos);
  371. } else {
  372. return this.getPosition(pos);
  373. }
  374. },
  375. offset: function(pos) {
  376. var offset;
  377. offset = this.getOffset(pos);
  378. return offset;
  379. }
  380. };
  381.  
  382. oDocument = null;
  383.  
  384. oWindow = null;
  385.  
  386. oFrame = null;
  387.  
  388. setContextBy = function(settings) {
  389. var iframe;
  390. if (iframe = settings != null ? settings.iframe : void 0) {
  391. oFrame = iframe;
  392. oWindow = iframe.contentWindow;
  393. return oDocument = iframe.contentDocument || oWindow.document;
  394. } else {
  395. oFrame = void 0;
  396. oWindow = window;
  397. return oDocument = document;
  398. }
  399. };
  400.  
  401. discoveryIframeOf = function($dom) {
  402. var error;
  403. oDocument = $dom[0].ownerDocument;
  404. oWindow = oDocument.defaultView || oDocument.parentWindow;
  405. try {
  406. return oFrame = oWindow.frameElement;
  407. } catch (_error) {
  408. error = _error;
  409. }
  410. };
  411.  
  412. $.fn.caret = function(method, value, settings) {
  413. var caret;
  414. if (methods[method]) {
  415. if ($.isPlainObject(value)) {
  416. setContextBy(value);
  417. value = void 0;
  418. } else {
  419. setContextBy(settings);
  420. }
  421. caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
  422. return methods[method].apply(caret, [value]);
  423. } else {
  424. return $.error("Method " + method + " does not exist on jQuery.caret");
  425. }
  426. };
  427.  
  428. $.fn.caret.EditableCaret = EditableCaret;
  429.  
  430. $.fn.caret.InputCaret = InputCaret;
  431.  
  432. $.fn.caret.Utils = Utils;
  433.  
  434. $.fn.caret.apis = methods;
  435.  
  436.  
  437. }));