source: documentation/trunk/packages/dokuwiki-2011-05-25a/lib/scripts/edit.js@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 10.8 KB
Line 
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 <[email protected]>
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 <[email protected]>
14 */
15function createToolButton(icon,label,key,id,classname){
16 var btn = document.createElement('button');
17 var ico = document.createElement('img');
18
19 // preapare the basic button stuff
20 btn.className = 'toolbutton';
21 if(classname){
22 btn.className += ' '+classname;
23 }
24 btn.title = label;
25 if(key){
26 btn.title += ' ['+key.toUpperCase()+']';
27 btn.accessKey = key;
28 }
29
30 // set IDs if given
31 if(id){
32 btn.id = id;
33 ico.id = id+'_ico';
34 }
35
36 // create the icon and add it to the button
37 if(icon.substr(0,1) == '/'){
38 ico.src = icon;
39 }else{
40 ico.src = DOKU_BASE+'lib/images/toolbar/'+icon;
41 }
42 btn.appendChild(ico);
43
44 return btn;
45}
46
47/**
48 * Creates a picker window for inserting text
49 *
50 * The given list can be an associative array with text,icon pairs
51 * or a simple list of text. Style the picker window through the picker
52 * class or the picker buttons with the pickerbutton class. Picker
53 * windows are appended to the body and created invisible.
54 *
55 * @param string id the ID to assign to the picker
56 * @param array props the properties for the picker
57 * @param string edid the ID of the textarea
58 * @rteurn DOMobject the created picker
59 * @author Andreas Gohr <[email protected]>
60 */
61function createPicker(id,props,edid){
62 var icobase = props['icobase'];
63 var list = props['list'];
64
65 // create the wrapping div
66 var picker = document.createElement('div');
67 picker.className = 'picker';
68 if(props['class']){
69 picker.className += ' '+props['class'];
70 }
71 picker.id = id;
72 picker.style.position = 'absolute';
73 picker.style.marginLeft = '-10000px'; // no display:none, to keep access keys working
74 picker.style.marginTop = '-10000px';
75
76 for(var key in list){
77 if (!list.hasOwnProperty(key)) continue;
78
79 if(isNaN(key)){
80 // associative array -> treat as image/value pairs
81 var btn = document.createElement('button');
82 btn.className = 'pickerbutton';
83 var ico = document.createElement('img');
84 if(list[key].substr(0,1) == '/'){
85 ico.src = list[key];
86 }else{
87 ico.src = DOKU_BASE+'lib/images/'+icobase+'/'+list[key];
88 }
89 btn.title = key;
90 btn.appendChild(ico);
91 addEvent(btn,'click',bind(pickerInsert,key,edid));
92 picker.appendChild(btn);
93 }else if(isString(list[key])){
94 // a list of text -> treat as text picker
95 var btn = document.createElement('button');
96 btn.className = 'pickerbutton';
97 var txt = document.createTextNode(list[key]);
98 btn.title = list[key];
99 btn.appendChild(txt);
100 addEvent(btn,'click',bind(pickerInsert,list[key],edid));
101 picker.appendChild(btn);
102 }else{
103 // a list of lists -> treat it as subtoolbar
104 initToolbar(picker,edid,list);
105 break; // all buttons handled already
106 }
107
108 }
109 var body = document.getElementsByTagName('body')[0];
110 body.appendChild(picker);
111 return picker;
112}
113
114/**
115 * Called by picker buttons to insert Text and close the picker again
116 *
117 * @author Andreas Gohr <[email protected]>
118 */
119function pickerInsert(text,edid){
120 insertAtCarret(edid,text);
121 pickerClose();
122}
123
124/**
125 * Add button action for signature button
126 *
127 * @param DOMElement btn Button element to add the action to
128 * @param array props Associative array of button properties
129 * @param string edid ID of the editor textarea
130 * @return boolean If button should be appended
131 * @author Gabriel Birke <[email protected]>
132 */
133function addBtnActionSignature(btn, props, edid) {
134 if(typeof(SIG) != 'undefined' && SIG != ''){
135 addEvent(btn,'click',bind(insertAtCarret,edid,SIG));
136 return true;
137 }
138 return false;
139}
140
141/**
142 * Make intended formattings easier to handle
143 *
144 * Listens to all key inputs and handle indentions
145 * of lists and code blocks
146 *
147 * Currently handles space, backspce and enter presses
148 *
149 * @author Andreas Gohr <[email protected]>
150 * @fixme handle tabs
151 */
152function keyHandler(e){
153 if(e.keyCode != 13 &&
154 e.keyCode != 8 &&
155 e.keyCode != 32) return;
156 var field = e.target;
157 var selection = getSelection(field);
158 if(selection.getLength()) return; //there was text selected, keep standard behavior
159 var search = "\n"+field.value.substr(0,selection.start);
160 var linestart = Math.max(search.lastIndexOf("\n"),
161 search.lastIndexOf("\r")); //IE workaround
162 search = search.substr(linestart);
163
164
165 if(e.keyCode == 13){ // Enter
166 // keep current indention for lists and code
167 var match = search.match(/(\n +([\*-] ?)?)/);
168 if(match){
169 var scroll = field.scrollHeight;
170 var match2 = search.match(/^\n +[\*-]\s*$/);
171 // Cancel list if the last item is empty (i. e. two times enter)
172 if (match2 && field.value.substr(selection.start).match(/^($|\r?\n)/)) {
173 field.value = field.value.substr(0, linestart) + "\n" +
174 field.value.substr(selection.start);
175 selection.start = linestart + 1;
176 selection.end = linestart + 1;
177 setSelection(selection);
178 } else {
179 insertAtCarret(field.id,match[1]);
180 }
181 field.scrollTop += (field.scrollHeight - scroll);
182 e.preventDefault(); // prevent enter key
183 return false;
184 }
185 }else if(e.keyCode == 8){ // Backspace
186 // unindent lists
187 var match = search.match(/(\n +)([*-] ?)$/);
188 if(match){
189 var spaces = match[1].length-1;
190
191 if(spaces > 3){ // unindent one level
192 field.value = field.value.substr(0,linestart)+
193 field.value.substr(linestart+2);
194 selection.start = selection.start - 2;
195 selection.end = selection.start;
196 }else{ // delete list point
197 field.value = field.value.substr(0,linestart)+
198 field.value.substr(selection.start);
199 selection.start = linestart;
200 selection.end = linestart;
201 }
202 setSelection(selection);
203 e.preventDefault(); // prevent backspace
204 return false;
205 }
206 }else if(e.keyCode == 32){ // Space
207 // intend list item
208 var match = search.match(/(\n +)([*-] )$/);
209 if(match){
210 field.value = field.value.substr(0,linestart)+' '+
211 field.value.substr(linestart);
212 selection.start = selection.start + 2;
213 selection.end = selection.start;
214 setSelection(selection);
215 e.preventDefault(); // prevent space
216 return false;
217 }
218 }
219}
220
221//FIXME consolidate somewhere else
222addInitEvent(function(){
223 var field = $('wiki__text');
224 if(!field) return;
225 // in Firefox, keypress doesn't send the correct keycodes,
226 // in Opera, the default of keydown can't be prevented
227 if (is_opera) {
228 addEvent(field,'keypress',keyHandler);
229 } else {
230 addEvent(field,'keydown',keyHandler);
231 }
232});
233
234/**
235 * Determine the current section level while editing
236 *
237 * @author Andreas Gohr <[email protected]>
238 */
239function currentHeadlineLevel(textboxId){
240 var field = $(textboxId);
241 var selection = getSelection(field);
242 var search = "\n"+field.value.substr(0,selection.start);
243 var lasthl = search.lastIndexOf("\n==");
244 if(lasthl == -1 && field.form.prefix){
245 // we need to look in prefix context
246 search = field.form.prefix.value;
247 lasthl = search.lastIndexOf("\n==");
248 }
249 search = search.substr(lasthl+1,6);
250
251 if(search == '======') return 1;
252 if(search.substr(0,5) == '=====') return 2;
253 if(search.substr(0,4) == '====') return 3;
254 if(search.substr(0,3) == '===') return 4;
255 if(search.substr(0,2) == '==') return 5;
256
257 return 0;
258}
259
260
261/**
262 * global var used for not saved yet warning
263 */
264window.textChanged = false;
265
266/**
267 * Delete the draft before leaving the page
268 */
269function deleteDraft() {
270 if (is_opera) return;
271 if (window.keepDraft) return;
272
273 // remove a possibly saved draft using ajax
274 var dwform = $('dw__editform');
275 if(dwform){
276 var params = 'call=draftdel';
277 params += '&id='+encodeURIComponent(dwform.elements.id.value);
278
279 var sackobj = new sack(DOKU_BASE + 'lib/exe/ajax.php');
280 // this needs to be synchronous and GET to not be aborted upon page unload
281 sackobj.asynchronous = false;
282 sackobj.method = 'GET';
283 sackobj.AjaxFailedAlert = '';
284 sackobj.encodeURIString = false;
285 sackobj.runAJAX(params);
286 }
287}
288
289/**
290 * Activate "not saved" dialog, add draft deletion to page unload,
291 * add handlers to monitor changes
292 *
293 * Sets focus to the editbox as well
294 */
295addInitEvent(function (){
296 var editform = $('dw__editform');
297 if (!editform) return;
298
299 var edit_text = $('wiki__text');
300 if(edit_text) {
301 if(edit_text.readOnly) return;
302
303 // set focus and place cursor at the start
304 var sel = getSelection(edit_text);
305 sel.start = 0;
306 sel.end = 0;
307 setSelection(sel);
308 edit_text.focus();
309 }
310
311 var checkfunc = function(){
312 window.textChanged = true; //global var
313 summaryCheck();
314 };
315 addEvent(editform, 'change', checkfunc);
316 addEvent(editform, 'keydown', checkfunc);
317
318 window.onbeforeunload = function(){
319 if(window.textChanged) {
320 return LANG.notsavedyet;
321 }
322 };
323 window.onunload = deleteDraft;
324
325 // reset change memory var on submit
326 addEvent($('edbtn__save'), 'click', function(){
327 window.onbeforeunload = '';
328 window.textChanged = false;
329 });
330 addEvent($('edbtn__preview'), 'click', function(){
331 window.onbeforeunload = '';
332 window.textChanged = false;
333 window.keepDraft = true; // needed to keep draft on page unload
334 });
335
336 var summary = $('edit__summary');
337 addEvent(summary, 'change', summaryCheck);
338 addEvent(summary, 'keyup', summaryCheck);
339 if (window.textChanged) summaryCheck();
340});
341
342/**
343 * Checks if a summary was entered - if not the style is changed
344 *
345 * @author Andreas Gohr <[email protected]>
346 */
347function summaryCheck(){
348 var sum = document.getElementById('edit__summary');
349 if(sum.value === ''){
350 sum.className='missing';
351 }else{
352 sum.className='edit';
353 }
354}
355
Note: See TracBrowser for help on using the repository browser.