xref: /dokuwiki/lib/scripts/edit.js (revision cf5038dd45e664aedaa343463e9ecdbd34073e8c)
1/**
2 * Functions for text editing (toolbar stuff)
3 *
4 * @todo most of the stuff in here should be revamped and then moved to toolbar.js
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 * @author Michal Rezler <m.rezler@centrum.cz>
15 */
16function createToolButton(icon,label,key,id,classname){
17    var $btn = jQuery(document.createElement('button')),
18        $ico = jQuery(document.createElement('img'));
19
20    // prepare the basic button stuff
21    $btn.addClass('toolbutton');
22    if(classname){
23        $btn.addClass(classname);
24    }
25
26    $btn.attr('title', label).attr('aria-controls', 'wiki__text');
27    if(key){
28        $btn.attr('title', label + ' ['+key.toUpperCase()+']')
29            .attr('accessKey', key);
30    }
31
32    // set IDs if given
33    if(id){
34        $btn.attr('id', id);
35        $ico.attr('id', id+'_ico');
36    }
37
38    // create the icon and add it to the button
39    if(icon.substr(0,1) !== '/'){
40        icon = DOKU_BASE + 'lib/images/toolbar/' + icon;
41    }
42    $ico.attr('src', icon);
43    $ico.attr('alt', '');
44    $ico.attr('width', 16);
45    $ico.attr('height', 16);
46    $btn.append($ico);
47
48    // we have to return a DOM object (for compatibility reasons)
49    return $btn[0];
50}
51
52/**
53 * Creates a picker window for inserting text
54 *
55 * The given list can be an associative array with text,icon pairs
56 * or a simple list of text. Style the picker window through the picker
57 * class or the picker buttons with the pickerbutton class. Picker
58 * windows are appended to the body and created invisible.
59 *
60 * @param  string id    the ID to assign to the picker
61 * @param  array  props the properties for the picker
62 * @param  string edid  the ID of the textarea
63 * @rteurn DOMobject    the created picker
64 * @author Andreas Gohr <andi@splitbrain.org>
65 */
66function createPicker(id,props,edid){
67    // create the wrapping div
68    var $picker = jQuery(document.createElement('div'));
69
70    $picker.addClass('picker a11y');
71    if(props['class']){
72        $picker.addClass(props['class']);
73    }
74
75    $picker.attr('id', id).css('position', 'absolute');
76
77    function $makebutton(title) {
78        var $btn = jQuery(document.createElement('button'))
79            .addClass('pickerbutton').attr('title', title)
80            .attr('aria-controls', edid)
81            .bind('click', bind(pickerInsert, title, edid))
82            .appendTo($picker);
83        return $btn;
84    }
85
86    jQuery.each(props.list, function (key, item) {
87        if (!props.list.hasOwnProperty(key)) {
88            return;
89        }
90
91        if(isNaN(key)){
92            // associative array -> treat as text => image pairs
93            if (item.substr(0,1) !== '/') {
94                item = DOKU_BASE+'lib/images/'+props.icobase+'/'+item;
95            }
96            jQuery(document.createElement('img'))
97                .attr('src', item)
98                .attr('alt', '')
99                .appendTo($makebutton(key));
100        }else if (typeof item == 'string'){
101            // a list of text -> treat as text picker
102            $makebutton(item).text(item);
103        }else{
104            // a list of lists -> treat it as subtoolbar
105            initToolbar($picker,edid,props.list);
106            return false; // all buttons handled already
107        }
108
109    });
110    jQuery('body').append($picker);
111
112    // we have to return a DOM object (for compatibility reasons)
113    return $picker[0];
114}
115
116/**
117 * Called by picker buttons to insert Text and close the picker again
118 *
119 * @author Andreas Gohr <andi@splitbrain.org>
120 */
121function pickerInsert(text,edid){
122    insertAtCarret(edid,text);
123    pickerClose();
124}
125
126/**
127 * Add button action for signature button
128 *
129 * @param  DOMElement btn   Button element to add the action to
130 * @param  array      props Associative array of button properties
131 * @param  string     edid  ID of the editor textarea
132 * @return boolean    If button should be appended
133 * @author Gabriel Birke <birke@d-scribe.de>
134 */
135function addBtnActionSignature($btn, props, edid) {
136    if(typeof SIG != 'undefined' && SIG != ''){
137        $btn.bind('click', function (e) {
138            insertAtCarret(edid,SIG);
139            e.preventDefault();
140        });
141        return edid;
142    }
143    return '';
144}
145
146/**
147 * Determine the current section level while editing
148 *
149 * @author Andreas Gohr <gohr@cosmocode.de>
150 */
151function currentHeadlineLevel(textboxId){
152    var field = jQuery('#' + textboxId)[0],
153        s = false,
154        opts = [field.value.substr(0,DWgetSelection(field).start)];
155    if (field.form.prefix) {
156        // we need to look in prefix context
157        opts.push(field.form.prefix.value);
158    }
159
160    jQuery.each(opts, function (_, opt) {
161        // Check whether there is a headline in the given string
162        var str = "\n" + opt,
163            lasthl = str.lastIndexOf("\n==");
164        if (lasthl !== -1) {
165            s = str.substr(lasthl+1,6);
166            return false;
167        }
168    });
169    if (s === false) {
170        return 0;
171    }
172    return 7 - s.match(/^={2,6}/)[0].length;
173}
174
175
176/**
177 * global var used for not saved yet warning
178 */
179window.textChanged = false;
180
181/**
182 * Delete the draft before leaving the page
183 */
184function deleteDraft() {
185    if (is_opera || window.keepDraft) {
186        return;
187    }
188
189    var $dwform = jQuery('#dw__editform');
190
191    if($dwform.length === 0) {
192        return;
193    }
194
195    // remove a possibly saved draft using ajax
196    jQuery.post(DOKU_BASE + 'lib/exe/ajax.php',
197        {
198            call: 'draftdel',
199            id: $dwform.find('input[name=id]').val()
200        }
201    );
202}
203
204/**
205 * Activate "not saved" dialog, add draft deletion to page unload,
206 * add handlers to monitor changes
207 *
208 * Sets focus to the editbox as well
209 */
210jQuery(function () {
211    var $editform = jQuery('#dw__editform');
212    if ($editform.length == 0) {
213        return;
214    }
215
216    var $edit_text = jQuery('#wiki__text');
217    if ($edit_text.length > 0) {
218        if($edit_text.attr('readOnly')) {
219            return;
220        }
221
222        // set focus and place cursor at the start
223        var sel = DWgetSelection($edit_text[0]);
224        sel.start = 0;
225        sel.end   = 0;
226        DWsetSelection(sel);
227        $edit_text.focus();
228    }
229
230    var checkfunc = function() {
231        textChanged = true; //global var
232        summaryCheck();
233    };
234
235    $editform.change(checkfunc);
236    $editform.keydown(checkfunc);
237
238    window.onbeforeunload = function(){
239        if(window.textChanged) {
240            return LANG.notsavedyet;
241        }
242    };
243    window.onunload = deleteDraft;
244
245    // reset change memory var on submit
246    jQuery('#edbtn__save').click(
247        function() {
248            window.onbeforeunload = '';
249            textChanged = false;
250        }
251    );
252    jQuery('#edbtn__preview').click(
253        function() {
254            window.onbeforeunload = '';
255            textChanged = false;
256            window.keepDraft = true; // needed to keep draft on page unload
257        }
258    );
259
260    var $summary = jQuery('#edit__summary');
261    $summary.change(summaryCheck);
262    $summary.keyup(summaryCheck);
263
264    if (textChanged) summaryCheck();
265});
266
267/**
268 * Checks if a summary was entered - if not the style is changed
269 *
270 * @author Andreas Gohr <andi@splitbrain.org>
271 */
272function summaryCheck(){
273    var $sum = jQuery('#edit__summary'),
274        missing = $sum.val() === '';
275    $sum.toggleClass('missing', missing).toggleClass('edit', !missing);
276}
277