[ Index ] |
|
Code source de DokuWiki 2006-11-06 |
1 /** 2 * Functions for text editing (toolbar stuff) 3 * 4 * @todo I'm no JS guru please help if you know how to improve 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 */ 15 function createToolButton(icon,label,key,id){ 16 var btn = document.createElement('button'); 17 var ico = document.createElement('img'); 18 19 // preapare the basic button stuff 20 btn.className = 'toolbutton'; 21 btn.title = label; 22 if(key){ 23 btn.title += ' [ALT+'+key.toUpperCase()+']'; 24 btn.accessKey = key; 25 } 26 27 // set IDs if given 28 if(id){ 29 btn.id = id; 30 ico.id = id+'_ico'; 31 } 32 33 // create the icon and add it to the button 34 ico.src = DOKU_BASE+'lib/images/toolbar/'+icon; 35 btn.appendChild(ico); 36 37 return btn; 38 } 39 40 /** 41 * Creates a picker window for inserting text 42 * 43 * The given list can be an associative array with text,icon pairs 44 * or a simple list of text. Style the picker window through the picker 45 * class or the picker buttons with the pickerbutton class. Picker 46 * windows are appended to the body and created invisible. 47 * 48 * @author Andreas Gohr <andi@splitbrain.org> 49 */ 50 function createPicker(id,list,icobase,edid){ 51 var cnt = list.length; 52 53 var picker = document.createElement('div'); 54 picker.className = 'picker'; 55 picker.id = id; 56 picker.style.position = 'absolute'; 57 picker.style.display = 'none'; 58 59 for(var key in list){ 60 var btn = document.createElement('button'); 61 62 btn.className = 'pickerbutton'; 63 64 // associative array? 65 if(isNaN(key)){ 66 var ico = document.createElement('img'); 67 ico.src = DOKU_BASE+'lib/images/'+icobase+'/'+list[key]; 68 btn.title = key; 69 btn.appendChild(ico); 70 eval("btn.onclick = function(){pickerInsert('"+id+"','"+ 71 jsEscape(key)+"','"+ 72 jsEscape(edid)+"');return false;}"); 73 }else{ 74 var txt = document.createTextNode(list[key]); 75 btn.title = list[key]; 76 btn.appendChild(txt); 77 eval("btn.onclick = function(){pickerInsert('"+id+"','"+ 78 jsEscape(list[key])+"','"+ 79 jsEscape(edid)+"');return false;}"); 80 } 81 82 picker.appendChild(btn); 83 } 84 var body = document.getElementsByTagName('body')[0]; 85 body.appendChild(picker); 86 } 87 88 /** 89 * Called by picker buttons to insert Text and close the picker again 90 * 91 * @author Andreas Gohr <andi@splitbrain.org> 92 */ 93 function pickerInsert(pickerid,text,edid){ 94 // insert 95 insertAtCarret(edid,text); 96 // close picker 97 pobj = document.getElementById(pickerid); 98 pobj.style.display = 'none'; 99 } 100 101 /** 102 * Show a previosly created picker window 103 * 104 * @author Andreas Gohr <andi@splitbrain.org> 105 */ 106 function showPicker(pickerid,btn){ 107 var picker = document.getElementById(pickerid); 108 var x = findPosX(btn); 109 var y = findPosY(btn); 110 if(picker.style.display == 'none'){ 111 picker.style.display = 'block'; 112 picker.style.left = (x+3)+'px'; 113 picker.style.top = (y+btn.offsetHeight+3)+'px'; 114 }else{ 115 picker.style.display = 'none'; 116 } 117 } 118 119 /** 120 * Create a toolbar 121 * 122 * @param string tbid ID of the element where to insert the toolbar 123 * @param string edid ID of the editor textarea 124 * @param array tb Associative array defining the buttons 125 * @author Andreas Gohr <andi@splitbrain.org> 126 */ 127 function initToolbar(tbid,edid,tb){ 128 var toolbar = $(tbid); 129 if(!toolbar) return; 130 131 //empty the toolbar area: 132 toolbar.innerHTML=''; 133 134 var cnt = tb.length; 135 for(var i=0; i<cnt; i++){ 136 // create new button 137 btn = createToolButton(tb[i]['icon'], 138 tb[i]['title'], 139 tb[i]['key']); 140 141 // add button action dependend on type 142 switch(tb[i]['type']){ 143 case 'format': 144 var sample = tb[i]['title']; 145 if(tb[i]['sample']){ sample = tb[i]['sample']; } 146 147 eval("btn.onclick = function(){insertTags('"+ 148 jsEscape(edid)+"','"+ 149 jsEscape(tb[i]['open'])+"','"+ 150 jsEscape(tb[i]['close'])+"','"+ 151 jsEscape(sample)+ 152 "');return false;}"); 153 toolbar.appendChild(btn); 154 break; 155 case 'insert': 156 eval("btn.onclick = function(){insertAtCarret('"+ 157 jsEscape(edid)+"','"+ 158 jsEscape(tb[i]['insert'])+ 159 "');return false;}"); 160 toolbar.appendChild(btn); 161 break; 162 case 'signature': 163 if(typeof(SIG) != 'undefined' && SIG != ''){ 164 eval("btn.onclick = function(){insertAtCarret('"+ 165 jsEscape(edid)+"','"+ 166 jsEscape(SIG)+ 167 "');return false;}"); 168 toolbar.appendChild(btn); 169 } 170 break; 171 case 'picker': 172 createPicker('picker'+i, 173 tb[i]['list'], 174 tb[i]['icobase'], 175 edid); 176 eval("btn.onclick = function(){showPicker('picker"+i+ 177 "',this);return false;}"); 178 toolbar.appendChild(btn); 179 break; 180 case 'mediapopup': 181 eval("btn.onclick = function(){window.open('"+ 182 jsEscape(tb[i]['url']+NS)+"','"+ 183 jsEscape(tb[i]['name'])+"','"+ 184 jsEscape(tb[i]['options'])+ 185 "');return false;}"); 186 toolbar.appendChild(btn); 187 break; 188 } // end switch 189 } // end for 190 } 191 192 /** 193 * Format selection 194 * 195 * Apply tagOpen/tagClose to selection in textarea, use sampleText instead 196 * of selection if there is none. Copied and adapted from phpBB 197 * 198 * @author phpBB development team 199 * @author MediaWiki development team 200 * @author Andreas Gohr <andi@splitbrain.org> 201 * @author Jim Raynor <jim_raynor@web.de> 202 */ 203 function insertTags(edid,tagOpen, tagClose, sampleText) { 204 var txtarea = document.getElementById(edid); 205 // IE 206 if(document.selection && !is_gecko) { 207 var theSelection = document.selection.createRange().text; 208 var replaced = true; 209 if(!theSelection){ 210 replaced = false; 211 theSelection=sampleText; 212 } 213 txtarea.focus(); 214 215 // This has change 216 var text = theSelection; 217 if(theSelection.charAt(theSelection.length - 1) == " "){// exclude ending space char, if any 218 theSelection = theSelection.substring(0, theSelection.length - 1); 219 r = document.selection.createRange(); 220 r.text = tagOpen + theSelection + tagClose + " "; 221 } else { 222 r = document.selection.createRange(); 223 r.text = tagOpen + theSelection + tagClose; 224 } 225 if(!replaced){ 226 r.moveStart('character',-text.length-tagClose.length); 227 r.moveEnd('character',-tagClose.length); 228 } 229 r.select(); 230 // Mozilla 231 } else if(txtarea.selectionStart || txtarea.selectionStart == '0') { 232 replaced = false; 233 var startPos = txtarea.selectionStart; 234 var endPos = txtarea.selectionEnd; 235 if(endPos - startPos){ replaced = true; } 236 var scrollTop=txtarea.scrollTop; 237 var myText = (txtarea.value).substring(startPos, endPos); 238 if(!myText) { myText=sampleText;} 239 if(myText.charAt(myText.length - 1) == " "){ // exclude ending space char, if any 240 subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " "; 241 } else { 242 subst = tagOpen + myText + tagClose; 243 } 244 txtarea.value = txtarea.value.substring(0, startPos) + subst + 245 txtarea.value.substring(endPos, txtarea.value.length); 246 txtarea.focus(); 247 248 //set new selection 249 if(replaced){ 250 var cPos=startPos+(tagOpen.length+myText.length+tagClose.length); 251 txtarea.selectionStart=cPos; 252 txtarea.selectionEnd=cPos; 253 }else{ 254 txtarea.selectionStart=startPos+tagOpen.length; 255 txtarea.selectionEnd=startPos+tagOpen.length+myText.length; 256 } 257 txtarea.scrollTop=scrollTop; 258 // All others 259 } else { 260 var copy_alertText=alertText; 261 var re1=new RegExp("\\$1","g"); 262 var re2=new RegExp("\\$2","g"); 263 copy_alertText=copy_alertText.replace(re1,sampleText); 264 copy_alertText=copy_alertText.replace(re2,tagOpen+sampleText+tagClose); 265 266 if (sampleText) { 267 text=prompt(copy_alertText); 268 } else { 269 text=""; 270 } 271 if(!text) { text=sampleText;} 272 text=tagOpen+text+tagClose; 273 //append to the end 274 txtarea.value += "\n"+text; 275 276 // in Safari this causes scrolling 277 if(!is_safari) { 278 txtarea.focus(); 279 } 280 281 } 282 // reposition cursor if possible 283 if (txtarea.createTextRange){ 284 txtarea.caretPos = document.selection.createRange().duplicate(); 285 } 286 } 287 288 /* 289 * Insert the given value at the current cursor position 290 * 291 * @see http://www.alexking.org/index.php?content=software/javascript/content.php 292 */ 293 function insertAtCarret(edid,value){ 294 var field = document.getElementById(edid); 295 296 //IE support 297 if (document.selection) { 298 field.focus(); 299 sel = document.selection.createRange(); 300 sel.text = value; 301 //MOZILLA/NETSCAPE support 302 }else if (field.selectionStart || field.selectionStart == '0') { 303 var startPos = field.selectionStart; 304 var endPos = field.selectionEnd; 305 var scrollTop = field.scrollTop; 306 field.value = field.value.substring(0, startPos) + 307 value + 308 field.value.substring(endPos, field.value.length); 309 310 field.focus(); 311 var cPos=startPos+(value.length); 312 field.selectionStart=cPos; 313 field.selectionEnd=cPos; 314 field.scrollTop=scrollTop; 315 } else { 316 field.value += "\n"+value; 317 } 318 // reposition cursor if possible 319 if (field.createTextRange){ 320 field.caretPos = document.selection.createRange().duplicate(); 321 } 322 } 323 324 325 /** 326 * global var used for not saved yet warning 327 */ 328 var textChanged = false; 329 330 /** 331 * Check for changes before leaving the page 332 */ 333 function changeCheck(msg){ 334 if(textChanged){ 335 var ok = confirm(msg); 336 if(ok){ 337 // remove a possibly saved draft using ajax 338 var dwform = $('dw__editform'); 339 if(dwform){ 340 var params = 'call=draftdel'; 341 params += '&id='+encodeURIComponent(dwform.elements.id.value); 342 343 var sackobj = new sack(DOKU_BASE + 'lib/exe/ajax.php'); 344 sackobj.AjaxFailedAlert = ''; 345 sackobj.encodeURIString = false; 346 sackobj.runAJAX(params); 347 // we send this request blind without waiting for 348 // and handling the returned data 349 } 350 } 351 return ok; 352 }else{ 353 return true; 354 } 355 } 356 357 /** 358 * Add changeCheck to all Links and Forms (except those with a 359 * JSnocheck class), add handlers to monitor changes 360 * 361 * Sets focus to the editbox as well 362 */ 363 function initChangeCheck(msg){ 364 if(!document.getElementById){ return false; } 365 // add change check for links 366 var links = document.getElementsByTagName('a'); 367 for(var i=0; i < links.length; i++){ 368 if(links[i].className.indexOf('JSnocheck') == -1){ 369 links[i].onclick = function(){ 370 var rc = changeCheck(msg); 371 if(window.event) window.event.returnValue = rc; 372 return rc; 373 }; 374 links[i].onkeypress = function(){ 375 var rc = changeCheck(msg); 376 if(window.event) window.event.returnValue = rc; 377 return rc; 378 }; 379 } 380 } 381 // add change check for forms 382 var forms = document.forms; 383 for(i=0; i < forms.length; i++){ 384 if(forms[i].className.indexOf('JSnocheck') == -1){ 385 forms[i].onsubmit = function(){ 386 var rc = changeCheck(msg); 387 if(window.event) window.event.returnValue = rc; 388 return rc; 389 }; 390 } 391 } 392 393 // reset change memory var on submit 394 var btn_save = document.getElementById('edbtn__save'); 395 btn_save.onclick = function(){ textChanged = false; }; 396 btn_save.onkeypress = function(){ textChanged = false; }; 397 var btn_prev = document.getElementById('edbtn__preview'); 398 btn_prev.onclick = function(){ textChanged = false; }; 399 btn_prev.onkeypress = function(){ textChanged = false; }; 400 401 // add change memory setter 402 var edit_text = document.getElementById('wiki__text'); 403 edit_text.onchange = function(){ 404 textChanged = true; //global var 405 summaryCheck(); 406 }; 407 edit_text.onkeyup = summaryCheck; 408 var summary = document.getElementById('edit__summary'); 409 addEvent(summary, 'change', summaryCheck); 410 addEvent(summary, 'keyup', summaryCheck); 411 412 // set focus 413 edit_text.focus(); 414 } 415 416 /** 417 * Checks if a summary was entered - if not the style is changed 418 * 419 * @author Andreas Gohr <andi@splitbrain.org> 420 */ 421 function summaryCheck(){ 422 var sum = document.getElementById('edit__summary'); 423 if(sum.value === ''){ 424 sum.className='missing'; 425 }else{ 426 sum.className='edit'; 427 } 428 } 429 430 431 /** 432 * Class managing the timer to display a warning on a expiring lock 433 */ 434 function locktimer_class(){ 435 this.sack = null; 436 this.timeout = 0; 437 this.timerID = null; 438 this.lasttime = null; 439 this.msg = ''; 440 this.pageid = ''; 441 }; 442 var locktimer = new locktimer_class(); 443 locktimer.init = function(timeout,msg,draft){ 444 // init values 445 locktimer.timeout = timeout*1000; 446 locktimer.msg = msg; 447 locktimer.draft = draft; 448 locktimer.lasttime = new Date(); 449 450 if(!$('dw__editform')) return; 451 locktimer.pageid = $('dw__editform').elements.id.value; 452 if(!locktimer.pageid) return; 453 454 // init ajax component 455 locktimer.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php'); 456 locktimer.sack.AjaxFailedAlert = ''; 457 locktimer.sack.encodeURIString = false; 458 locktimer.sack.onCompletion = locktimer.refreshed; 459 460 // register refresh event 461 addEvent($('dw__editform').elements.wikitext,'keyup',function(){locktimer.refresh();}); 462 463 // start timer 464 locktimer.reset(); 465 }; 466 467 /** 468 * (Re)start the warning timer 469 */ 470 locktimer.reset = function(){ 471 locktimer.clear(); 472 locktimer.timerID = window.setTimeout("locktimer.warning()", locktimer.timeout); 473 }; 474 475 /** 476 * Display the warning about the expiring lock 477 */ 478 locktimer.warning = function(){ 479 locktimer.clear(); 480 alert(locktimer.msg); 481 }; 482 483 /** 484 * Remove the current warning timer 485 */ 486 locktimer.clear = function(){ 487 if(locktimer.timerID !== null){ 488 window.clearTimeout(locktimer.timerID); 489 locktimer.timerID = null; 490 } 491 }; 492 493 /** 494 * Refresh the lock via AJAX 495 * 496 * Called on keypresses in the edit area 497 */ 498 locktimer.refresh = function(){ 499 var now = new Date(); 500 // refresh every minute only 501 if(now.getTime() - locktimer.lasttime.getTime() > 30*1000){ //FIXME decide on time 502 var params = 'call=lock&id='+encodeURIComponent(locktimer.pageid); 503 if(locktimer.draft){ 504 var dwform = $('dw__editform'); 505 params += '&prefix='+encodeURIComponent(dwform.elements.prefix.value); 506 params += '&wikitext='+encodeURIComponent(dwform.elements.wikitext.value); 507 params += '&suffix='+encodeURIComponent(dwform.elements.suffix.value); 508 params += '&date='+encodeURIComponent(dwform.elements.date.value); 509 } 510 locktimer.sack.runAJAX(params); 511 locktimer.lasttime = now; 512 } 513 }; 514 515 516 /** 517 * Callback. Resets the warning timer 518 */ 519 locktimer.refreshed = function(){ 520 var data = this.response; 521 var error = data.charAt(0); 522 data = data.substring(1); 523 524 $('draft__status').innerHTML=data; 525 if(error != '1') return; // locking failed 526 locktimer.reset(); 527 }; 528 // end of locktimer class functions 529
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Tue Apr 3 20:47:31 2007 | par Balluche grâce à PHPXref 0.7 |