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