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