jquery.selection.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*!
  2. * jQuery.selection - jQuery Plugin
  3. *
  4. * Copyright (c) 2010-2014 IWASAKI Koji (@madapaja).
  5. * http://blog.madapaja.net/
  6. * Under The MIT License
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining
  9. * a copy of this software and associated documentation files (the
  10. * "Software"), to deal in the Software without restriction, including
  11. * without limitation the rights to use, copy, modify, merge, publish,
  12. * distribute, sublicense, and/or sell copies of the Software, and to
  13. * permit persons to whom the Software is furnished to do so, subject to
  14. * the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  23. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  24. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  25. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. */
  27. (function($, win, doc) {
  28. /**
  29. * get caret status of the selection of the element
  30. *
  31. * @param {Element} element target DOM element
  32. * @return {Object} return
  33. * @return {String} return.text selected text
  34. * @return {Number} return.start start position of the selection
  35. * @return {Number} return.end end position of the selection
  36. */
  37. var _getCaretInfo = function(element){
  38. var res = {
  39. text: '',
  40. start: 0,
  41. end: 0
  42. };
  43. if (!element.value) {
  44. /* no value or empty string */
  45. return res;
  46. }
  47. try {
  48. if (win.getSelection) {
  49. /* except IE */
  50. res.start = element.selectionStart;
  51. res.end = element.selectionEnd;
  52. res.text = element.value.slice(res.start, res.end);
  53. } else if (doc.selection) {
  54. /* for IE */
  55. element.focus();
  56. var range = doc.selection.createRange(),
  57. range2 = doc.body.createTextRange();
  58. res.text = range.text;
  59. try {
  60. range2.moveToElementText(element);
  61. range2.setEndPoint('StartToStart', range);
  62. } catch (e) {
  63. range2 = element.createTextRange();
  64. range2.setEndPoint('StartToStart', range);
  65. }
  66. res.start = element.value.length - range2.text.length;
  67. res.end = res.start + range.text.length;
  68. }
  69. } catch (e) {
  70. /* give up */
  71. }
  72. return res;
  73. };
  74. /**
  75. * caret operation for the element
  76. * @type {Object}
  77. */
  78. var _CaretOperation = {
  79. /**
  80. * get caret position
  81. *
  82. * @param {Element} element target element
  83. * @return {Object} return
  84. * @return {Number} return.start start position for the selection
  85. * @return {Number} return.end end position for the selection
  86. */
  87. getPos: function(element) {
  88. var tmp = _getCaretInfo(element);
  89. return {start: tmp.start, end: tmp.end};
  90. },
  91. /**
  92. * set caret position
  93. *
  94. * @param {Element} element target element
  95. * @param {Object} toRange caret position
  96. * @param {Number} toRange.start start position for the selection
  97. * @param {Number} toRange.end end position for the selection
  98. * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
  99. */
  100. setPos: function(element, toRange, caret) {
  101. caret = this._caretMode(caret);
  102. if (caret === 'start') {
  103. toRange.end = toRange.start;
  104. } else if (caret === 'end') {
  105. toRange.start = toRange.end;
  106. }
  107. element.focus();
  108. try {
  109. if (element.createTextRange) {
  110. var range = element.createTextRange();
  111. if (win.navigator.userAgent.toLowerCase().indexOf("msie") >= 0) {
  112. toRange.start = element.value.substr(0, toRange.start).replace(/\r/g, '').length;
  113. toRange.end = element.value.substr(0, toRange.end).replace(/\r/g, '').length;
  114. }
  115. range.collapse(true);
  116. range.moveStart('character', toRange.start);
  117. range.moveEnd('character', toRange.end - toRange.start);
  118. range.select();
  119. } else if (element.setSelectionRange) {
  120. element.setSelectionRange(toRange.start, toRange.end);
  121. }
  122. } catch (e) {
  123. /* give up */
  124. }
  125. },
  126. /**
  127. * get selected text
  128. *
  129. * @param {Element} element target element
  130. * @return {String} return selected text
  131. */
  132. getText: function(element) {
  133. return _getCaretInfo(element).text;
  134. },
  135. /**
  136. * get caret mode
  137. *
  138. * @param {String} caret caret mode
  139. * @return {String} return any of the following: "keep" | "start" | "end"
  140. */
  141. _caretMode: function(caret) {
  142. caret = caret || "keep";
  143. if (caret === false) {
  144. caret = 'end';
  145. }
  146. switch (caret) {
  147. case 'keep':
  148. case 'start':
  149. case 'end':
  150. break;
  151. default:
  152. caret = 'keep';
  153. }
  154. return caret;
  155. },
  156. /**
  157. * replace selected text
  158. *
  159. * @param {Element} element target element
  160. * @param {String} text replacement text
  161. * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
  162. */
  163. replace: function(element, text, caret) {
  164. var tmp = _getCaretInfo(element),
  165. orig = element.value,
  166. pos = $(element).scrollTop(),
  167. range = {start: tmp.start, end: tmp.start + text.length};
  168. element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.end);
  169. $(element).scrollTop(pos);
  170. this.setPos(element, range, caret);
  171. },
  172. /**
  173. * insert before the selected text
  174. *
  175. * @param {Element} element target element
  176. * @param {String} text insertion text
  177. * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
  178. */
  179. insertBefore: function(element, text, caret) {
  180. var tmp = _getCaretInfo(element),
  181. orig = element.value,
  182. pos = $(element).scrollTop(),
  183. range = {start: tmp.start + text.length, end: tmp.end + text.length};
  184. element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.start);
  185. $(element).scrollTop(pos);
  186. this.setPos(element, range, caret);
  187. },
  188. /**
  189. * insert after the selected text
  190. *
  191. * @param {Element} element target element
  192. * @param {String} text insertion text
  193. * @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
  194. */
  195. insertAfter: function(element, text, caret) {
  196. var tmp = _getCaretInfo(element),
  197. orig = element.value,
  198. pos = $(element).scrollTop(),
  199. range = {start: tmp.start, end: tmp.end};
  200. element.value = orig.substr(0, tmp.end) + text + orig.substr(tmp.end);
  201. $(element).scrollTop(pos);
  202. this.setPos(element, range, caret);
  203. }
  204. };
  205. /* add jQuery.selection */
  206. $.extend({
  207. /**
  208. * get selected text on the window
  209. *
  210. * @param {String} mode selection mode: any of the following: "text" | "html"
  211. * @return {String} return
  212. */
  213. selection: function(mode) {
  214. var getText = ((mode || 'text').toLowerCase() === 'text');
  215. try {
  216. if (win.getSelection) {
  217. if (getText) {
  218. // get text
  219. return win.getSelection().toString();
  220. } else {
  221. // get html
  222. var sel = win.getSelection(), range;
  223. if (sel.getRangeAt) {
  224. range = sel.getRangeAt(0);
  225. } else {
  226. range = doc.createRange();
  227. range.setStart(sel.anchorNode, sel.anchorOffset);
  228. range.setEnd(sel.focusNode, sel.focusOffset);
  229. }
  230. return $('<div></div>').append(range.cloneContents()).html();
  231. }
  232. } else if (doc.selection) {
  233. if (getText) {
  234. // get text
  235. return doc.selection.createRange().text;
  236. } else {
  237. // get html
  238. return doc.selection.createRange().htmlText;
  239. }
  240. }
  241. } catch (e) {
  242. /* give up */
  243. }
  244. return '';
  245. }
  246. });
  247. /* add selection */
  248. $.fn.extend({
  249. selection: function(mode, opts) {
  250. opts = opts || {};
  251. switch (mode) {
  252. /**
  253. * selection('getPos')
  254. * get caret position
  255. *
  256. * @return {Object} return
  257. * @return {Number} return.start start position for the selection
  258. * @return {Number} return.end end position for the selection
  259. */
  260. case 'getPos':
  261. return _CaretOperation.getPos(this[0]);
  262. /**
  263. * selection('setPos', opts)
  264. * set caret position
  265. *
  266. * @param {Number} opts.start start position for the selection
  267. * @param {Number} opts.end end position for the selection
  268. */
  269. case 'setPos':
  270. return this.each(function() {
  271. _CaretOperation.setPos(this, opts);
  272. });
  273. /**
  274. * selection('replace', opts)
  275. * replace the selected text
  276. *
  277. * @param {String} opts.text replacement text
  278. * @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
  279. */
  280. case 'replace':
  281. return this.each(function() {
  282. _CaretOperation.replace(this, opts.text, opts.caret);
  283. });
  284. /**
  285. * selection('insert', opts)
  286. * insert before/after the selected text
  287. *
  288. * @param {String} opts.text insertion text
  289. * @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
  290. * @param {String} opts.mode insertion mode: any of the following: "before" | "after"
  291. */
  292. case 'insert':
  293. return this.each(function() {
  294. if (opts.mode === 'before') {
  295. _CaretOperation.insertBefore(this, opts.text, opts.caret);
  296. } else {
  297. _CaretOperation.insertAfter(this, opts.text, opts.caret);
  298. }
  299. });
  300. /**
  301. * selection('get')
  302. * get selected text
  303. *
  304. * @return {String} return
  305. */
  306. case 'get':
  307. /* falls through */
  308. default:
  309. return _CaretOperation.getText(this[0]);
  310. }
  311. return this;
  312. }
  313. });
  314. })(jQuery, window, window.document);