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');
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