(function(F){
"use strict";
const E = (s)=>document.querySelector(s),
D = F.dom,
P = F.page;
P.config = {
defaultMaxStashSize: 10,
useConfirmerButtons:{
save: false,
reload: true,
discardStash: true
}
};
const $stash = {
keys: {
index: F.page.name+'.index'
},
indexKey: function(winfo){return winfo.name},
contentKey: function(suffix){return P.name+'/'+suffix},
getIndex: function(){
if(!this.index){
this.index = F.storage.getJSON(
this.keys.index, {}
);
}
return this.index;
},
_fireStashEvent: function(){
if(this._disableNextEvent) delete this._disableNextEvent;
else F.page.dispatchEvent('wiki-stash-updated', this);
},
getWinfo: function(winfo){
const ndx = this.getIndex();
return ndx[this.indexKey(winfo)];
},
storeIndex: function(){
if(this.index) F.storage.setJSON(this.keys.index,this.index);
return this;
},
updateWinfo: function(winfo,content){
const ndx = this.getIndex(),
key = this.indexKey(winfo),
old = ndx[key];
const record = old || (ndx[key]={
name: winfo.name
});
record.mimetype = winfo.mimetype;
record.type = winfo.type;
record.parent = winfo.parent;
record.version = winfo.version;
record.stashTime = new Date().getTime();
record.isEmpty = !!winfo.isEmpty;
record.attachments = winfo.attachments;
this.storeIndex();
if(arguments.length>1){
if(content) delete record.isEmpty;
F.storage.set(this.contentKey(key), content);
}
this._fireStashEvent();
return this;
},
stashedContent: function(winfo){
return F.storage.get(this.contentKey(this.indexKey(winfo)));
},
hasStashedContent: function(winfo){
if('string'===typeof winfo) winfo = {name: winfo};
return F.storage.contains(this.contentKey(this.indexKey(winfo)));
},
unstash: function(winfo){
const ndx = this.getIndex(),
key = this.indexKey(winfo);
delete winfo.stashTime;
delete ndx[key];
F.storage.remove(this.contentKey(key));
this.storeIndex();
this._fireStashEvent();
return this;
},
clear: function(){
const ndx = this.getIndex(),
self = this;
let count = 0;
Object.keys(ndx).forEach(function(k){
++count;
const e = ndx[k];
delete ndx[k];
F.storage.remove(self.contentKey(k));
});
F.storage.remove(this.keys.index);
delete this.index;
if(count) this._fireStashEvent();
return this;
},
prune: function f(maxCount){
const ndx = this.getIndex();
const li = [];
if(!maxCount || maxCount<0) maxCount = f.defaultMaxCount;
Object.keys(ndx).forEach((k)=>li.push(ndx[k]));
li.sort((l,r)=>l.stashTime - r.stashTime);
let n = 0;
while(li.length>maxCount){
++n;
const e = li.shift();
this._disableNextEvent = true;
this.unstash(e);
console.warn("Pruned oldest local file edit entry:",e);
}
if(n) this._fireStashEvent();
}
};
$stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
P.$stash = $stash;
P.selectMimetype = function(modeValue, forceEvent){
const s = this.e.selectMimetype;
if(!modeValue) modeValue = s.value;
else if(s.value != modeValue){
s.value = modeValue;
forceEvent = true;
}
if(forceEvent){
s.dispatchEvent(new Event('change',{target:s}));
}
};
const getEditMarker = function f(winfo, textOnly){
const esm = F.config.editStateMarkers;
if(f.NEW===winfo){
return textOnly ? esm.isNew :
D.addClass(D.append(D.span(),esm.isNew), 'is-new');
}else if(f.MODIFIED===winfo){
return textOnly ? esm.isModified :
D.addClass(D.append(D.span(),esm.isModified), 'is-modified');
}else if(f.DELETED===winfo){
return textOnly ? esm.isDeleted :
D.addClass(D.append(D.span(),esm.isDeleted), 'is-deleted');
}else if(winfo && winfo.version){
if($stash.getWinfo(winfo)){
return textOnly ? esm.isModified :
D.addClass(D.append(D.span(),esm.isModified), 'is-modified');
}
}
else if(winfo){
if('sandbox'!==winfo.type){
return textOnly ? esm.isNew :
D.addClass(D.append(D.span(),esm.isNew), 'is-new');
}else if($stash.getWinfo(winfo)){
return textOnly ? esm.isModified :
D.addClass(D.append(D.span(),esm.isModified), 'is-modified');
}
}
return textOnly ? '' : D.span();
};
getEditMarker.NEW = 1;
getEditMarker.MODIFIED = 2;
getEditMarker.DELETED = 3;
const winfoIsNew = function(winfo){
if(!winfo) return undefined;
else if('sandbox' === winfo.type) return false;
else return !winfo.version;
};
const WikiList = {
e: {
filterCheckboxes: {
},
},
cache: {
pageList: [],
optByName:{},
names: {
}
},
_refreshStashMarks: function callee(option){
if(!callee.eachOpt){
const self = this;
callee.eachOpt = function(keyOrOpt){
const opt = 'string'===typeof keyOrOpt ? self.e.select.options[keyOrOpt] : keyOrOpt;
const stashed = $stash.getWinfo({name:opt.value});
var prefix = '';
D.removeClass(opt, 'stashed', 'stashed-new', 'deleted');
if(stashed){
const isNew = winfoIsNew(stashed);
prefix = getEditMarker(isNew ? getEditMarker.NEW : getEditMarker.MODIFIED, true);
D.addClass(opt, isNew ? 'stashed-new' : 'stashed');
D.removeClass(opt, 'deleted');
}else if(opt.dataset.isDeleted){
prefix = getEditMarker(getEditMarker.DELETED,true);
D.addClass(opt, 'deleted');
}
opt.innerText = prefix + opt.value;
self.cache.names[opt.value] = true;
};
}
if(arguments.length){
callee.eachOpt(option);
}else{
this.cache.names = {};
Object.keys(this.e.select.options).forEach(callee.eachOpt);
}
},
removeEntry: function(name){
const sel = this.e.select;
var ndx = sel.selectedIndex;
sel.value = name;
if(sel.selectedIndex>-1){
if(ndx === sel.selectedIndex) ndx = -1;
sel.options.remove(sel.selectedIndex);
}
sel.selectedIndex = ndx;
delete this.cache.names[name];
delete this.cache.optByName[name];
this.cache.pageList = this.cache.pageList.filter((wi)=>name !== wi.name);
},
_rebuildList: function callee(){
const list = this.cache.pageList;
if(!list) return;
if(!callee.sorticase){
callee.sorticase = function(l,r){
if(l===r) return 0;
l = l.toLowerCase();
r = r.toLowerCase();
return l<=r ? -1 : 1;
};
}
const map = {}, ndx = $stash.getIndex(), sel = this.e.select;
D.clearElement(sel);
list.forEach((winfo)=>map[winfo.name] = winfo);
Object.keys(ndx).forEach(function(key){
const winfo = ndx[key];
if(!winfo.version) map[winfo.name] = winfo;
});
const self = this;
Object.keys(map)
.sort(callee.sorticase)
.forEach(function(name){
const winfo = map[name];
const opt = D.option(sel, winfo.name);
const wtype = opt.dataset.wtype =
winfo.type==='sandbox' ? 'normal' : (winfo.type||'normal');
const cb = self.e.filterCheckboxes[wtype];
self.cache.optByName[winfo.name] = opt;
if(cb && !cb.checked) D.addClass(opt, 'hidden');
if(winfo.isEmpty){
opt.dataset.isDeleted = true;
}
self._refreshStashMarks(opt);
});
D.enable(sel);
if(P.winfo) sel.value = P.winfo.name;
},
loadList: function callee(){
if(!callee.onload){
const self = this;
callee.onload = function(list){
self.cache.pageList = list;
self._rebuildList();
F.message("Loaded page list.");
};
}
if(P.initialPageList){
const list = P.initialPageList;
delete P.initialPageList;
callee.onload(list);
}else{
F.fetch('wikiajax/list',{
urlParams:{verbose:true},
responseType: 'json',
onload: callee.onload
});
}
return this;
},
validatePageName: function(name){
var err;
if(!name){
err = "may not be empty";
}else if(this.cache.names.hasOwnProperty(name)){
err = "page already exists: "+name;
}else if(name.length>100){
err = "too long (limit is 100)";
}else if(/\s{2,}/.test(name)){
err = "multiple consecutive spaces";
}else if(/[\t\r\n]/.test(name)){
err = "contains control character(s)";
}else{
let i = 0, n = name.length, c;
for( ; i < n; ++i ){
if(name.charCodeAt(i)<0x20){
err = "contains control character(s)";
break;
}
}
}
if(err){
F.error("Invalid name:",err);
}
return !err;
},
addNewPage: function(name){
name = name.trim();
if(!this.validatePageName(name)) return false;
var wtype = 'normal';
if(0===name.indexOf('checkin/')) wtype = 'checkin';
else if(0===name.indexOf('branch/')) wtype = 'branch';
else if(0===name.indexOf('tag/')) wtype = 'tag';
const winfo = {
name: name, type: wtype, mimetype: 'text/x-markdown',
version: null, parent: null
};
this.cache.pageList.push(
winfo
);
$stash.updateWinfo(winfo, '');
this._rebuildList();
P.loadPage(winfo.name);
return true;
},
init: function(parentElem){
const sel = D.select(), btn = D.addClass(D.button("Reload page list"), 'save');
this.e.select = sel;
D.addClass(parentElem, 'WikiList');
D.clearElement(parentElem);
D.append(
parentElem,
D.append(D.fieldset("Select a page to edit"),
sel)
);
D.attr(sel, 'size', 12);
D.option(D.disable(D.clearElement(sel)), undefined, "Loading...");
const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
fsFilterBody = D.div(),
filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
;
D.append(fsFilter, fsFilterBody);
D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
const self = this;
const filterByType = function(wtype, show){
sel.querySelectorAll('option[data-wtype='+wtype+']').forEach(function(opt){
if(show) opt.classList.remove('hidden');
else opt.classList.add('hidden');
});
};
filters.forEach(function(label){
const wtype = label.split('/')[0];
const cbId = 'wtype-filter-'+wtype,
lbl = D.attr(D.append(D.label(),label),
'for', cbId),
cb = D.attr(D.input('checkbox'), 'id', cbId);
D.append(fsFilterBody, D.append(D.span(), cb, lbl));
self.e.filterCheckboxes[wtype] = cb;
cb.checked = true;
filterByType(wtype, cb.checked);
cb.addEventListener(
'change',
function(ev){filterByType(wtype, ev.target.checked)},
false
);
});
{
const cbId = 'wtype-filter-deleted',
lbl = D.attr(D.append(D.label(),
getEditMarker(getEditMarker.DELETED,false),
'deleted'),
'for', cbId),
cb = D.attr(D.input('checkbox'), 'id', cbId);
cb.checked = false;
D.addClass(parentElem,'hide-deleted');
D.attr(lbl);
const deletedTip = F.helpButtonlets.create(
D.span(),
'Fossil considers empty pages to be "deleted" in some contexts.'
);
D.append(fsFilterBody, D.append(
D.span(), cb, lbl, deletedTip
));
cb.addEventListener(
'change',
function(ev){
if(ev.target.checked) D.removeClass(parentElem,'hide-deleted');
else D.addClass(parentElem,'hide-deleted');
},
false);
}
const fsLegend = D.fieldset("Edit status"),
fsLegendBody = D.div();
D.append(fsLegend, fsLegendBody);
D.addClass(fsLegendBody, 'flex-container', 'flex-column', 'stretch');
D.append(
fsLegendBody,
D.append(D.span(), getEditMarker(getEditMarker.NEW,false)," = new/unsaved"),
D.append(D.span(), getEditMarker(getEditMarker.MODIFIED,false)," = has local edits"),
D.append(D.span(), getEditMarker(getEditMarker.DELETED,false)," = is empty (deleted)")
);
const fsNewPage = D.fieldset("Create new page"),
fsNewPageBody = D.div(),
newPageName = D.input('text'),
newPageBtn = D.button("Add page locally")
;
D.append(parentElem, fsNewPage);
D.append(fsNewPage, fsNewPageBody);
D.addClass(fsNewPageBody, 'flex-container', 'flex-column', 'new-page');
D.append(
fsNewPageBody, newPageName, newPageBtn,
D.append(D.addClass(D.span(), 'mini-tip'),
"New pages exist only in this browser until they are saved.")
);
newPageBtn.addEventListener('click', function(){
if(self.addNewPage(newPageName.value)){
newPageName.value = '';
}
}, false);
D.append(
parentElem,
D.append(D.addClass(D.div(), 'fieldset-wrapper'),
fsFilter, fsNewPage, fsLegend)
);
D.append(parentElem, btn);
btn.addEventListener('click', ()=>this.loadList(), false);
this.loadList();
const onSelect = (e)=>P.loadPage(e.target.value);
sel.addEventListener('change', onSelect, false);
sel.addEventListener('dblclick', onSelect, false);
F.page.addEventListener('wiki-stash-updated', ()=>{
if(P.winfo) this._refreshStashMarks();
else this._rebuildList();
});
F.page.addEventListener('wiki-page-loaded', function(ev){
const page = ev.detail,
opt = self.cache.optByName[page.name];
if(opt){
if(page.isEmpty) opt.dataset.isDeleted = true;
else delete opt.dataset.isDeleted;
self._refreshStashMarks(opt);
}else if('sandbox'!==page.type){
F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name);
}
});
delete this.init;
}
};
P.stashWidget = {
e:{},
init: function(domInsertPoint){
const wrapper = D.addClass(
D.attr(D.div(),'id','wikiedit-stash-selector'),
'input-with-label'
);
const sel = this.e.select = D.select(),
btnClear = this.e.btnClear = D.button("Discard Edits"),
btnHelp = D.append(
D.addClass(D.div(), "help-buttonlet"),
'Locally-edited wiki pages. Timestamps are the last local edit time. ',
'Only the ',P.config.defaultMaxStashSize,' most recent pages ',
'are retained. Saving or reloading a file removes it from this list. ',
D.append(D.code(),F.storage.storageImplName()),
' = ',F.storage.storageHelpDescription()
);
D.append(wrapper, "Local edits (",
D.append(D.code(),
F.storage.storageImplName()),
"):",
btnHelp, sel, btnClear);
F.helpButtonlets.setup(btnHelp);
D.option(D.disable(sel), undefined, "(empty)");
P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail));
P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail));
sel.addEventListener('change',function(e){
const opt = this.selectedOptions[0];
if(opt && opt._winfo) P.loadPage(opt._winfo);
});
if(F.storage.isTransient()){
D.append(wrapper, D.append(
D.addClass(D.span(),'warning'),
"Warning: persistent storage is not available, "+
"so uncomitted edits will not survive a page reload."
));
}
domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint);
if(P.config.useConfirmerButtons.discardStash){
F.confirmer(btnClear, {
pinSize: true,
confirmText: "DISCARD all local edits?",
onconfirm: ()=>P.clearStash(),
ticks: F.config.confirmerButtonTicks
});
}else{
btnClear.addEventListener('click', ()=>P.clearStash(), false);
}
D.addClass(btnClear,'hidden');
$stash._fireStashEvent();
delete this.init;
},
updateList: function f(stasher,theWinfo){
if(!f.compare){
const cmpBase = (l,r)=>l<r ? -1 : (l===r ? 0 : 1);
f.compare = (l,r)=>cmpBase(l.name.toLowerCase(), r.name.toLowerCase());
f.rxZ = /\.\d+Z$/;
const pad=(x)=>(''+x).length>1 ? x : '0'+x;
f.timestring = function(d){
return [
d.getFullYear(),'-',pad(d.getMonth()+1),'-',pad(d.getDate()),
'@',pad(d.getHours()),':',pad(d.getMinutes())
].join('');
};
}
const index = stasher.getIndex(), ilist = [];
Object.keys(index).forEach((winfo)=>{
ilist.push(index[winfo]);
});
const self = this;
D.clearElement(this.e.select);
if(0===ilist.length){
D.addClass(this.e.btnClear, 'hidden');
D.option(D.disable(this.e.select),undefined,"No local edits");
return;
}
D.enable(this.e.select);
if(true){
D.removeClass(this.e.btnClear, 'hidden');
}
D.disable(D.option(this.e.select,undefined,"Select a local edit..."));
const currentWinfo = theWinfo || P.winfo || {name:''};
ilist.sort(f.compare).forEach(function(winfo,n){
const key = stasher.indexKey(winfo),
rev = winfo.version || '';
const opt = D.option(
self.e.select, n+1,
[winfo.name,
' [',
rev ? F.hashDigits(rev) : (
winfo.type==='sandbox' ? 'sandbox' : 'new/local'
),'] ',
f.timestring(new Date(winfo.stashTime))
].join('')
);
opt._winfo = winfo;
if(0===f.compare(currentWinfo, winfo)){
D.attr(opt, 'selected', true);
}
});
}
};
const ajaxState = {
count: 0,
toDisable: undefined
};
F.fetch.beforesend = function f(){
if(!ajaxState.toDisable){
ajaxState.toDisable = document.querySelectorAll(
['button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'fieldset:not([disabled])'
].join(',')
);
}
if(1===++ajaxState.count){
D.addClass(document.body, 'waiting');
D.disable(ajaxState.toDisable);
}
};
F.fetch.aftersend = function(){
if(0===--ajaxState.count){
D.removeClass(document.body, 'waiting');
D.enable(ajaxState.toDisable);
delete ajaxState.toDisable;
}
};
F.onPageLoad(function() {
document.body.classList.add('wikiedit');
P.base = {tag: E('base'), wikiUrl: F.repoUrl('wiki')};
P.base.originalHref = P.base.tag.href;
P.e = {
taEditor: E('#wikiedit-content-editor'),
btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"),
btnSave: E("button.wikiedit-save"),
btnSaveClose: E("button.wikiedit-save-close"),
selectMimetype: E('select[name=mimetype]'),
selectFontSizeWrap: E('#select-font-size'),
cbAutoPreview: E('#cb-preview-autorefresh'),
previewTarget: E('#wikiedit-tab-preview-wrapper'),
diffTarget: E('#wikiedit-tab-diff-wrapper'),
editStatus: E('#wikiedit-edit-status'),
tabContainer: E('#wikiedit-tabs'),
attachmentContainer: E("#attachment-wrapper"),
tabs:{
pageList: E('#wikiedit-tab-pages'),
content: E('#wikiedit-tab-content'),
preview: E('#wikiedit-tab-preview'),
diff: E('#wikiedit-tab-diff'),
misc: E('#wikiedit-tab-misc')
}
};
P.tabs = new F.TabManager(D.clearElement(P.e.tabContainer));
P.tabs.addCustomWidget( E('#fossil-status-bar') ).addCustomWidget(P.e.editStatus);
let currentTab;
P.tabs.addEventListener(
'before-switch-to', function(ev){
const theTab = currentTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot');
if(btnSlot){
btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot );
btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot );
P.updateSaveButton();
}
if(theTab===P.e.tabs.preview){
P.baseHrefForWiki();
if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview();
}else if(theTab===P.e.tabs.diff){
D.removeClass(P.e.diffTarget, 'hidden');
}
}
);
P.tabs.addEventListener(
'before-switch-from', function(ev){
const theTab = ev.detail;
if(theTab===P.e.tabs.preview){
P.baseHrefRestore();
}else if(theTab===P.e.tabs.diff){
D.addClass(P.e.diffTarget, 'hidden');
}
}
);
P.e.taEditor.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
P.e.taEditor.blur();
P.tabs.switchToTab(P.e.tabs.preview);
if(!P.e.cbAutoPreview.checked){
P.preview();
}
}
}, false);
document.body.addEventListener('keydown',function(ev){
if(ev.shiftKey && 13 === ev.keyCode){
if(currentTab === P.e.tabs.preview){
ev.preventDefault();
ev.stopPropagation();
P.tabs.switchToTab(P.e.tabs.content);
P.e.taEditor.focus();
return false;
}
}
}, true);
F.connectPagePreviewers(
P.e.tabs.preview.querySelector(
'#btn-preview-refresh'
)
);
const diffButtons = E('#wikiedit-tab-diff-buttons');
diffButtons.querySelector('button.sbs').addEventListener(
"click",(e)=>P.diff(true), false
);
diffButtons.querySelector('button.unified').addEventListener(
"click",(e)=>P.diff(false), false
);
if(0) P.e.btnCommit.addEventListener(
"click",(e)=>P.commit(), false
);
const doSave = function(alsoClose){
const w = P.winfo;
if(!w){
F.error("No page loaded.");
return;
}
if(alsoClose){
P.save(()=>window.location.href=F.repoUrl('wiki',{name: w.name}));
}else{
P.save();
}
};
const doReload = function(e){
const w = P.winfo;
if(!w){
F.error("No page loaded.");
return;
}
if(!w.version
&& w.type!=='sandbox'
&& P.wikiContent()){
F.error("This new/unsaved page has content.",
"To really discard this page,",
"first clear its content",
"then use the Discard button.");
return;
}
P.unstashContent();
if(w.version || w.type==='sandbox'){
P.loadPage(w);
}else{
WikiList.removeEntry(w.name);
delete P.winfo;
P.updatePageTitle();
F.message("Discarded new page ["+w.name+"].");
}
};
if(P.config.useConfirmerButtons.reload){
P.tabs.switchToTab(1);
F.confirmer(P.e.btnReload, {
pinSize: true,
confirmText: "Really reload, losing edits?",
onconfirm: doReload,
ticks: F.config.confirmerButtonTicks
});
}else{
P.e.btnReload.addEventListener('click', doReload, false);
}
if(P.config.useConfirmerButtons.save){
P.tabs.switchToTab(1);
F.confirmer(P.e.btnSave, {
pinSize: true,
confirmText: "Really save changes?",
onconfirm: ()=>doSave(),
ticks: F.config.confirmerButtonTicks
});
F.confirmer(P.e.btnSaveClose, {
pinSize: true,
confirmText: "Really save changes?",
onconfirm: ()=>doSave(true),
ticks: F.config.confirmerButtonTicks
});
}else{
P.e.btnSave.addEventListener('click', ()=>doSave(), false);
P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
}
P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
P.selectMimetype(false, true);
P.e.selectMimetype.addEventListener(
'change',
function(e){
if(P.winfo && P.winfo.mimetype !== e.target.value){
P.winfo.mimetype = e.target.value;
P._isDirty = true;
P.stashContentChange(true);
}
},
false
);
const selectFontSize = E('select[name=editor_font_size]');
if(selectFontSize){
selectFontSize.addEventListener(
"change",function(e){
const ed = P.e.taEditor;
ed.className = ed.className.replace(
/\bfont-size-\d+/g, '' );
ed.classList.add('font-size-'+e.target.value);
}, false
);
selectFontSize.dispatchEvent(
new Event('change',{target:selectFontSize})
);
}
P.addEventListener(
'wiki-content-replaced',
()=>{
P.previewNeedsUpdate = true;
D.clearElement(P.e.diffTarget, P.e.previewTarget);
}
);
P.addEventListener(
'wiki-saved',
(e)=>{
D.clearElement(P.e.diffTarget, P.e.previewTarget);
}
);
P.addEventListener('wiki-stash-updated',function(){
if(P.winfo && !P.winfo.version && !$stash.getWinfo(P.winfo)){
delete P.winfo;
P.wikiContent('');
P.updatePageTitle();
}
P.updateSaveButton();
}).updatePageTitle().updateSaveButton();
P.addEventListener(
'wiki-page-loaded',
function(ev){
delete P._isDirty;
const winfo = ev.detail;
P.winfo = winfo;
P.previewNeedsUpdate = true;
P.e.selectMimetype.value = winfo.mimetype;
P.tabs.switchToTab(P.e.tabs.content);
P.wikiContent(winfo.content || '');
WikiList.e.select.value = winfo.name;
if(!winfo.version && winfo.type!=='sandbox'){
F.message('You are editing a new, unsaved page:',winfo.name);
}
P.updatePageTitle().updateSaveButton();
},
false
);
P.tabs.switchToTab(0);
WikiList.init( P.e.tabs.pageList.firstElementChild );
P.tabs.switchToTab(1);
P.stashWidget.init(P.e.tabs.content.lastElementChild);
P.tabs.switchToTab(0);
});
const affirmPageLoaded = function(quiet){
if(!P.winfo && !quiet) F.error("No wiki page is loaded.");
return !!P.winfo;
};
P.updateAttachmentsView = function f(){
if(!f.eAttach){
f.eAttach = P.e.attachmentContainer.querySelector('div');
}
D.clearElement(f.eAttach);
const wi = this.winfo;
if(!wi){
D.append(f.eAttach,"No page loaded.");
return this;
}
else if(!wi.version){
D.append(f.eAttach,
"Page ["+wi.name+"] cannot have ",
"attachments until it is saved once.");
return this;
}
const btnReload = D.button("Reload list");
const self = this;
btnReload.addEventListener('click', function(){
const isStashed = $stash.hasStashedContent(wi);
F.fetch('wikiajax/attachments',{
responseType: 'json',
urlParams: {page: wi.name},
onload: function(r){
wi.attachments = r;
if(isStashed) self.stashContentChange(true);
F.message("Reloaded attachment list for ["+wi.name+"].");
self.updateAttachmentsView();
}
});
});
if(!wi.attachments || !wi.attachments.length){
D.append(f.eAttach,
btnReload,
" No attachments found for page ["+wi.name+"]. ",
D.a(F.repoUrl('attachadd',{
page: wi.name,
from: F.repoUrl('wikiedit',{name: wi.name})}),
"Add attachments..." )
);
return this;
}
D.append(
f.eAttach,
D.append(D.p(),
btnReload," ",
D.a(F.repoUrl('attachlist',{page:wi.name}),
"Attachments for page ["+wi.name+"]."),
" ",
D.a(F.repoUrl('attachadd',{
page:wi.name,
from: F.repoUrl('wikiedit',{name: wi.name})}),
"Add attachments..." )
)
);
wi.attachments.forEach(function(a){
const wrap = D.div();
D.append(f.eAttach, wrap);
D.append(wrap,
D.append(D.div(),
"Attachment ",
D.addClass(
D.a(F.repoUrl('ainfo',{name:a.uuid}),
F.hashDigits(a.uuid,true)),
'monospace'),
" ",
a.filename,
(a.isLatest ? " (latest)" : "")
)
);
const ul = D.ul();
D.append(wrap, ul);
[
[
"attachdownload?page=",
encodeURIComponent(wi.name),
"&file=",
encodeURIComponent(a.filename)
].join(''),
"raw/"+a.src
].forEach(function(url){
const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
const urlCopy = D.span();
const li = D.li(ul);
D.append(li, urlCopy, " ", imgUrl);
F.copyButton(urlCopy, {copyFromElement: imgUrl});
});
});
return this;
};
P.updateEditStatus = function f(){
if(!f.eLinks){
f.eName = P.e.editStatus.querySelector('span.name');
f.eLinks = P.e.editStatus.querySelector('span.links');
}
const wi = this.winfo;
D.clearElement(f.eName, f.eLinks);
if(!wi){
D.append(f.eName, '(no page loaded)');
this.updateAttachmentsView();
return this;
}
D.append(f.eName,getEditMarker(wi, false),wi.name);
this.updateAttachmentsView();
if(!wi.version) return this;
D.append(
f.eLinks,
D.a(F.repoUrl('wiki',{name:wi.name}),"viewer"),
D.a(F.repoUrl('whistory',{name:wi.name}),'history'),
D.a(F.repoUrl('attachlist',{page:wi.name}),"attachments"),
D.a(F.repoUrl('attachadd',{page:wi.name,from: F.repoUrl('wikiedit',{name: wi.name})}), "attach"),
D.a(F.repoUrl('wikiedit',{name:wi.name}),"editor permalink")
);
return this;
};
P.updatePageTitle = function f(){
if(!f.titleElement){
f.titleElement = document.head.querySelector('title');
}
const wi = P.winfo, marker = getEditMarker(wi, true),
title = wi ? wi.name : 'no page loaded';
f.titleElement.innerText = 'Wiki Editor: ' + marker + title;
this.updateEditStatus();
return this;
};
P.updateSaveButton = function(){
return this;
};
P.wikiContent = function f(){
if(0===arguments.length){
return f.get();
}else{
f.set(arguments[0] || '');
this.dispatchEvent('wiki-content-replaced', this);
return this;
}
};
P.wikiContent.get = function(){return P.e.taEditor.value};
P.wikiContent.set = function(content){P.e.taEditor.value = content};
P.setContentMethods = function(getter, setter){
this.wikiContent.get = getter;
this.wikiContent.set = setter;
return this;
};
P.notifyOfChange = function(){
P._isDirty = true;
P.stashContentChange();
};
P.replaceEditorElement = function(newEditor){
P.e.taEditor.parentNode.insertBefore(newEditor, P.e.taEditor);
P.e.taEditor.remove();
P.e.selectFontSizeWrap.remove();
delete this.replaceEditorElement;
return P;
};
P.baseHrefForWiki = function f(){
this.base.tag.href = this.base.wikiUrl;
return this;
};
P.baseHrefRestore = function(){
this.base.tag.href = this.base.originalHref;
};
P.loadPage = function(name){
if(0===arguments.length){
if(!affirmPageLoaded()) return this;
name = this.winfo.name;
}else if(1===arguments.length && 'string' !== typeof name){
const arg = arguments[0];
name = arg.name;
}
const onload = (r)=>{
this.dispatchEvent('wiki-page-loaded', r);
};
const stashWinfo = this.getStashedWinfo({name: name});
if(stashWinfo){
F.message("Fetched from the local-edit storage:", stashWinfo.name);
onload({
name: stashWinfo.name,
mimetype: stashWinfo.mimetype,
type: stashWinfo.type,
version: stashWinfo.version,
parent: stashWinfo.parent,
isEmpty: !!stashWinfo.isEmpty,
content: $stash.stashedContent(stashWinfo),
attachments: stashWinfo.attachments
});
this._isDirty = true;
return this;
}
F.message(
"Loading content..."
).fetch('wikiajax/fetch',{
urlParams: {
page: name
},
responseType: 'json',
onload:(r)=>{
F.message('Loaded page ['+r.name+'].');
onload(r);
}
});
return this;
};
P.preview = function f(switchToTab){
if(!affirmPageLoaded()) return this;
return this._postPreview(this.wikiContent(), function(c){
P._previewTo(c);
if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview);
});
};
P._previewTo = function(c){
const target = this.e.previewTarget;
D.clearElement(target);
if('string'===typeof c) D.parseHtml(target,c);
if(F.pikchr){
F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
}
};
P._postPreview = function(content,callback){
if(!affirmPageLoaded()) return this;
if(!content){
callback(content);
return this;
}
const fd = new FormData();
const mimetype = this.e.selectMimetype.value;
fd.append('page', this.winfo.name);
fd.append('mimetype',mimetype);
fd.append('content',content || '');
F.message(
"Fetching preview..."
).fetch('wikiajax/preview',{
payload: fd,
onload: (r,header)=>{
callback(r);
F.message('Updated preview.');
P.previewNeedsUpdate = false;
P.dispatchEvent('wiki-preview-updated',{
mimetype: mimetype,
element: P.e.previewTarget
});
},
onerror: (e)=>{
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
P.diff = function f(sbs){
if(!affirmPageLoaded()) return this;
const content = this.wikiContent(),
self = this,
target = this.e.diffTarget;
const fd = new FormData();
fd.append('page',this.winfo.name);
fd.append('sbs', sbs ? 1 : 0);
fd.append('content',content);
if(this.e.selectDiffWS) fd.append('ws',this.e.selectDiffWS.value);
F.message(
"Fetching diff..."
).fetch('wikiajax/diff',{
payload: fd,
onload: function(c){
D.parseHtml(D.clearElement(target), [
"<div>Diff <code>[",
self.winfo.name,
"]</code> &rarr; Local Edits</div>",
c||'No changes.'
].join(''));
F.diff.setupDiffContextLoad();
if(sbs) P.tweakSbsDiffs();
F.message('Updated diff.');
self.tabs.switchToTab(self.e.tabs.diff);
}
});
return this;
};
P.save = function callee(onSuccessCallback){
if(!affirmPageLoaded()) return this;
else if(!this._isDirty){
F.error("There are no changes to save.");
return this;
}
const content = this.wikiContent();
const self = this;
callee.onload = function(w){
const oldWinfo = self.winfo;
self.unstashContent(oldWinfo);
self.dispatchEvent('wiki-page-loaded', w);
F.message("Saved page: ["+w.name+"].");
if('function'===typeof onSuccessCallback){
onSuccessCallback();
}
};
const fd = new FormData(), w = P.winfo;
fd.append('page',w.name);
fd.append('mimetype', w.mimetype);
fd.append('isnew', w.version ? 0 : 1);
fd.append('content', P.wikiContent());
F.message(
"Saving page..."
).fetch('wikiajax/save',{
payload: fd,
responseType: 'json',
onload: callee.onload
});
return this;
};
P.stashContentChange = function(onlyWinfo){
if(affirmPageLoaded(true)){
const wi = this.winfo;
wi.mimetype = P.e.selectMimetype.value;
if(onlyWinfo && $stash.hasStashedContent(wi)){
$stash.updateWinfo(wi);
}else{
$stash.updateWinfo(wi, P.wikiContent());
}
F.message("Stashed changes to page ["+wi.name+"].");
P.updatePageTitle();
$stash.prune();
this.previewNeedsUpdate = true;
}
return this;
};
P.unstashContent = function(){
const winfo = arguments[0] || this.winfo;
if(winfo){
this.previewNeedsUpdate = true;
$stash.unstash(winfo);
F.message("Unstashed page ["+winfo.name+"].");
}
return this;
};
P.clearStash = function(){
$stash.clear();
return this;
};
P.contentFromStash = function(){
return affirmPageLoaded(true) ? $stash.stashedContent(this.winfo) : undefined;
};
P.getStashedWinfo = function(winfo){
return $stash.getWinfo(winfo);
};
})(window.fossil);