jquery.caret-0.3.1.js 12 KB

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