xref: /dokuwiki/lib/scripts/textselection.js (revision 58b091dee4e42c6703517f6f710998b63b403407)
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