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