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