prometheus
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*/
37var _getCaretInfo = function(element){38var res = {39text: '',40start: 0,41end: 042};43
44if (!element.value) {45/* no value or empty string */46return res;47}48
49try {50if (win.getSelection) {51/* except IE */52res.start = element.selectionStart;53res.end = element.selectionEnd;54res.text = element.value.slice(res.start, res.end);55} else if (doc.selection) {56/* for IE */57element.focus();58
59var range = doc.selection.createRange(),60range2 = doc.body.createTextRange();61
62res.text = range.text;63
64try {65range2.moveToElementText(element);66range2.setEndPoint('StartToStart', range);67} catch (e) {68range2 = element.createTextRange();69range2.setEndPoint('StartToStart', range);70}71
72res.start = element.value.length - range2.text.length;73res.end = res.start + range.text.length;74}75} catch (e) {76/* give up */77}78
79return res;80};81
82/**83* caret operation for the element
84* @type {Object}
85*/
86var _CaretOperation = {87/**88* get caret position
89*
90* @param {Element} element target element
91* @return {Object} return
92* @return {Number} return.start start position for the selection
93* @return {Number} return.end end position for the selection
94*/
95getPos: function(element) {96var tmp = _getCaretInfo(element);97return {start: tmp.start, end: tmp.end};98},99
100/**101* set caret position
102*
103* @param {Element} element target element
104* @param {Object} toRange caret position
105* @param {Number} toRange.start start position for the selection
106* @param {Number} toRange.end end position for the selection
107* @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
108*/
109setPos: function(element, toRange, caret) {110caret = this._caretMode(caret);111
112if (caret === 'start') {113toRange.end = toRange.start;114} else if (caret === 'end') {115toRange.start = toRange.end;116}117
118element.focus();119try {120if (element.createTextRange) {121var range = element.createTextRange();122
123if (win.navigator.userAgent.toLowerCase().indexOf("msie") >= 0) {124toRange.start = element.value.substr(0, toRange.start).replace(/\r/g, '').length;125toRange.end = element.value.substr(0, toRange.end).replace(/\r/g, '').length;126}127
128range.collapse(true);129range.moveStart('character', toRange.start);130range.moveEnd('character', toRange.end - toRange.start);131
132range.select();133} else if (element.setSelectionRange) {134element.setSelectionRange(toRange.start, toRange.end);135}136} catch (e) {137/* give up */138}139},140
141/**142* get selected text
143*
144* @param {Element} element target element
145* @return {String} return selected text
146*/
147getText: function(element) {148return _getCaretInfo(element).text;149},150
151/**152* get caret mode
153*
154* @param {String} caret caret mode
155* @return {String} return any of the following: "keep" | "start" | "end"
156*/
157_caretMode: function(caret) {158caret = caret || "keep";159if (caret === false) {160caret = 'end';161}162
163switch (caret) {164case 'keep':165case 'start':166case 'end':167break;168
169default:170caret = 'keep';171}172
173return caret;174},175
176/**177* replace selected text
178*
179* @param {Element} element target element
180* @param {String} text replacement text
181* @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
182*/
183replace: function(element, text, caret) {184var tmp = _getCaretInfo(element),185orig = element.value,186pos = $(element).scrollTop(),187range = {start: tmp.start, end: tmp.start + text.length};188
189element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.end);190
191$(element).scrollTop(pos);192this.setPos(element, range, caret);193},194
195/**196* insert before the selected text
197*
198* @param {Element} element target element
199* @param {String} text insertion text
200* @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
201*/
202insertBefore: function(element, text, caret) {203var tmp = _getCaretInfo(element),204orig = element.value,205pos = $(element).scrollTop(),206range = {start: tmp.start + text.length, end: tmp.end + text.length};207
208element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.start);209
210$(element).scrollTop(pos);211this.setPos(element, range, caret);212},213
214/**215* insert after the selected text
216*
217* @param {Element} element target element
218* @param {String} text insertion text
219* @param {String} caret caret mode: any of the following: "keep" | "start" | "end"
220*/
221insertAfter: function(element, text, caret) {222var tmp = _getCaretInfo(element),223orig = element.value,224pos = $(element).scrollTop(),225range = {start: tmp.start, end: tmp.end};226
227element.value = orig.substr(0, tmp.end) + text + orig.substr(tmp.end);228
229$(element).scrollTop(pos);230this.setPos(element, range, caret);231}232};233
234/* add jQuery.selection */235$.extend({236/**237* get selected text on the window
238*
239* @param {String} mode selection mode: any of the following: "text" | "html"
240* @return {String} return
241*/
242selection: function(mode) {243var getText = ((mode || 'text').toLowerCase() === 'text');244
245try {246if (win.getSelection) {247if (getText) {248// get text249return win.getSelection().toString();250} else {251// get html252var sel = win.getSelection(), range;253
254if (sel.getRangeAt) {255range = sel.getRangeAt(0);256} else {257range = doc.createRange();258range.setStart(sel.anchorNode, sel.anchorOffset);259range.setEnd(sel.focusNode, sel.focusOffset);260}261
262return $('<div></div>').append(range.cloneContents()).html();263}264} else if (doc.selection) {265if (getText) {266// get text267return doc.selection.createRange().text;268} else {269// get html270return doc.selection.createRange().htmlText;271}272}273} catch (e) {274/* give up */275}276
277return '';278}279});280
281/* add selection */282$.fn.extend({283selection: function(mode, opts) {284opts = opts || {};285
286switch (mode) {287/**288* selection('getPos')
289* get caret position
290*
291* @return {Object} return
292* @return {Number} return.start start position for the selection
293* @return {Number} return.end end position for the selection
294*/
295case 'getPos':296return _CaretOperation.getPos(this[0]);297
298/**299* selection('setPos', opts)
300* set caret position
301*
302* @param {Number} opts.start start position for the selection
303* @param {Number} opts.end end position for the selection
304*/
305case 'setPos':306return this.each(function() {307_CaretOperation.setPos(this, opts);308});309
310/**311* selection('replace', opts)
312* replace the selected text
313*
314* @param {String} opts.text replacement text
315* @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
316*/
317case 'replace':318return this.each(function() {319_CaretOperation.replace(this, opts.text, opts.caret);320});321
322/**323* selection('insert', opts)
324* insert before/after the selected text
325*
326* @param {String} opts.text insertion text
327* @param {String} opts.caret caret mode: any of the following: "keep" | "start" | "end"
328* @param {String} opts.mode insertion mode: any of the following: "before" | "after"
329*/
330case 'insert':331return this.each(function() {332if (opts.mode === 'before') {333_CaretOperation.insertBefore(this, opts.text, opts.caret);334} else {335_CaretOperation.insertAfter(this, opts.text, opts.caret);336}337});338
339/**340* selection('get')
341* get selected text
342*
343* @return {String} return
344*/
345case 'get':346/* falls through */347default:348return _CaretOperation.getText(this[0]);349}350
351return this;352}353});354})(jQuery, window, window.document);355