/* * $Id: documentselection.js 164 2006-12-30 02:09:25Z wingedfox $ * $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.003/documentselection.js $ * * Class implements cross-browser work with text selection * * @author Ilya Lebedev * @author $Author: wingedfox $ * @modified $Date: 2006-12-30 05:09:25 +0300 (Сбт, 30 Дек 2006) $ * @version $Rev: 164 $ * @license LGPL */ /* * @class DocumentSelection */ DocumentSelection = new function () { var self = this; /* * Stores hash of keys, applied to elements * * @type Object * @scope private */ var keys = { 'selectionStart' : '__DSselectionStart', 'selectionEnd' : '__DSselectionEnd' } /* * Properties to save current scrolling position in Mozilla * * */ var scrollTop, scrollLeft; //--------------------------------------------------------------------------- // SETTERS //--------------------------------------------------------------------------- /** * getSelectionRange wrapper/emulator * adapted version * * @see http://www.bazon.net/mishoo/articles.epl?art_id=1292 * @param {HTMLElement} * @param {Number} start position * @param {Number} end position * @param {Boolean} related indicates calculation of range relatively to current start point * @return void * @scope public */ this.setRange = function(el, start, end, related) { /* * set range on relative coordinates */ if (related) { var st = self.getStart(el); end = st+end; start = st+start; } if ('function' == typeof el.setSelectionRange) { /* * for Mozilla */ try {el.setSelectionRange(start, end)} catch (e) {} } else { /* * for IE */ var range; /* * just try to create a range.... */ try { range = el.createTextRange(); } catch(e) { try { range = document.body.createTextRange(); range.moveToElementText(el); } catch(e) { range = false; } } // if cannot create range if (!range) return false; range.collapse(true); range.moveStart("character", start); range.moveEnd("character", end - start); range.select(); } self.setCursorPosition(el,start,end); } /** * Set sursor position for supplied child * * @param {HTMLElement} element to set cursor position on * @param {Number} start selection start * @param {Number} end selection end * @scope public */ this.setCursorPosition = function (el,start,end) { el[keys['selectionStart']] = parseInt(start); el[keys['selectionEnd']] = parseInt(end); // if (scrollTop) el.scrollTop = scrollTop; // if (scrollLeft) el.scrollLeft = scrollLeft; // scrollTop = null; // scrollLeft= null; } //--------------------------------------------------------------------------- // GETTERS //--------------------------------------------------------------------------- /** * Return contents of the current selection * * @param {HTMLElement} el element to look position on * @return {String} * @scope public */ this.getSelection = function(el) { var s = self.getCursorPosition(el), e = self.getEnd(el); /* * w/o this check content might be duplicated on delete */ if (e0 || force) { try { var endReal = Math.abs(el.createTextRange().moveEnd("character", -100000000)); /* * calculate node offset */ var r = document.body.createTextRange(); r.moveToElementText(el); var sTest = Math.abs(r.moveStart("character", -100000000)); var eTest = Math.abs(r.moveEnd("character", -100000000)); /* * test for the TEXTAREA's dumb behavior */ if (el.tagName.toLowerCase() != 'input' && eTest - endReal == sTest) { start -= sTest; } } catch(err) {} } } catch (e) { /* * for Mozilla */ try { start = el.selectionStart } catch (e) { start = -1 } } return start<1?(start==0&&force?0:(parseInt(el[keys['selectionStart']])?parseInt(el[keys['selectionStart']]):0)):start; } /* * getSelectionStart wrapper/emulator * adapted version * * @see http://www.bazon.net/mishoo/articles.epl?art_id=1292 * @param {HTMLElement} el element to calculate end position for * @param {HTMLElement} force force calculation * @return {Number} start position * @scope public */ this.getEnd = function (el,force) { var end; /* * for IE */ try { end = Math.abs(document.selection.createRange().moveEnd("character", -100000000)); // end if (end>0 || force) { try { var endReal = Math.abs(el.createTextRange().moveEnd("character", -100000000)); /* * calculate node offset */ var r = document.body.createTextRange(); r.moveToElementText(el); var sTest = Math.abs(r.moveStart("character", -100000000)); var eTest = Math.abs(r.moveEnd("character", -100000000)); /* * test for the TEXTAREA's dumb behavior */ if (el.tagName.toLowerCase() != 'input' && eTest - endReal == sTest) { end -= sTest; } } catch(err) {} } } catch (e) { /* * for Mozilla */ try { end = el.selectionEnd } catch (e) { end = -1 } } return end<1?(end==0&&force?0:(parseInt(el[keys['selectionEnd']])?parseInt(el[keys['selectionEnd']]):0)):end; } /* * Return cursor position for supplied field * * @param {HTMLElement} element to get cursor position from * @return {Number} position * @scope public */ this.getCursorPosition = function (el) { // scrollTop = el.scrollTop; // scrollLeft = el.scrollLeft; return self.getStart(el); } //--------------------------------------------------------------------------- // MICS FUNCTIONS //--------------------------------------------------------------------------- /* * Used to save cursor position on click. * * @param {MouseEvent} click event * @scope protected */ this.saveCursorPosition = function (e) { var el = e.srcElement||e.target; if (!el || el.tagName.toLowerCase() == 'select') return false; self.setCursorPosition(el,self.getStart(el,true),self.getEnd(el,true)); } /* * Insert text at cursor position * * @param {HTMLElement} text field to insert text * @param {String} text to insert * @scope public */ this.insertAtCursor = function (fld, val) { var r = self.getCursorPosition(fld); /* * check for IE, because Opera does use \r\n sequence, but calculate positions correctly */ var tmp = document.selection?fld.value.replace(/\r/g,""):fld.value; fld.value = tmp.substring(0, r)+val+tmp.substring(r,tmp.length); self.setRange(fld,r+val.length,r+val.length); } /* * Deletes char at cursor position * * @param {HTMLElement} text field to delete text * @param {Boolean} delete text before (backspace) or after (del) cursor * @scope public */ this.deleteAtCursor = function (fld, after) { if (!after) after = false; var r = self.getCursorPosition(fld), e = self.getEnd(fld); /* * w/o this check content might be duplicated on delete */ if (e