xref: /dokuwiki/lib/scripts/editor.js (revision b838050e5828b5cbf32b9e82ce11c9cc54592809)
1/**
2 * The DokuWiki editor features
3 *
4 * These are the advanced features of the editor. It does NOT contain any
5 * code for the toolbar buttons and it functions. See toolbar.js for that.
6 */
7
8var dw_editor = {
9
10    /**
11     * initialize the default editor functionality
12     *
13     * All other functions can also be called separately for non-default
14     * textareas
15     */
16    init: function(){
17        var $editor = jQuery('#wiki__text');
18        if(!$editor.length) return;
19
20        dw_editor.initSizeCtl('#size__ctl',$editor);
21
22        if($editor.attr('readOnly')) return;
23
24        // in Firefox, keypress doesn't send the correct keycodes,
25        // in Opera, the default of keydown can't be prevented
26        if (jQuery.browser.opera) {
27            $editor.keypress(dw_editor.keyHandler);
28        } else {
29            $editor.keydown(dw_editor.keyHandler);
30        }
31
32    },
33
34    /**
35     * Add the edit window size and wrap controls
36     *
37     * Initial values are read from cookie if it exists
38     *
39     * @param selector ctlarea the div to place the controls
40     * @param selector editor  the textarea to control
41     */
42    initSizeCtl: function(ctlarea,editor){
43        var $ctl      = jQuery(ctlarea);
44        var $textarea = jQuery(editor);
45        if(!$ctl.length || !$textarea.length) return;
46
47        var hgt = DokuCookie.getValue('sizeCtl');
48        if(hgt){
49            $textarea.css('height', hgt);
50        }else{
51            $textarea.css('height', '300px');
52        }
53
54        var wrp = DokuCookie.getValue('wrapCtl');
55        if(wrp){
56            dw_editor.setWrap($textarea[0], wrp);
57        } // else use default value
58
59        var l = document.createElement('img');
60        var s = document.createElement('img');
61        var w = document.createElement('img');
62        l.src = DOKU_BASE+'lib/images/larger.gif';
63        s.src = DOKU_BASE+'lib/images/smaller.gif';
64        w.src = DOKU_BASE+'lib/images/wrap.gif';
65        jQuery(l).click(function(){dw_editor.sizeCtl(editor,100);});
66        jQuery(s).click(function(){dw_editor.sizeCtl(editor,-100);});
67        jQuery(w).click(function(){dw_editor.toggleWrap(editor);});
68        $ctl.append(l);
69        $ctl.append(s);
70        $ctl.append(w);
71    },
72
73    /**
74     * This sets the vertical size of the editbox and adjusts the cookie
75     *
76     * @param selector editor  the textarea to control
77     * @param int val          the relative value to resize in pixel
78     */
79    sizeCtl: function(editor,val){
80        var $textarea = jQuery(editor);
81        var height = parseInt($textarea.css('height'));
82        height += val;
83        $textarea.css('height', height+'px');
84        DokuCookie.setValue('sizeCtl',$textarea.css('height'));
85    },
86
87    /**
88     * Toggle the wrapping mode of the editor textarea and adjusts the
89     * cookie
90     *
91     * @param selector editor  the textarea to control
92     */
93    toggleWrap: function(editor){
94        var $textarea = jQuery(editor);
95        var wrap = textarea.attr('wrap');
96        if(wrap && wrap.toLowerCase() == 'off'){
97            dw_editor.setWrap(textarea[0], 'soft');
98        }else{
99            dw_editor.setWrap(textarea[0], 'off');
100        }
101        DokuCookie.setValue('wrapCtl',$textarea.attr('wrap'));
102    },
103
104    /**
105     * Set the wrapping mode of a textarea
106     *
107     * @author Fluffy Convict <fluffyconvict@hotmail.com>
108     * @author <shutdown@flashmail.com>
109     * @link   http://news.hping.org/comp.lang.javascript.archive/12265.html
110     * @link   https://bugzilla.mozilla.org/show_bug.cgi?id=41464
111     * @param  DomObject textarea
112     * @param  string wrapAttrValue
113     */
114    setWrap: function(textarea, wrapAttrValue){
115        textarea.setAttribute('wrap', wrapAttrValue);
116
117        // Fix display for mozilla
118        var parNod = textarea.parentNode;
119        var nxtSib = textarea.nextSibling;
120        parNod.removeChild(textarea);
121        parNod.insertBefore(textarea, nxtSib);
122    },
123
124    /**
125     * Make intended formattings easier to handle
126     *
127     * Listens to all key inputs and handle indentions
128     * of lists and code blocks
129     *
130     * Currently handles space, backspce and enter presses
131     *
132     * @author Andreas Gohr <andi@splitbrain.org>
133     * @fixme handle tabs
134     * @param event e - the key press event object
135     */
136    keyHandler: function(e){
137        if(e.keyCode != 13 &&
138           e.keyCode != 8  &&
139           e.keyCode != 32) return;
140        var field     = e.target;
141        var selection = getSelection(field);
142        if(selection.getLength()) return; //there was text selected, keep standard behavior
143        var search    = "\n"+field.value.substr(0,selection.start);
144        var linestart = Math.max(search.lastIndexOf("\n"),
145                                 search.lastIndexOf("\r")); //IE workaround
146        search = search.substr(linestart);
147
148        if(e.keyCode == 13){ // Enter
149            // keep current indention for lists and code
150            var match = search.match(/(\n  +([\*-] ?)?)/);
151            if(match){
152                var scroll = field.scrollHeight;
153                var match2 = search.match(/^\n  +[\*-]\s*$/);
154                // Cancel list if the last item is empty (i. e. two times enter)
155                if (match2 && field.value.substr(selection.start).match(/^($|\r?\n)/)) {
156                    field.value = field.value.substr(0, linestart) + "\n" +
157                                  field.value.substr(selection.start);
158                    selection.start = linestart + 1;
159                    selection.end = linestart + 1;
160                    setSelection(selection);
161                } else {
162                    insertAtCarret(field.id,match[1]);
163                }
164                field.scrollTop += (field.scrollHeight - scroll);
165                e.preventDefault(); // prevent enter key
166                return false;
167            }
168        }else if(e.keyCode == 8){ // Backspace
169            // unindent lists
170            var match = search.match(/(\n  +)([*-] ?)$/);
171            if(match){
172                var spaces = match[1].length-1;
173
174                if(spaces > 3){ // unindent one level
175                    field.value = field.value.substr(0,linestart)+
176                                  field.value.substr(linestart+2);
177                    selection.start = selection.start - 2;
178                    selection.end   = selection.start;
179                }else{ // delete list point
180                    field.value = field.value.substr(0,linestart)+
181                                  field.value.substr(selection.start);
182                    selection.start = linestart;
183                    selection.end   = linestart;
184                }
185                setSelection(selection);
186                e.preventDefault(); // prevent backspace
187                return false;
188            }
189        }else if(e.keyCode == 32){ // Space
190            // intend list item
191            var match = search.match(/(\n  +)([*-] )$/);
192            if(match){
193                field.value = field.value.substr(0,linestart)+'  '+
194                              field.value.substr(linestart);
195                selection.start = selection.start + 2;
196                selection.end   = selection.start;
197                setSelection(selection);
198                e.preventDefault(); // prevent space
199                return false;
200            }
201        }
202    }
203
204
205};
206
207jQuery(dw_editor.init);
208