Fossil SCM
Enable side by side editing and preview. While automatic updating of the preview 'pane'. Tested with Chrome and Firefox under Linux. Only Chrome works.
Commit
20362c85a832af48310cd7edd0a5b3af9c87a834
Parent
68a8a7e925e5cf8…
1 file changed
+168
-10
+168
-10
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -258,10 +258,11 @@ | ||
| 258 | 258 | Blob wiki; |
| 259 | 259 | Manifest *pWiki = 0; |
| 260 | 260 | const char *zPageName; |
| 261 | 261 | char *zHtmlPageName; |
| 262 | 262 | int n; |
| 263 | + int ss; | |
| 263 | 264 | const char *z; |
| 264 | 265 | char *zBody = (char*)P("w"); |
| 265 | 266 | |
| 266 | 267 | if( zBody ){ |
| 267 | 268 | zBody = mprintf("%s", zBody); |
| @@ -336,36 +337,193 @@ | ||
| 336 | 337 | if( zBody==0 ){ |
| 337 | 338 | zBody = mprintf("<i>Empty Page</i>"); |
| 338 | 339 | } |
| 339 | 340 | zHtmlPageName = mprintf("Edit: %s", zPageName); |
| 340 | 341 | style_header(zHtmlPageName); |
| 341 | - if( P("preview")!=0 ){ | |
| 342 | + ss = 0; | |
| 343 | + if(P("ss")) ss = atoi(P("ss")); | |
| 344 | + if( P("ssb")!=0 ){ | |
| 345 | + ss= ss ? 0 : 1; | |
| 346 | + } | |
| 347 | + if(ss){ | |
| 342 | 348 | blob_zero(&wiki); |
| 343 | 349 | blob_append(&wiki, zBody, -1); |
| 344 | - @ Preview:<hr /> | |
| 345 | - wiki_convert(&wiki, 0, 0); | |
| 346 | - @ <hr /> | |
| 347 | - blob_reset(&wiki); | |
| 350 | + } else { | |
| 351 | + if( P("preview")!=0 ){ | |
| 352 | + blob_zero(&wiki); | |
| 353 | + blob_append(&wiki, zBody, -1); | |
| 354 | + @ Preview:<hr /> | |
| 355 | + wiki_convert(&wiki, 0, 0); | |
| 356 | + @ <hr /> | |
| 357 | + blob_reset(&wiki); | |
| 358 | + } | |
| 348 | 359 | } |
| 349 | 360 | for(n=2, z=zBody; z[0]; z++){ |
| 350 | 361 | if( z[0]=='\n' ) n++; |
| 351 | 362 | } |
| 352 | 363 | if( n<20 ) n = 20; |
| 353 | 364 | if( n>40 ) n = 40; |
| 354 | - @ <form method="post" action="%s(g.zTop)/wikiedit"><div> | |
| 365 | + | |
| 366 | + if(ss){ | |
| 367 | + @ <div id="twocolumns" style="width: 100%%;overflow:hidden"> | |
| 368 | + @ <div id="colright" style="display:block;float:right;width:50%%;"> | |
| 369 | + @ <div><h3>Preview</h3></div><div id="previewdiv"> | |
| 370 | + wiki_convert(&wiki, 0, 0); | |
| 371 | + blob_reset(&wiki); | |
| 372 | + @ </div></div> | |
| 373 | + @ <div id="colleft" style="width:50%%;"> | |
| 374 | + } | |
| 375 | + @ <form method="POST" action="%s(g.zTop)/wikiedit"> | |
| 355 | 376 | login_insert_csrf_secret(); |
| 356 | 377 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 357 | - @ <textarea name="w" class="wikiedit" cols="80" | |
| 358 | - @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> | |
| 378 | + @ <div> <input type="submit" id="ssb" name="ssb" value="Toggle Side-by-side" /> | |
| 379 | + @ </div><input type="hidden" name="ss" value="%d(ss)" /> | |
| 380 | + @ <textarea id="w" name="w" class="wikiedit" cols="70" rows="40" | |
| 381 | + @ >%h(zBody)</textarea> | |
| 359 | 382 | @ <br /> |
| 360 | - @ <input type="submit" name="preview" value="Preview Your Changes" /> | |
| 383 | + if(!ss){ | |
| 384 | + @ <input type="submit" name="preview" value="Preview Your Changes" /> | |
| 385 | + } | |
| 361 | 386 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 362 | 387 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 363 | - @ </div></form> | |
| 388 | + @ </form> | |
| 389 | + if(ss){ | |
| 390 | + @ </div> | |
| 391 | + @ <script type='text/javascript'> | |
| 392 | + @ function ajax(u,a,cb,errcb){ | |
| 393 | + @ var _url = u; | |
| 394 | + @ var _method ='POST'; | |
| 395 | + @ var _async = a; | |
| 396 | + @ var _userCallback = cb ; | |
| 397 | + @ var _defError = function(s,t){ alert('Ajax err('+s+') : '+t);} ; | |
| 398 | + @ var _errorCallback = errcb === undefined ? _defError : errcb; | |
| 399 | + @ | |
| 400 | + @ var _starting; | |
| 401 | + @ var _request = false; | |
| 402 | + @ var _debug = false; | |
| 403 | + @ var _busy = function(){ | |
| 404 | + @ return (0 < _request.readyState && 4 > _request.readyState); | |
| 405 | + @ } | |
| 406 | + @ | |
| 407 | + @ if (window.XMLHttpRequest){ // Non-IE browsers | |
| 408 | + @ _request = new XMLHttpRequest(); | |
| 409 | + @ } else if (window.ActiveXObject) { // IE | |
| 410 | + @ try { | |
| 411 | + @ _request = new ActiveXObject("Msxml2.XMLHTTP"); | |
| 412 | + @ } catch(e) { | |
| 413 | + @ try { | |
| 414 | + @ _request = new ActiveXObject("Microsoft.XMLHTTP"); | |
| 415 | + @ } catch(E) { | |
| 416 | + @ _request = false; | |
| 417 | + @ } | |
| 418 | + @ } | |
| 419 | + @ } else { | |
| 420 | + @ _request = false; | |
| 421 | + @ _errrorCallback(-1,'Ajax not available!'); | |
| 422 | + @ } | |
| 423 | + @ var _callback =function(){ | |
| 424 | + @ if(4 == _request.readyState){ | |
| 425 | + @ if(200 == _request.status) { | |
| 426 | + @ if(_debug){ | |
| 427 | + @ var _ending = new Date(); | |
| 428 | + @ var sec = (_ending.getTime() - _starting.getTime())/1000; | |
| 429 | + @ alert(sec+" seconds. Callback with text\n "+_request.responseText); | |
| 430 | + @ } | |
| 431 | + @ _userCallback(_request.responseText,_request.responseXML); | |
| 432 | + @ } else { | |
| 433 | + @ _errorCallback(_request.status,_request.status); | |
| 434 | + @ } | |
| 435 | + @ } | |
| 436 | + @ } | |
| 437 | + @ this.abort = function(){ if(_busy()) {_request.abort(); } } | |
| 438 | + @ this.setDebug = function(onOff){ _debug = onOff;} | |
| 439 | + @ this.setMethod = function(onOff){ _method = onOff ? 'POST' : 'GET' ; } | |
| 440 | + @ this.setUrl = function(u){ _url = u; } | |
| 441 | + @ this.setAsync = function(onOff){ _async = onOff; } | |
| 442 | + @ this.send = function(data){ | |
| 443 | + @ if(_debug) alert("in send function\nurl: "+_url | |
| 444 | + @ +"\nmethode "+_method | |
| 445 | + @ +"\nAsync "+_async | |
| 446 | + @ //+"\ndata "+data | |
| 447 | + @ ); | |
| 448 | + @ if(_busy()){ | |
| 449 | + @ if(_debug) alert('call in progress'); | |
| 450 | + @ return 1; | |
| 451 | + @ } | |
| 452 | + @ try { | |
| 453 | + @ _starting = new Date(); | |
| 454 | + @ _request.open(_method,_url,_async) | |
| 455 | + @ if(_async) | |
| 456 | + @ _request.onreadystatechange = _callback; | |
| 457 | + @ _request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); | |
| 458 | + @ _request.send(data); | |
| 459 | + @ if(!_async){ | |
| 460 | + @ _callback(); | |
| 461 | + @ return _request.status; | |
| 462 | + @ } | |
| 463 | + @ } catch (e) { | |
| 464 | + @ _request.abort(); | |
| 465 | + @ } | |
| 466 | + @ } | |
| 467 | + @ } | |
| 468 | + @ x = new ajax("%s(g.zTop)/wikipreview",true,cb) | |
| 469 | + @ //x.setDebug(true) | |
| 470 | + @ | |
| 471 | + @ var sto = 10000 | |
| 472 | + @ window.ta = document.getElementById('w') | |
| 473 | + @ window.ta.onkeydown= changed; | |
| 474 | + @ window.onload = pageLoaded | |
| 475 | + @ window.prvw = document.getElementById('previewdiv') | |
| 476 | + @ window.isChanged | |
| 477 | + @ function cb(text,xml){ | |
| 478 | + @ window.prvw.innerHTML = text; | |
| 479 | + @ return true; | |
| 480 | + @ } | |
| 481 | + @ function preview(){ | |
| 482 | + @ if(window.isChanged){ | |
| 483 | + @ window.isChanged =0 | |
| 484 | + @ x.send("w="+encodeURIComponent(window.ta.value)) | |
| 485 | + @ } | |
| 486 | + @ setTimeout(preview,sto) | |
| 487 | + @ } | |
| 488 | + @ function pageLoaded(e){ | |
| 489 | + @ window.isChanged =0 | |
| 490 | + @ setTimeout(preview,sto) | |
| 491 | + @ } | |
| 492 | + @ function changed(e){ | |
| 493 | + @ window.isChanged++ | |
| 494 | + @ return true | |
| 495 | + @ } | |
| 496 | + @ </script> | |
| 497 | + } | |
| 364 | 498 | manifest_destroy(pWiki); |
| 365 | 499 | style_footer(); |
| 366 | 500 | } |
| 501 | + | |
| 502 | +/* | |
| 503 | +** WEBPAGE: wikipreview | |
| 504 | +** URL: /wikipreview | |
| 505 | +** | |
| 506 | +** render the contents of w | |
| 507 | +** for use with Ajax. This is NOT | |
| 508 | +** a complete web-page! | |
| 509 | +*/ | |
| 510 | +void wikipreview_page(void){ | |
| 511 | + Blob wiki; | |
| 512 | + char *zBody = (char*)P("w"); | |
| 513 | + if( zBody ){ | |
| 514 | + zBody = mprintf("%s", zBody); | |
| 515 | + } | |
| 516 | + login_check_credentials(); | |
| 517 | + if( zBody==0 ){ | |
| 518 | + zBody = mprintf("<i>Empty Page</i>"); | |
| 519 | + } | |
| 520 | + blob_zero(&wiki); | |
| 521 | + blob_append(&wiki, zBody, -1); | |
| 522 | + wiki_convert(&wiki, 0, 0); | |
| 523 | + blob_reset(&wiki); | |
| 524 | +} | |
| 367 | 525 | |
| 368 | 526 | /* |
| 369 | 527 | ** WEBPAGE: wikinew |
| 370 | 528 | ** URL /wikinew |
| 371 | 529 | ** |
| 372 | 530 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -258,10 +258,11 @@ | |
| 258 | Blob wiki; |
| 259 | Manifest *pWiki = 0; |
| 260 | const char *zPageName; |
| 261 | char *zHtmlPageName; |
| 262 | int n; |
| 263 | const char *z; |
| 264 | char *zBody = (char*)P("w"); |
| 265 | |
| 266 | if( zBody ){ |
| 267 | zBody = mprintf("%s", zBody); |
| @@ -336,36 +337,193 @@ | |
| 336 | if( zBody==0 ){ |
| 337 | zBody = mprintf("<i>Empty Page</i>"); |
| 338 | } |
| 339 | zHtmlPageName = mprintf("Edit: %s", zPageName); |
| 340 | style_header(zHtmlPageName); |
| 341 | if( P("preview")!=0 ){ |
| 342 | blob_zero(&wiki); |
| 343 | blob_append(&wiki, zBody, -1); |
| 344 | @ Preview:<hr /> |
| 345 | wiki_convert(&wiki, 0, 0); |
| 346 | @ <hr /> |
| 347 | blob_reset(&wiki); |
| 348 | } |
| 349 | for(n=2, z=zBody; z[0]; z++){ |
| 350 | if( z[0]=='\n' ) n++; |
| 351 | } |
| 352 | if( n<20 ) n = 20; |
| 353 | if( n>40 ) n = 40; |
| 354 | @ <form method="post" action="%s(g.zTop)/wikiedit"><div> |
| 355 | login_insert_csrf_secret(); |
| 356 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 357 | @ <textarea name="w" class="wikiedit" cols="80" |
| 358 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 359 | @ <br /> |
| 360 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 361 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 362 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 363 | @ </div></form> |
| 364 | manifest_destroy(pWiki); |
| 365 | style_footer(); |
| 366 | } |
| 367 | |
| 368 | /* |
| 369 | ** WEBPAGE: wikinew |
| 370 | ** URL /wikinew |
| 371 | ** |
| 372 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -258,10 +258,11 @@ | |
| 258 | Blob wiki; |
| 259 | Manifest *pWiki = 0; |
| 260 | const char *zPageName; |
| 261 | char *zHtmlPageName; |
| 262 | int n; |
| 263 | int ss; |
| 264 | const char *z; |
| 265 | char *zBody = (char*)P("w"); |
| 266 | |
| 267 | if( zBody ){ |
| 268 | zBody = mprintf("%s", zBody); |
| @@ -336,36 +337,193 @@ | |
| 337 | if( zBody==0 ){ |
| 338 | zBody = mprintf("<i>Empty Page</i>"); |
| 339 | } |
| 340 | zHtmlPageName = mprintf("Edit: %s", zPageName); |
| 341 | style_header(zHtmlPageName); |
| 342 | ss = 0; |
| 343 | if(P("ss")) ss = atoi(P("ss")); |
| 344 | if( P("ssb")!=0 ){ |
| 345 | ss= ss ? 0 : 1; |
| 346 | } |
| 347 | if(ss){ |
| 348 | blob_zero(&wiki); |
| 349 | blob_append(&wiki, zBody, -1); |
| 350 | } else { |
| 351 | if( P("preview")!=0 ){ |
| 352 | blob_zero(&wiki); |
| 353 | blob_append(&wiki, zBody, -1); |
| 354 | @ Preview:<hr /> |
| 355 | wiki_convert(&wiki, 0, 0); |
| 356 | @ <hr /> |
| 357 | blob_reset(&wiki); |
| 358 | } |
| 359 | } |
| 360 | for(n=2, z=zBody; z[0]; z++){ |
| 361 | if( z[0]=='\n' ) n++; |
| 362 | } |
| 363 | if( n<20 ) n = 20; |
| 364 | if( n>40 ) n = 40; |
| 365 | |
| 366 | if(ss){ |
| 367 | @ <div id="twocolumns" style="width: 100%%;overflow:hidden"> |
| 368 | @ <div id="colright" style="display:block;float:right;width:50%%;"> |
| 369 | @ <div><h3>Preview</h3></div><div id="previewdiv"> |
| 370 | wiki_convert(&wiki, 0, 0); |
| 371 | blob_reset(&wiki); |
| 372 | @ </div></div> |
| 373 | @ <div id="colleft" style="width:50%%;"> |
| 374 | } |
| 375 | @ <form method="POST" action="%s(g.zTop)/wikiedit"> |
| 376 | login_insert_csrf_secret(); |
| 377 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 378 | @ <div> <input type="submit" id="ssb" name="ssb" value="Toggle Side-by-side" /> |
| 379 | @ </div><input type="hidden" name="ss" value="%d(ss)" /> |
| 380 | @ <textarea id="w" name="w" class="wikiedit" cols="70" rows="40" |
| 381 | @ >%h(zBody)</textarea> |
| 382 | @ <br /> |
| 383 | if(!ss){ |
| 384 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 385 | } |
| 386 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 387 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 388 | @ </form> |
| 389 | if(ss){ |
| 390 | @ </div> |
| 391 | @ <script type='text/javascript'> |
| 392 | @ function ajax(u,a,cb,errcb){ |
| 393 | @ var _url = u; |
| 394 | @ var _method ='POST'; |
| 395 | @ var _async = a; |
| 396 | @ var _userCallback = cb ; |
| 397 | @ var _defError = function(s,t){ alert('Ajax err('+s+') : '+t);} ; |
| 398 | @ var _errorCallback = errcb === undefined ? _defError : errcb; |
| 399 | @ |
| 400 | @ var _starting; |
| 401 | @ var _request = false; |
| 402 | @ var _debug = false; |
| 403 | @ var _busy = function(){ |
| 404 | @ return (0 < _request.readyState && 4 > _request.readyState); |
| 405 | @ } |
| 406 | @ |
| 407 | @ if (window.XMLHttpRequest){ // Non-IE browsers |
| 408 | @ _request = new XMLHttpRequest(); |
| 409 | @ } else if (window.ActiveXObject) { // IE |
| 410 | @ try { |
| 411 | @ _request = new ActiveXObject("Msxml2.XMLHTTP"); |
| 412 | @ } catch(e) { |
| 413 | @ try { |
| 414 | @ _request = new ActiveXObject("Microsoft.XMLHTTP"); |
| 415 | @ } catch(E) { |
| 416 | @ _request = false; |
| 417 | @ } |
| 418 | @ } |
| 419 | @ } else { |
| 420 | @ _request = false; |
| 421 | @ _errrorCallback(-1,'Ajax not available!'); |
| 422 | @ } |
| 423 | @ var _callback =function(){ |
| 424 | @ if(4 == _request.readyState){ |
| 425 | @ if(200 == _request.status) { |
| 426 | @ if(_debug){ |
| 427 | @ var _ending = new Date(); |
| 428 | @ var sec = (_ending.getTime() - _starting.getTime())/1000; |
| 429 | @ alert(sec+" seconds. Callback with text\n "+_request.responseText); |
| 430 | @ } |
| 431 | @ _userCallback(_request.responseText,_request.responseXML); |
| 432 | @ } else { |
| 433 | @ _errorCallback(_request.status,_request.status); |
| 434 | @ } |
| 435 | @ } |
| 436 | @ } |
| 437 | @ this.abort = function(){ if(_busy()) {_request.abort(); } } |
| 438 | @ this.setDebug = function(onOff){ _debug = onOff;} |
| 439 | @ this.setMethod = function(onOff){ _method = onOff ? 'POST' : 'GET' ; } |
| 440 | @ this.setUrl = function(u){ _url = u; } |
| 441 | @ this.setAsync = function(onOff){ _async = onOff; } |
| 442 | @ this.send = function(data){ |
| 443 | @ if(_debug) alert("in send function\nurl: "+_url |
| 444 | @ +"\nmethode "+_method |
| 445 | @ +"\nAsync "+_async |
| 446 | @ //+"\ndata "+data |
| 447 | @ ); |
| 448 | @ if(_busy()){ |
| 449 | @ if(_debug) alert('call in progress'); |
| 450 | @ return 1; |
| 451 | @ } |
| 452 | @ try { |
| 453 | @ _starting = new Date(); |
| 454 | @ _request.open(_method,_url,_async) |
| 455 | @ if(_async) |
| 456 | @ _request.onreadystatechange = _callback; |
| 457 | @ _request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); |
| 458 | @ _request.send(data); |
| 459 | @ if(!_async){ |
| 460 | @ _callback(); |
| 461 | @ return _request.status; |
| 462 | @ } |
| 463 | @ } catch (e) { |
| 464 | @ _request.abort(); |
| 465 | @ } |
| 466 | @ } |
| 467 | @ } |
| 468 | @ x = new ajax("%s(g.zTop)/wikipreview",true,cb) |
| 469 | @ //x.setDebug(true) |
| 470 | @ |
| 471 | @ var sto = 10000 |
| 472 | @ window.ta = document.getElementById('w') |
| 473 | @ window.ta.onkeydown= changed; |
| 474 | @ window.onload = pageLoaded |
| 475 | @ window.prvw = document.getElementById('previewdiv') |
| 476 | @ window.isChanged |
| 477 | @ function cb(text,xml){ |
| 478 | @ window.prvw.innerHTML = text; |
| 479 | @ return true; |
| 480 | @ } |
| 481 | @ function preview(){ |
| 482 | @ if(window.isChanged){ |
| 483 | @ window.isChanged =0 |
| 484 | @ x.send("w="+encodeURIComponent(window.ta.value)) |
| 485 | @ } |
| 486 | @ setTimeout(preview,sto) |
| 487 | @ } |
| 488 | @ function pageLoaded(e){ |
| 489 | @ window.isChanged =0 |
| 490 | @ setTimeout(preview,sto) |
| 491 | @ } |
| 492 | @ function changed(e){ |
| 493 | @ window.isChanged++ |
| 494 | @ return true |
| 495 | @ } |
| 496 | @ </script> |
| 497 | } |
| 498 | manifest_destroy(pWiki); |
| 499 | style_footer(); |
| 500 | } |
| 501 | |
| 502 | /* |
| 503 | ** WEBPAGE: wikipreview |
| 504 | ** URL: /wikipreview |
| 505 | ** |
| 506 | ** render the contents of w |
| 507 | ** for use with Ajax. This is NOT |
| 508 | ** a complete web-page! |
| 509 | */ |
| 510 | void wikipreview_page(void){ |
| 511 | Blob wiki; |
| 512 | char *zBody = (char*)P("w"); |
| 513 | if( zBody ){ |
| 514 | zBody = mprintf("%s", zBody); |
| 515 | } |
| 516 | login_check_credentials(); |
| 517 | if( zBody==0 ){ |
| 518 | zBody = mprintf("<i>Empty Page</i>"); |
| 519 | } |
| 520 | blob_zero(&wiki); |
| 521 | blob_append(&wiki, zBody, -1); |
| 522 | wiki_convert(&wiki, 0, 0); |
| 523 | blob_reset(&wiki); |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | ** WEBPAGE: wikinew |
| 528 | ** URL /wikinew |
| 529 | ** |
| 530 |