1/** 2* The fastwiki plugin loads 'do' actions as AJAX requests when possible, to speed up the page. It also adds section editing. 3*/ 4var plugin_fastwiki = (function($) { 5 var m_viewMode, m_origViewMode, m_prevView; // show, edit, subscribe etc 6 var m_isSecedit, m_wasSecedit; 7 var m_hasDraft; 8 var m_pageObjs = {}; // Edit objects 9 var m_content; 10 var m_initialId; 11 var m_debug = document.location.host == 'localhost'; 12 var m_cache = new CPageCache(JSINFO.fastwiki.preload_per_page, JSINFO.fastwiki.preload_batchsize, m_debug); 13 // Unsupported actions, and reason for lack of support: 14 // login, register and resendpwd: Templates, plugins or future versions of dokuwiki might make them https. 15 // admin: Admin can change things outside the main content area. 16 // conflict, denied and locked: I don't know what they do. 17 // showtag: It doesn't work properly with unicode tags. 18 var m_supportedActions = {'':1, edit:1, draft:1, history:1, recent:1, revisions:1, show:1, subscribe:1, backlink:1, index:1, profile:1, media:1, diff:1, save:1}; 19 var m_modeClassElt; 20 var m_browserHistory = new CBrowserHistory(); 21 22 23 ////////// 24 // On load initialization 25 ////////// 26 $(function() { 27 // Leaving imgdetail with ajax is just too complicated to support. 28 if (document.location.href.indexOf("detail.php") >= 0) 29 m_viewMode = 'unsupported'; 30 else { 31 var urlParams = _urlToObj(document.location.href); 32 m_viewMode = urlParams['do'] || 'show'; 33 if (!m_supportedActions[m_viewMode]) 34 m_viewMode = 'unsupported'; 35 else if (window.tpl_fastwiki_startmode_support && !(m_viewMode in tpl_fastwiki_startmode_support)) 36 m_viewMode = 'unsupported'; 37 } 38 m_origViewMode = m_viewMode; 39 40 // plugin_fastwiki_marker was added by the action plugin. It makes it possible to find the main content area regardless of the template used. 41 m_content = $('.plugin_fastwiki_marker').parent(); 42 m_content.addClass('content_initial'); 43 m_initialId = m_content.attr('id'); 44 45 m_modeClassElt = m_content.hasClass('dokuwiki') ? m_content : $(m_content.parents('.dokuwiki')[0] || document.body); 46 47 $(window).trigger('fastwiki:init', [m_viewMode]); 48 49 if (JSINFO.fastwiki.fastpages) 50 fixActionLinks(document.body); 51 52 // The feature is not supported by IE 9 and below. 53 if (JSINFO.fastwiki.fastshow && (m_origViewMode != 'show' || !window.history || !history.pushState)) 54 JSINFO.fastwiki.fastshow = false; 55 56 if (JSINFO.fastwiki.fastshow) 57 m_browserHistory.init(load); 58 }); 59 60 61 /** 62 * Find supported action links (do=???) and references to the current page, and turn them into AJAX requests. 63 * 64 * @param {DOMNode} elt - Do it inside this element. 65 */ 66 function fixActionLinks(elt) { 67 if (m_origViewMode == 'unsupported') 68 return; 69 70 var formActions = {search: 1}; 71 var supportedFields = {'do':1, rev:1, id:1}; 72 73 // TODO: Support search: Hook search box, not just href. Note that supporting search changes doku behavior -- search results now have namespaces and origin pages. 74 // Because of this, search will have to be a separate config setting. 75 // TODO: Profile needs button hooks. 76 77 // Intercept all action (do=) urls, switching them to AJAX. 78 $('a[href *= "?do="]', elt).click(function(e) { 79 if (this.href.indexOf('block_fastwiki') < 0) { 80 var curPathId = JSINFO.id.replace(/:/g, '/'); 81 var aPathId = this.href.replace(/\?.*$/, '').replace(/:/g, '/'); 82 // The magic only works if the do= is for the current page. 83 if (aPathId.substr(-curPathId.length) == curPathId) { 84 var params = _urlToObj(this.href); 85 if (!params['do']) 86 params['do'] = 'show'; 87 88 if (params['do'] in m_supportedActions) { 89 e.preventDefault(); 90 load(params['do'], null, params); 91 } 92 } 93 } 94 }); 95 96 $('input[type="submit"], input[type="button"], button', elt).click(function(e) { 97 var form = $(this).parents('form'); 98 if (form.length > 0 && form[0]['do'] && form[0]['do'].value in m_supportedActions) { 99 // For now, only allow very simple forms 100 var supported = true; 101 $('input[type != "submit"]', form).each(function(idx, elt) { 102 if (elt.type != 'button' && (elt.type != 'hidden' || !(elt.name in supportedFields))) 103 supported = false; 104 }); 105 106 if (supported) { 107 e.preventDefault(); 108 var params = _formToObj(form[0]); 109 if (!params['do']) 110 params['do'] = 'show'; 111 load(params['do'], null, params); 112 } 113 } 114 }); 115 116 // Only fix self-referrential links if we started out in show mode. 117 if (m_origViewMode == 'show' && window.JSINFO) { 118 var pathId = JSINFO.id.replace(/:/g, '/'); 119 // Handle all anchors instead of using CSS selectors to narrow it down, since the current id can change. 120 $('a', elt).click(function(e) { 121 // TODO Document: Doesn't work with cannonical url feature. 122 var href = this.getAttribute('href'); // Use getAttribute because some browsers make href appear to be cannonical. 123 if (href && href.indexOf('://') < 0) { 124 if (href.match(m_browserHistory.getSelfRefRegex())) { 125 load('show'); 126 e.preventDefault(); 127 } 128 else if (JSINFO.fastwiki.fastshow) { 129 var numParams = href.split('=').length; 130 if (href.indexOf('id=') >= 0) 131 numParams--; 132 if (numParams == 1) { 133 //TODO: What about pages that aren't in the wiki at all? Forums etc. Use a config field? 134 if (m_browserHistory.switchBasePath(href)) { 135 m_viewMode = null; 136 e.preventDefault(); 137 } 138 } 139 } 140 } 141 }); 142 // Old selector: 143 // 'a[href $= "doku.php?id=' + JSINFO.id + '"], a[href $= "doku.php/' + pathId + '"], a[href = "/' + pathId + '"]' 144 } 145 146 // Inline section edit 147 if (JSINFO.fastwiki.secedit) { 148 $('.editbutton_section .btn_secedit input[type=submit], .editbutton_section .btn_secedit button', elt).click(function(e) { 149 e.preventDefault(); 150 var form = $(this).parents('form'); 151 load('edit', form, _formToObj(form)); 152 }); 153 } 154 155 if (JSINFO.fastwiki.preload) 156 m_cache.load(elt, m_browserHistory); 157 } 158 159 160 /** 161 * Preview a page edit without reloading the page. 162 * 163 * @private 164 * @param {Form=} sectionForm - If a section is being edited instead of the whole document, this is the form in that section. 165 */ 166 function _preview(sectionForm) { 167 var body = $(document.body); 168 var params = _formToObj($('#dw__editform')); 169 params['do'] = 'preview'; 170 _sendPartial(params, $('.dokuwiki .editBox'), function(data) { 171 var preview = $('<div id="preview_container">' + data + '</div>'); 172 173 // In case anything changed, migrate values over to the existing form. 174 var pvEditor = preview.find('#dw__editform'); 175 var editor = $('#dw__editform')[0]; 176 pvEditor.find('input[type=hidden]').each(function(idx, elt) { 177 editor[elt.name].value = elt.value; 178 }); 179 180 // Strip out the editor. We already have that. 181 preview.find('#scroll__here').prevAll().remove(); 182 183 var oldpreview = $('#preview_container'); 184 if (oldpreview.length > 0) 185 oldpreview.replaceWith(preview); 186 else 187 $('.content_partial').append(preview); 188 189 setTimeout(function() { 190 $('html,body').animate({scrollTop: $('#scroll__here').offset().top+'px'}, 300); 191 }, 1); 192 }, 'text'); 193 } 194 195 196 /** 197 * Get an editable page section. 198 * Algorithm taken from dw_page.sectionHighlight(). 199 * 200 * @private 201 * @param {Form=} sectionForm - The form representing the editable section. 202 */ 203 function _getSection(sectionForm) { 204 var pieces = $(); 205 var target = sectionForm.parent(); 206 var nr = target.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2]; 207 208 // Walk the dom tree in reverse to find the sibling which is or contains the section edit marker 209 while (target.length > 0 && !(target.hasClass('sectionedit' + nr) || target.find('.sectionedit' + nr).length)) { 210 target = target.prev(); 211 212 // section_highlight is a temporary div that wraps around the text when your cursor is 213 // over the edit icon. Since this div will go away when edit starts, you don't want to 214 // keep the div itself. Instead, grab all of its children. 215 if (target.hasClass('section_highlight')) 216 pieces = pieces.add(target.children()); 217 else 218 pieces = pieces.add(target); 219 220 } 221 return pieces; 222 } 223 224 225 /** 226 * Switch focus to the editor. 227 */ 228 function _focusEdit() { 229 var $edit_text = $('#wiki__text'); 230 if ($edit_text.length > 0 && !$edit_text.attr('readOnly')) { 231 // set focus and place cursor at the start 232 var sel = DWgetSelection($edit_text[0]); 233 sel.start = 0; 234 sel.end = 0; 235 try { 236 DWsetSelection(sel); 237 } 238 catch(e) { 239 // DWsetSelection can sometimes fail in FireFox. Try again later. 240 setTimeout(function() { 241 try { 242 DWsetSelection(sel); 243 } 244 catch(e) { 245 } 246 }, 500); 247 } 248 $edit_text.focus(); 249 } 250 } 251 252 253 /** 254 * Initialize a page edit. This must be called every time the editor is loaded. 255 * Most of this function was derived from core DokuWiki scripts, because Doku doesn't have init functions -- it does 256 * all initialization in global jQuery DOMContentReady scope. 257 * 258 * @private 259 */ 260 function _initEdit() { 261 // Comatibility: before 2016-06-26a 262 if (!window.doku_summaryCheck) { 263 window.doku_summaryCheck = window.summaryCheck; 264 } 265 266 dw_editor.init(); 267 dw_locktimer.init(JSINFO.fastwiki.locktime, JSINFO.fastwiki.usedraft); 268 269 // From edit.js 270 var $editform = $('#dw__editform'); 271 if ($editform.length == 0) 272 return; 273 274 var $edit_text = $('#wiki__text'); 275 if ($edit_text.length > 0) { 276 if($edit_text.attr('readOnly')) { 277 return; 278 } 279 280 // set focus and place cursor at the start 281 var sel = DWgetSelection($edit_text[0]); 282 sel.start = 0; 283 sel.end = 0; 284 DWsetSelection(sel); 285 $edit_text.trigger('focus'); 286 287 doku_edit_text_content = $edit_text.val(); 288 } 289 290 $editform.on("change keydown", function(e) { 291 window.textChanged = true; 292 doku_summaryCheck(); 293 }); 294 295 m_pageObjs.content = $edit_text.val(); 296 window.onbeforeunload = function() { 297 if (window.textChanged && m_pageObjs.content != $edit_text.val()) 298 return LANG.notsavedyet; 299 }; 300 window.onunload = deleteDraft; 301 302 $('#edbtn__preview').click(function(e) { 303 if (JSINFO.fastwiki.preview) { 304 e.preventDefault(); 305 _preview(m_pageObjs.sectionForm); 306 m_hasDraft = true; 307 dw_locktimer.reset(); 308 } 309 else { 310 // Original behavior from edit.js. 311 window.onbeforeunload = ''; 312 textChanged = false; 313 window.keepDraft = true; 314 } 315 }); 316 317 $('#edit__summary').on("change keyup", doku_summaryCheck); 318 if (textChanged) 319 doku_summaryCheck(); 320 321 // From toolbar.js 322 initToolbar('tool__bar','wiki__text', toolbar); 323 $('#tool__bar').attr('role', 'toolbar'); 324 325 // Work-around for https://github.com/splitbrain/dokuwiki/issues/3466 326 // See lib/scripts/linkwiz.js:init 327 setTimeout(function() { 328 dw_linkwiz.$wiz = null; 329 $('#link__wiz').remove(); 330 // Position is usually calculated from the editor, which breaks if the editor has a positioned parent. 331 // Instead, calculate based on the button. 332 dw_linkwiz.init($('[aria-controls="link__wiz"]')); 333 // Now restore linkwiz's link to the editor. 334 dw_linkwiz.textArea = $('#wiki__text')[0]; 335 }, 0); 336 337 // reset change memory var on submit 338 $('#edbtn__save').click(function(e) { 339 textChanged = false; 340 341 // Do a fast save if we started on 'show' and we're not creating or deleting the page. 342 if (JSINFO.fastwiki.save && m_origViewMode == 'show' && $edit_text.val().length>0 && m_pageObjs.content.length>0) { 343 e.preventDefault(); 344 load('save', null, _formToObj($('#dw__editform'))); 345 } 346 // Invalidate the cache if fastwiki.save is off. If it's on, the cache will be updated after save. 347 else 348 m_cache.remove(JSINFO.id); 349 350 window.onbeforeunload = ''; 351 dw_locktimer.clear(); 352 }); 353 354 // Cancel button on edit, or Delete Draft button on draft. 355 $('input[name="do[draftdel]"]', $editform).click(function(e) { 356 e.preventDefault(); 357 var id = $editform.find('input[name=id]').val(); 358 load('show'); 359 360 if (!window.keepDraft) { 361 // Silently remove a possibly saved draft using ajax. 362 jQuery.post(DOKU_BASE + 'lib/exe/ajax.php', { 363 call: 'draftdel', 364 id: id, 365 success: function(data, textStatus, jqXHR) { 366 m_hasDraft = false; 367 } 368 }); 369 } 370 }); 371 // Cancel button on draft 372 $('input[name="do[show]"]', $editform).click(function(e) { 373 e.preventDefault(); 374 load('show'); 375 }); 376 377 $('.picker.pk_hl').addClass('dokuwiki'); 378 } 379 380 381 /** 382 * Change the current body class to represent the given action. 383 * 384 * @private 385 * @param {String} action - The new page action. 386 * @param {String=} target - The part of the page being targetted. Can be one of: {section} 387 */ 388 function _setBodyClass(action, target) { 389 for (var k in m_supportedActions) 390 m_modeClassElt.removeClass('mode_' + k); 391 m_modeClassElt.addClass('mode_'+action); 392 // Special case for section edit. 393 if (target == 'section') 394 m_modeClassElt.removeClass('mode_edit').addClass('mode_show mode_secedit'); 395 396 $('.content_partial').toggle(m_viewMode != m_origViewMode); 397 $('.content_initial').toggle(m_viewMode == m_origViewMode || target == 'section'); 398 } 399 400 401 /** 402 * Update page objects on view switch. 403 * 404 * @private 405 */ 406 function _updatePageObjsOnSwitch() { 407 if (m_pageObjs.showOnSwitch) 408 m_pageObjs.showOnSwitch.show(); 409 delete m_pageObjs.showOnSwitch; 410 delete m_pageObjs.content; 411 delete m_pageObjs.sectionForm; 412 413 var hasToc = {show: 1, admin: 1}; 414 415 // #dw__toc is common to all templates. #dw_toc_head is from the zioth template. #dw_toc is from starterbootstrap 416 $("#dw__toc, #dw_toc_head, #dw_toc").css('display', m_viewMode in hasToc ? '' : 'none'); 417 } 418 419 420 /** 421 * Convert a form to an object suitable for $.post(). 422 * 423 * @private 424 */ 425 function _formToObj(form) { 426 var obj = {}; 427 $(form).serializeArray().map(function(item){obj[item.name] = item.value;}); 428 return obj; 429 } 430 431 432 /** 433 * Convert a url to an object suitable for $.post(). 434 * 435 * @private 436 */ 437 function _urlToObj(url) { 438 var obj = {}; 439 var a = url.replace(/.*\?/, '').split('&'); 440 for (var x=0; x<a.length; x++) { 441 var parts = unescape(a[x]).split('='); 442 var name = parts.shift(); 443 obj[name] = parts.join('='); // Restore any lost = signs from the split. 444 } 445 return obj; 446 } 447 448 449 /** 450 * Side effects of performing various actions. 451 * 452 * @private 453 */ 454 var m_actionEffects = { 455 subscribe: function(params, extraData) { 456 // Subscribe actions are a special case. Rather than replace the content, they add a success or error message to the top. 457 function subscribeAction(sparams) { 458 _sendPartial(sparams, _getVisibleContent(), function(data) { 459 // data is just a success or error message. 460 load(m_origViewMode); 461 462 var body = $('<div class="message_partial"></div>').append(data); 463 $('.content_initial').before(body); 464 }, 'text'); 465 } 466 467 var form = $('#subscribe__form'); 468 $('input[name="do[subscribe]"]', form).click(function(e) { 469 e.preventDefault(); 470 subscribeAction(_formToObj(form)); 471 }); 472 473 $('.content_partial .unsubscribe').click(function(e) { 474 e.preventDefault(); 475 subscribeAction(_urlToObj(this.href)); 476 }); 477 }, 478 index: function(params, extraData) { 479 // Global init from index.js 480 dw_index.$obj = $('#index__tree'); 481 dw_index.init(); 482 }, 483 edit: function(params, extraData) { 484 var draft = params['do'] == 'draft'; 485 if (m_hasDraft === true) 486 draft = true; 487 else if (m_hasDraft === false) 488 draft = params.rev = null; 489 if (extraData.sectionForm) { 490 // Define showOnSwitch here, not above, so _updatePageObjsOnSwitch doesn't re-show them too early. 491 m_pageObjs.sectionForm = extraData.sectionForm; // Redefine. 492 extraData.sectionParts = extraData.sectionParts.add('.editbutton_section'); 493 m_pageObjs.showOnSwitch = extraData.sectionParts; 494 m_pageObjs.showOnSwitch.hide(); 495 _initEdit(); 496 _focusEdit(); 497 } 498 else 499 _initEdit(); 500 501 setTimeout(function() { 502 // Focusing the editor causes the browser to scroll, so wait until it's likely to be in view (after the page is rearranged) before calling this. 503 _focusEdit(); 504 505 if (document.body.scrollTop > 0) 506 $('html,body').animate({scrollTop: Math.max(0, Math.floor($('.content_partial').offset().top)-20)+'px'}, 300); 507 }, 1); 508 }, 509 revisions: function(params, extraData) { 510 $('.content_partial form').each(function(idx, form) { 511 $('input[name="do[diff]"]', form).click(function(e) { 512 e.preventDefault(); 513 load('diff', null, _formToObj(form)); 514 }); 515 }); 516 }, 517 save: function(params, extraData) { 518 // If dates don't match, there's a conflict. 519 if ($('.content_partial #a_newer_version_exists').length > 0) { 520 m_viewMode = 'edit'; 521 m_actionEffects.edit(params, m_pageObjs.sectionForm ? {sectionForm: m_pageObjs.sectionForm, sectionParts:_getSection(m_pageObjs.sectionForm)} : {}); 522 523 var editform = $('#dw__editform'); 524 $('input[name="do[save]"]', editform).click(function(e) { 525 e.preventDefault(); 526 load('save', null, _formToObj(editform)); 527 window.onbeforeunload = ''; 528 dw_locktimer.clear(); 529 }); 530 531 // Cancel button on edit, or Delete Draft button on draft. 532 $('input[name="do[cancel]"]', editform).click(function(e) { 533 e.preventDefault(); 534 var id = editform.find('input[name=id]').val(); 535 load('show'); 536 537 if (!window.keepDraft) { 538 // Silently remove a possibly saved draft using ajax. 539 jQuery.post(DOKU_BASE + 'lib/exe/ajax.php', { 540 call: 'draftdel', 541 id: id, 542 success: function(data, textStatus, jqXHR) { 543 m_hasDraft = false; 544 } 545 }); 546 } 547 }); 548 } 549 // Recoverable error. Return to the edit form. 550 else if ($('.content_partial #dw__editform').length > 0) { 551 m_viewMode = 'edit'; 552 m_actionEffects.edit(params, m_pageObjs.sectionForm ? {sectionForm: m_pageObjs.sectionForm, sectionParts:_getSection(m_pageObjs.sectionForm)} : {}); 553 } 554 // When ACL fails, a preview is returned. 555 else if ($('.content_partial #preview').length > 0) { 556 //TODO: How do I handle this case? Test. 557 load('show'); 558 } 559 else { 560 $('.content_initial').html($('.content_partial').html()); 561 $('.content_partial').remove(); 562 // The html() transfer above lost dynamic events. Reset. 563 fixActionLinks($('.content_initial')); 564 m_browserHistory.refreshPageTitle(false); 565 566 load('show'); 567 // These two lines are from dw_page.init() 568 dw_page.sectionHighlight(); 569 jQuery('a.fn_top').mouseover(dw_page.footnoteDisplay); 570 } 571 }, 572 show: function(params, extraData) { 573 $('.content_initial').html($('.content_partial').html()); 574 $('.content_partial').remove(); 575 // The html() transfer above lost dynamic events. Reset. 576 fixActionLinks($('.content_initial')); 577 // These two lines are from dw_page.init() 578 dw_page.sectionHighlight(); 579 jQuery('a.fn_top').mouseover(dw_page.footnoteDisplay); 580 } 581 }; 582 m_actionEffects.draft = m_actionEffects.edit; 583 m_actionEffects.diff = m_actionEffects.revisions; 584 585 586 /** 587 * Perform a standard partial AJAX action (edit, history, etc). 588 * 589 * @param {DOMNode=} insertLoc - Optional 590 * @private 591 */ 592 function _action(action, params, callback, insertLoc, extraData) { 593 params['do'] = action; 594 595 function cb(data) { 596 $('.content_partial, .message_partial').remove(); 597 $('.content_initial').attr('id', m_initialId); 598 var tagname = $('.content_initial')[0].tagName.toLowerCase(); 599 var body = $('<' + tagname + ' class="content_partial"></' + tagname + '>').append(data); 600 601 //TODO: I don't like having to put special case code here. Is there any better place to put it? m_actionEffects is too late. 602 if (insertLoc && action=='edit') { 603 var newform = $('#dw__editform', body); 604 if (newform.find('input[name=prefix]').val() == '.' && newform.find('input[name=suffix]').val() == '') { 605 // There was an error and the whole page is being edited, or there was only one section on the page. 606 delete m_pageObjs.sectionForm; 607 if (extraData) 608 delete extraData.sectionForm; 609 insertLoc = null; 610 } 611 } 612 613 if (insertLoc) 614 $(insertLoc[insertLoc.length - 1]).after(body); 615 // This kind of partial replaces the whole content area. 616 else { 617 // Swap ids and classes, so the new element is styled correctly. 618 var initial = $('.content_initial'); 619 body.addClass(initial[0].className.replace(/content_initial/, '')).attr('id', m_initialId); 620 initial.attr('id', '').after(body); 621 } 622 623 // fwJustincase is to catch the case where the callbacks modify the DOM enough that this element not longer exists. 624 var newToc = $('.content_partial #dw__toc').addClass('fwJustincase'); 625 var hasNewToc = newToc.length > 0; 626 627 _updatePageObjsOnSwitch(); 628 629 if (callback) 630 callback(data, extraData); 631 if (m_actionEffects[action]) 632 m_actionEffects[action](params, extraData||{}); 633 // Update TOC. The default behavior is just to leave it in place, where it comes in. 634 newToc = $('.fwJustincase').removeClass('fwJustincase'); 635 if (m_viewMode == 'show') 636 $(window).trigger('fastwiki:updateToc', [newToc]); 637 638 // Initialize TOC. Must happen after m_actionEffects, which can overwrite the HTML and lose events. 639 if (hasNewToc) 640 dw_page.makeToggle('#dw__toc h3','#dw__toc > div'); 641 642 // Update links in the content area. 643 fixActionLinks($('.content_partial')); 644 645 // It's important to use m_viewMode here instead of action, because the callbacks can change the action. 646 _setBodyClass(m_viewMode, m_pageObjs.sectionForm ? "section" : null); 647 648 // Cache the page. 649 if (m_viewMode == 'show') 650 m_cache.add(JSINFO.id, data, true); 651 652 // Update doku state. 653 if (!insertLoc) 654 dw_behaviour.init(); 655 656 $(window).trigger('fastwiki:afterSwitch', [m_pageObjs.sectionForm?'show':m_viewMode, !!m_pageObjs.sectionForm, m_prevView]); 657 658 if (!m_isSecedit && !m_wasSecedit) { 659 setTimeout(function() { 660 $('html,body').animate({scrollTop: 0}, 300); 661 }, 1); 662 } 663 } 664 665 //TODO: On save, refresh cache. 666 // If the page is cached, load it from cache. 667 if (action == 'show' && m_cache.get(JSINFO.id)) { 668 m_debug && console.log("Getting from cache: " + JSINFO.id); 669 cb(m_cache.get(JSINFO.id)); 670 } 671 else 672 _sendPartial(params, _getVisibleContent(), cb, 'text'); 673 } 674 675 676 /** 677 * Send a "partial" action, used for AJAX editing, previews, subscribe etc. 678 * 679 * @param {Object} params - Parameters to send to doku.php. 680 * @param {DOMNode} spinnerParent - Center the loading spinner in this object. 681 * @param {Function} callback - Call this function, with the content HTML as a parameter, when the action is complete. 682 * @private 683 */ 684 function _sendPartial(params, spinnerParent, callback) { 685 if ($('.partialsLoading').length == 0) { 686 var spinnerCss = spinnerParent.height() + spinnerParent.offset().top > $(window).height() ? {top: $(window).height() / 2} : {top: '50%'}; 687 spinnerParent.append($('<div class="partialsLoading"></div>').css('display', 'none').css(spinnerCss)); 688 // Give it some time in case the server is really responsive. 689 setTimeout(function() {$('.partialsLoading').css('display', '');}, 500); 690 } 691 692 params.partial = 1; 693 jQuery[!params['do'] || params['do']=='show' ? 'get' : 'post'](m_browserHistory.getBaseUrl(), params, function(data) { 694 // Special error conditions 695 if (data == 'PERMISSION_CHANGE') { 696 delete params.partial; 697 delete params.fastwiki_compareid; 698 var url = m_browserHistory.getBaseUrl() + '?' + $.param(params); 699 document.location.href = url; 700 } 701 else 702 callback(data); 703 704 // Remove all loading spinners, in case a bug let some extras slip in. 705 $('.partialsLoading').remove(); 706 }, 'text'); 707 } 708 709 710 /** 711 * Return the currently visible content area. 712 */ 713 function _getVisibleContent() { 714 var parentElt = $('.content_partial'); 715 if (parentElt.length == 0) 716 parentElt = $('.content_initial'); 717 return parentElt; 718 } 719 720 721 /** 722 * Load a new view, using AJAX to avoid page re-load. 723 * 724 * @param {String} page - The view to load. This can be 'show,' or the value of a do= action param. 725 * @param {Form=} sectionForm - Only valid when page=='edit' or page=='draft'. Used to edit a section inline. 726 * @param {Object=} params - Additional parameters to pass to the AJAX request. For example, 'rev' if a revision is being edited. 727 * @param {boolean=} force - Force an AJAX load, even if the code thinks it can optimize it out. 728 * @param {Function=} callback - Called after the new page is loaded. 729 */ 730 function load(page, sectionForm, params, force, callback) { 731 //TODO: What if load() is called while another load() is active? 732 // If edit text has changed, confirm before switching views. 733 if ((m_viewMode == 'edit' || m_viewMode == 'draft') && (page != 'save' && page != 'preview') && m_pageObjs.content != $('#wiki__text').val()) { 734 if (!confirm(LANG.notsavedyet)) 735 return; 736 } 737 738 m_prevView = m_viewMode; 739 // Edit: was=false, is=true 740 // Save: was=true, is=true 741 // Show: was=true, is=false 742 m_wasSecedit = m_isSecedit; 743 m_isSecedit = !!sectionForm || (m_wasSecedit && page=='save'); 744 m_viewMode = page; 745 if (!params) 746 params = {}; 747 window.onbeforeunload = ''; 748 dw_locktimer.clear(); 749 750 // First switch back to the original mode, canceling other modes. 751 _updatePageObjsOnSwitch(); 752 753 // If we're back to the original mode, just clean up and quit. 754 if (page == m_origViewMode && !force) { 755 $('.content_partial, .message_partial').remove(); 756 $('.content_initial').attr('id', m_initialId); 757 758 if (m_prevView != page) { 759 // Scroll to top. 760 if (!m_isSecedit && !m_wasSecedit) { 761 setTimeout(function() { 762 $('html,body').animate({scrollTop: 0}, 300); 763 }, 1); 764 } 765 766 _setBodyClass(page); 767 $(window).trigger('fastwiki:afterSwitch', [m_pageObjs.sectionForm?'show':m_viewMode, !!m_pageObjs.sectionForm]); 768 } 769 if (callback) 770 callback(); 771 } 772 // Sectionedit is special. Other special handlers are in m_actionEffects. 773 else if ((page == 'draft' || page == 'edit') && sectionForm) { 774 var sectionParts = _getSection(sectionForm); 775 _action(page, params, callback, sectionParts, {sectionForm: sectionForm, sectionParts:sectionParts}); 776 } 777 // Default action 778 else 779 _action(page, params, callback); 780 } 781 782 783 return { 784 load: load, 785 fixActionLinks: fixActionLinks 786 }; 787 788 /* DOKUWIKI:include pagecache.js */ 789 /* DOKUWIKI:include history.js */ 790})(jQuery); 791 792/* DOKUWIKI:include templates.js */ 793/* DOKUWIKI:include plugins.js */ 794