1/** 2 * Text selection related functions. 3 */ 4 5/** 6 * selection prototype 7 * 8 * Object that capsulates the selection in a textarea. Returned by getSelection. 9 * 10 * @author Andreas Gohr <andi@splitbrain.org> 11 */ 12function selection_class(){ 13 this.start = 0; 14 this.end = 0; 15 this.obj = null; 16 this.rangeCopy = null; 17 this.scroll = 0; 18 19 this.getLength = function(){ 20 return this.end - this.start; 21 }; 22 23 this.getText = function(){ 24 if(!this.obj) return ''; 25 return this.obj.value.substring(this.start,this.end); 26 } 27} 28 29/** 30 * Get current selection/cursor position in a given textArea 31 * 32 * @link http://groups.drupal.org/node/1210 33 * @author Andreas Gohr <andi@splitbrain.org> 34 * @returns object - a selection object 35 */ 36function getSelection(textArea) { 37 var sel = new selection_class(); 38 39 sel.obj = textArea; 40 sel.start = textArea.value.length; 41 sel.end = textArea.value.length; 42 43 textArea.focus(); 44 if(document.getSelection) { // Mozilla et al. 45 sel.start = textArea.selectionStart; 46 sel.end = textArea.selectionEnd; 47 sel.scroll = textArea.scrollTop; 48 } else if(document.selection) { // MSIE 49 // The current selection 50 var range = document.selection.createRange(); 51 sel.rangeCopy = range.duplicate(); 52 // Select all text 53 sel.rangeCopy.moveToElementText(textArea); 54 // Now move 'dummy' end point to end point of original range 55 sel.rangeCopy.setEndPoint( 'EndToEnd', range ); 56 // Now we can calculate start and end points 57 sel.start = sel.rangeCopy.text.length - range.text.length; 58 sel.end = sel.start + range.text.length; 59 } 60 return sel; 61} 62 63/** 64 * Set the selection 65 * 66 * You need to get a selection object via getSelection() first, then modify the 67 * start and end properties and pass it back to this function. 68 * 69 * @link http://groups.drupal.org/node/1210 70 * @author Andreas Gohr <andi@splitbrain.org> 71 * @param object selection - a selection object as returned by getSelection() 72 */ 73function setSelection(selection){ 74 if(document.getSelection){ // FF 75 // what a pleasure in FF ;) 76 selection.obj.setSelectionRange(selection.start,selection.end); 77 if(selection.scroll) selection.obj.scrollTop = selection.scroll; 78 } else if(document.selection) { // IE 79 // count number of newlines in str to work around stupid IE selection bug 80 var countNL = function(str) { 81 var m = str.split("\n"); 82 if (!m || !m.length) return 0; 83 return m.length-1; 84 }; 85 var fix = countNL(selection.obj.value.substring(0,selection.start)); 86 87 selection.rangeCopy.collapse(true); 88 selection.rangeCopy.moveStart('character',selection.start - fix); 89 selection.rangeCopy.moveEnd('character',selection.end - selection.start); 90 selection.rangeCopy.select(); 91 } 92} 93 94/** 95 * Inserts the given text at the current cursor position or replaces the current 96 * selection 97 * 98 * @author Andreas Gohr <andi@splitbrain.org> 99 * @param string text - the new text to be pasted 100 * @param objct selecttion - selection object returned by getSelection 101 * @param int opts.startofs - number of charcters at the start to skip from new selection 102 * @param int opts.endofs - number of charcters at the end to skip from new selection 103 * @param bool opts.ofs - set tru if new text should not be selected 104 */ 105function pasteText(selection,text,opts){ 106 if(!opts) opts = {}; 107 // replace the content 108 selection.obj.value = 109 selection.obj.value.substring(0, selection.start) + text + 110 selection.obj.value.substring(selection.end, selection.obj.value.length); 111 112 // set new selection 113 selection.end = selection.start + text.length; 114 115 // modify the new selection if wanted 116 if(opts.startofs) selection.start += opts.startofs; 117 if(opts.endofs) selection.end -= opts.endofs; 118 119 // no selection wanted? set cursor to end position 120 if(opts.nosel) selection.start = selection.end; 121 122 setSelection(selection); 123} 124 125 126/** 127 * Format selection 128 * 129 * Apply tagOpen/tagClose to selection in textarea, use sampleText instead 130 * of selection if there is none. 131 * 132 * @author Andreas Gohr <andi@splitbrain.org> 133 */ 134function insertTags(textAreaID, tagOpen, tagClose, sampleText){ 135 var txtarea = $(textAreaID); 136 137 var selection = getSelection(txtarea); 138 var text = selection.getText(); 139 var opts; 140 141 // don't include trailing space in selection 142 if(text.charAt(text.length - 1) == ' '){ 143 selection.end--; 144 text = selection.getText(); 145 } 146 147 if(!text){ 148 // nothing selected, use the sample text and select it 149 text = sampleText; 150 opts = { 151 startofs: tagOpen.length, 152 endofs: tagClose.length 153 }; 154 }else{ 155 // place cursor at the end 156 opts = { 157 nosel: true 158 }; 159 } 160 161 // surround with tags 162 text = tagOpen + text + tagClose; 163 164 // do it 165 pasteText(selection,text,opts); 166} 167 168/** 169 * Wraps around pasteText() for backward compatibility 170 * 171 * @author Andreas Gohr <andi@splitbrain.org> 172 */ 173function insertAtCarret(textAreaID, text){ 174 var txtarea = $(textAreaID); 175 var selection = getSelection(txtarea); 176 pasteText(selection,text,{nosel: true}); 177} 178 179