xref: /dokuwiki/lib/scripts/edit.js (revision 20d062ca5220daf6606e2b1bcdd73d84eebafa45)
1/**
2 * Functions for text editing (toolbar stuff)
3 *
4 * @todo I'm no JS guru please help if you know how to improve
5 * @author Andreas Gohr <andi@splitbrain.org>
6 */
7
8/**
9 * Creates a toolbar button through the DOM
10 *
11 * Style the buttons through the toolbutton class
12 *
13 * @author Andreas Gohr <andi@splitbrain.org>
14 */
15function createToolButton(icon,label,key,id){
16    var btn = document.createElement('button');
17    var ico = document.createElement('img');
18
19    // preapare the basic button stuff
20    btn.className = 'toolbutton';
21    btn.title = label;
22    if(key){
23        btn.title += ' [ALT+'+key.toUpperCase()+']';
24        btn.accesskey = key;
25    }
26
27    // set IDs if given
28    if(id){
29        btn.id = id;
30        ico.id = id+'_ico';
31    }
32
33    // create the icon and add it to the button
34    ico.src = DOKU_BASE+'lib/images/toolbar/'+icon;
35    btn.appendChild(ico);
36
37    return btn;
38}
39
40/**
41 * Creates a picker window for inserting text
42 *
43 * The given list can be an associative array with text,icon pairs
44 * or a simple list of text. Style the picker window through the picker
45 * class or the picker buttons with the pickerbutton class. Picker
46 * windows are appended to the body and created invisible.
47 *
48 * @author Andreas Gohr <andi@splitbrain.org>
49 */
50function createPicker(id,list,icobase,edid){
51    var cnt = list.length;
52
53    var picker = document.createElement('div');
54    picker.className = 'picker';
55    picker.id = id;
56    picker.style.position = 'absolute';
57    picker.style.display  = 'none';
58
59    for(var key in list){
60        var btn = document.createElement('button');
61
62        btn.className = 'pickerbutton'
63
64        // associative array?
65        if(isNaN(key)){
66            var ico = document.createElement('img');
67            ico.src       = DOKU_BASE+'lib/images/'+icobase+'/'+list[key];
68            btn.title     = key;
69            btn.appendChild(ico);
70            eval("btn.onclick = function(){pickerInsert('"+id+"','"+
71                                  jsEscape(key)+"','"+
72                                  jsEscape(edid)
73                                +"');return false;}");
74        }else{
75            var txt = document.createTextNode(list[key]);
76            btn.title     = list[key];
77            btn.appendChild(txt);
78            eval("btn.onclick = function(){pickerInsert('"+id+"','"+
79                                  jsEscape(list[key])+"','"+
80                                  jsEscape(edid)
81                                +"');return false;}");
82        }
83
84        picker.appendChild(btn);
85    }
86    var body = document.getElementsByTagName('body')[0];
87    body.appendChild(picker);
88}
89
90/**
91 * Called by picker buttons to insert Text and close the picker again
92 *
93 * @author Andreas Gohr <andi@splitbrain.org>
94 */
95function pickerInsert(pickerid,text,edid){
96    // insert
97    insertAtCarret(edid,text);
98    // close picker
99    pobj = document.getElementById(pickerid);
100    pobj.style.display = 'none';
101}
102
103/**
104 * Show a previosly created picker window
105 *
106 * @author Andreas Gohr <andi@splitbrain.org>
107 */
108function showPicker(pickerid,btn){
109    var picker = document.getElementById(pickerid);
110    var x = findPosX(btn);
111    var y = findPosY(btn);
112    if(picker.style.display == 'none'){
113        picker.style.display = 'block';
114        picker.style.left = (x+3)+'px';
115        picker.style.top = (y+btn.offsetHeight+3)+'px';
116    }else{
117        picker.style.display = 'none';
118    }
119}
120
121/**
122 * Create a toolbar
123 *
124 * @param  string tbid ID of the element where to insert the toolbar
125 * @param  string edid ID of the editor textarea
126 * @param  array  tb   Associative array defining the buttons
127 * @author Andreas Gohr <andi@splitbrain.org>
128 */
129function initToolbar(tbid,edid,tb){
130    var toolbar = document.getElementById(tbid);
131    var cnt = tb.length;
132    for(i=0; i<cnt; i++){
133        // create new button and add to the toolbar
134        btn = createToolButton(tb[i]['icon'],
135                               tb[i]['title'],
136                               tb[i]['key']);
137        toolbar.appendChild(btn);
138
139        // add button action dependend on type
140        switch(tb[i]['type']){
141            case 'format':
142                var sample = tb[i]['title'];
143                if(tb[i]['sample']) sample = tb[i]['sample'];
144
145                eval("btn.onclick = function(){insertTags('"+
146                                        jsEscape(edid)+"','"+
147                                        jsEscape(tb[i]['open'])+"','"+
148                                        jsEscape(tb[i]['close'])+"','"+
149                                        jsEscape(sample)+
150                                    "');return false;}");
151                break;
152            case 'insert':
153                eval("btn.onclick = function(){insertAtCarret('"+
154                                        jsEscape(edid)+"','"+
155                                        jsEscape(tb[i]['insert'])+
156                                    "');return false;}");
157                break;
158            case 'picker':
159                createPicker('picker'+i,
160                             tb[i]['list'],
161                             tb[i]['icobase'],
162                             edid);
163                eval("btn.onclick = function(){showPicker('picker"+i+
164                                    "',this);return false;}");
165                break;
166            case 'popup':
167                eval("btn.onclick = function(){window.open('"+
168                                        jsEscape(tb[i]['url'])+"','"+
169                                        jsEscape(tb[i]['name'])+"','"+
170                                        jsEscape(tb[i]['options'])+
171                                    "');return false;}");
172                break;
173        } // end switch
174    } // end for
175}
176
177/**
178 * Format selection
179 *
180 * Apply tagOpen/tagClose to selection in textarea, use sampleText instead
181 * of selection if there is none. Copied and adapted from phpBB
182 *
183 * @author phpBB development team
184 * @author MediaWiki development team
185 * @author Andreas Gohr <andi@splitbrain.org>
186 * @author Jim Raynor <jim_raynor@web.de>
187 */
188function insertTags(edid,tagOpen, tagClose, sampleText) {
189  var txtarea = document.getElementById(edid);
190  // IE
191  if(document.selection  && !is_gecko) {
192    var theSelection = document.selection.createRange().text;
193    var replaced = true;
194    if(!theSelection){
195      replaced = false;
196      theSelection=sampleText;
197    }
198    txtarea.focus();
199
200    // This has change
201    text = theSelection;
202    if(theSelection.charAt(theSelection.length - 1) == " "){// exclude ending space char, if any
203      theSelection = theSelection.substring(0, theSelection.length - 1);
204      r = document.selection.createRange();
205      r.text = tagOpen + theSelection + tagClose + " ";
206    } else {
207      r = document.selection.createRange();
208      r.text = tagOpen + theSelection + tagClose;
209    }
210    if(!replaced){
211      r.moveStart('character',-text.length-tagClose.length);
212      r.moveEnd('character',-tagClose.length);
213    }
214    r.select();
215  // Mozilla
216  } else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
217    var replaced = false;
218    var startPos = txtarea.selectionStart;
219    var endPos   = txtarea.selectionEnd;
220    if(endPos - startPos) replaced = true;
221    var scrollTop=txtarea.scrollTop;
222    var myText = (txtarea.value).substring(startPos, endPos);
223    if(!myText) { myText=sampleText;}
224    if(myText.charAt(myText.length - 1) == " "){ // exclude ending space char, if any
225      subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " ";
226    } else {
227      subst = tagOpen + myText + tagClose;
228    }
229    txtarea.value = txtarea.value.substring(0, startPos) + subst +
230                    txtarea.value.substring(endPos, txtarea.value.length);
231    txtarea.focus();
232
233    //set new selection
234    if(replaced){
235      var cPos=startPos+(tagOpen.length+myText.length+tagClose.length);
236      txtarea.selectionStart=cPos;
237      txtarea.selectionEnd=cPos;
238    }else{
239      txtarea.selectionStart=startPos+tagOpen.length;
240      txtarea.selectionEnd=startPos+tagOpen.length+myText.length;
241    }
242    txtarea.scrollTop=scrollTop;
243  // All others
244  } else {
245    var copy_alertText=alertText;
246    var re1=new RegExp("\\$1","g");
247    var re2=new RegExp("\\$2","g");
248    copy_alertText=copy_alertText.replace(re1,sampleText);
249    copy_alertText=copy_alertText.replace(re2,tagOpen+sampleText+tagClose);
250    var text;
251    if (sampleText) {
252      text=prompt(copy_alertText);
253    } else {
254      text="";
255    }
256    if(!text) { text=sampleText;}
257    text=tagOpen+text+tagClose;
258    //append to the end
259    txtarea.value += "\n"+text;
260
261    // in Safari this causes scrolling
262    if(!is_safari) {
263      txtarea.focus();
264    }
265
266  }
267  // reposition cursor if possible
268  if (txtarea.createTextRange) txtarea.caretPos = document.selection.createRange().duplicate();
269}
270
271/*
272 * Insert the given value at the current cursor position
273 *
274 * @see http://www.alexking.org/index.php?content=software/javascript/content.php
275 */
276function insertAtCarret(edid,value){
277  var field = document.getElementById(edid);
278
279  //IE support
280  if (document.selection) {
281    field.focus();
282    if(opener == null){
283      sel = document.selection.createRange();
284    }else{
285      sel = opener.document.selection.createRange();
286    }
287    sel.text = value;
288  //MOZILLA/NETSCAPE support
289  }else if (field.selectionStart || field.selectionStart == '0') {
290    var startPos  = field.selectionStart;
291    var endPos    = field.selectionEnd;
292    var scrollTop = field.scrollTop;
293    field.value = field.value.substring(0, startPos)
294                  + value
295                  + field.value.substring(endPos, field.value.length);
296
297    field.focus();
298    var cPos=startPos+(value.length);
299    field.selectionStart=cPos;
300    field.selectionEnd=cPos;
301    field.scrollTop=scrollTop;
302  } else {
303    field.value += "\n"+value;
304  }
305  // reposition cursor if possible
306  if (field.createTextRange) field.caretPos = document.selection.createRange().duplicate();
307}
308
309