(function(F){
"use strict";
const E = (s)=>document.querySelector(s),
D = F.dom,
P = F.page;
P.config = {
defaultMaxStashSize: 7
};
const $stash = {
keys: {
index: F.page.name+'.index'
},
indexKey: function(finfo){return finfo.checkin+':'+finfo.filename},
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('fileedit-stash-updated', this);
},
getFinfo: function(finfo){
const ndx = this.getIndex();
return ndx[this.indexKey(finfo)];
},
storeIndex: function(){
if(this.index) F.storage.setJSON(this.keys.index,this.index);
return this;
},
updateFile: function(finfo,content){
const ndx = this.getIndex(),
key = this.indexKey(finfo),
old = ndx[key];
const record = old || (ndx[key]={
checkin: finfo.checkin,
filename: finfo.filename,
mimetype: finfo.mimetype
});
record.isExe = !!finfo.isExe;
record.stashTime = new Date().getTime();
if(!record.branch) record.branch=finfo.branch;
this.storeIndex();
if(arguments.length>1){
F.storage.set(this.contentKey(key), content);
}
this._fireStashEvent();
return this;
},
stashedContent: function(finfo){
return F.storage.get(this.contentKey(this.indexKey(finfo)));
},
hasStashedContent: function(finfo){
return F.storage.contains(this.contentKey(this.indexKey(finfo)));
},
unstash: function(finfo){
const ndx = this.getIndex(),
key = this.indexKey(finfo);
delete finfo.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;
P.fileSelectWidget = {
e:{
container: E('#fileedit-file-selector')
},
finfo: {},
cache: {
checkins: undefined,
files:{},
branchKey: 'fileedit/uuid-branches',
branchNames: {}
},
loadLeaves: function(){
D.append(D.clearElement(
this.e.ciListLabel,
this.e.selectCi,
this.e.selectFiles
),"Loading leaves...");
D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi);
const self = this;
const onload = function(list){
D.append(D.clearElement(self.e.ciListLabel),
"Open leaves (newest first):");
self.cache.checkins = list;
D.clearElement(D.enable(self.e.selectCi));
let loadThisOne = P.initialFiles;
if(loadThisOne){
self.cache.files[loadThisOne.checkin] = loadThisOne;
delete P.initialFiles;
}
list.forEach(function(o,n){
if(!n && !loadThisOne) loadThisOne = o;
self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch;
D.option(self.e.selectCi, o.checkin,
o.timestamp+' ['+o.branch+']: '
+F.hashDigits(o.checkin));
});
F.storage.setJSON(self.cache.branchKey, self.cache.branchNames);
if(loadThisOne){
self.e.selectCi.value = loadThisOne.checkin;
}
self.loadFiles(loadThisOne ? loadThisOne.checkin : false);
};
if(P.initialLeaves){
const lv = P.initialLeaves;
delete P.initialLeaves;
onload(lv);
}else{
F.fetch('fileedit/filelist',{
urlParams:'leaves',
responseType: 'json',
onload: onload
});
}
},
loadFiles: function(ciUuid){
delete this.finfo.filename;
this.finfo.checkin = ciUuid;
const selFiles = this.e.selectFiles;
if(!ciUuid){
D.clearElement(D.disable(selFiles, this.e.btnLoadFile));
return this;
}
const onload = (response)=>{
D.clearElement(selFiles);
D.append(
D.clearElement(this.e.fileListLabel),
"Editable files for ",
D.append(
D.code(), "[",
D.a(F.repoUrl('timeline',{
c: ciUuid
}), F.hashDigits(ciUuid)),"]"
), ":"
);
this.cache.files[response.checkin] = response;
response.editableFiles.forEach(function(fn,n){
D.option(selFiles, fn);
});
if(selFiles.options.length){
D.enable(selFiles, this.e.btnLoadFile);
}
};
const got = this.cache.files[ciUuid];
if(got){
onload(got);
return this;
}
D.disable(selFiles,this.e.btnLoadFile);
D.clearElement(selFiles);
D.append(D.clearElement(this.e.fileListLabel),
"Loading files for "+F.hashDigits(ciUuid)+"...");
F.fetch('fileedit/filelist',{
urlParams:{checkin: ciUuid},
responseType: 'json',
onload
});
return this;
},
checkinBranchName: function(uuid){
return this.cache.branchNames[F.hashDigits(uuid,true)];
},
init: function(){
this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {});
const selCi = this.e.selectCi = D.addClass(D.select(), 'flex-grow'),
selFiles = this.e.selectFiles
= D.addClass(D.select(), 'file-list'),
btnLoad = this.e.btnLoadFile =
D.addClass(D.button("Load file"), "flex-shrink"),
filesLabel = this.e.fileListLabel =
D.addClass(D.div(),'flex-shrink','file-list-label'),
ciLabelWrapper = D.addClass(
D.div(), 'flex-container','flex-row', 'flex-shrink',
'stretch', 'child-gap-small'
),
btnReload = D.addClass(
D.button('Reload'), 'flex-shrink'
),
ciLabel = this.e.ciListLabel =
D.addClass(D.span(),'flex-shrink','checkin-list-label')
;
D.attr(selCi, 'title',"The list of opened leaves.");
D.attr(selFiles, 'title',
"The list of editable files for the selected checkin.");
D.attr(btnLoad, 'title',
"Load the selected file into the editor.");
D.disable(selCi, selFiles, btnLoad);
D.attr(selFiles, 'size', 12);
D.append(
this.e.container,
ciLabel,
D.append(ciLabelWrapper,
selCi,
btnReload),
filesLabel,
selFiles,
D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad)
);
if(F.config['fileedit-glob']){
D.append(
this.e.container,
D.append(
D.span(),
D.append(D.code(),"fileedit-glob"),
" config setting = ",
D.append(D.code(), JSON.stringify(F.config['fileedit-glob']))
)
);
}
this.loadLeaves();
selCi.addEventListener(
'change', (e)=>this.loadFiles(e.target.value), false
);
const doLoad = (e)=>{
this.finfo.filename = selFiles.value;
if(this.finfo.filename){
P.loadFile(this.finfo.filename, this.finfo.checkin);
}
};
btnLoad.addEventListener('click', doLoad, false);
selFiles.addEventListener('dblclick', doLoad, false);
btnReload.addEventListener(
'click', (e)=>this.loadLeaves(), false
);
delete this.init;
}
};
P.stashWidget = {
e:{},
init: function(domInsertPoint){
const wrapper = D.addClass(
D.attr(D.div(),'id','fileedit-stash-selector'),
'input-with-label'
);
const sel = this.e.select = D.select();
const btnClear = this.e.btnClear = D.button("Discard Edits"),
btnHelp = D.append(
D.addClass(D.div(), "help-buttonlet"),
'Locally-edited files. Timestamps are the last local edit time. ',
'Only the ',P.config.defaultMaxStashSize,' most recent files ',
'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)");
F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail));
F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail));
sel.addEventListener('change',function(e){
const opt = this.selectedOptions[0];
if(opt && opt._finfo) P.loadFile(opt._finfo);
});
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);
P.tabs.switchToTab(1);
F.confirmer(btnClear, {
pinSize: true,
confirmText: "DISCARD all local edits?",
onconfirm: function(e){
if(P.finfo){
const stashed = P.getStashedFinfo(P.finfo);
P.clearStash();
if(stashed) P.loadFile();
}else{
P.clearStash();
}
},
ticks: F.config.confirmerButtonTicks
});
D.addClass(this.e.btnClear,'hidden');
$stash._fireStashEvent();
P.tabs.switchToTab(0);
delete this.init;
},
updateList: function f(stasher,theFinfo){
if(!f.compare){
const cmpBase = (l,r)=>l<r ? -1 : (l===r ? 0 : 1);
f.compare = function(l,r){
const cmp = cmpBase(l.filename, r.filename);
return cmp ? cmp : cmpBase(l.checkin, r.checkin);
};
f.rxZ = /\.\d+Z$/;
const pad=(x)=>(''+x).length>1 ? x : '0'+x;
f.timestring = function ff(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((finfo)=>{
ilist.push(index[finfo]);
});
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);
D.removeClass(this.e.btnClear, 'hidden');
D.disable(D.option(this.e.select,0,"Select a local edit..."));
const currentFinfo = theFinfo || P.finfo || {filename:''};
ilist.sort(f.compare).forEach(function(finfo,n){
const key = stasher.indexKey(finfo),
branch = finfo.branch
|| P.fileSelectWidget.checkinBranchName(finfo.checkin)||'';
const opt = D.option(
self.e.select, n+1,
[F.hashDigits(finfo.checkin), ' [',branch||'?branch?','] ',
f.timestring(new Date(finfo.stashTime)),' ',
false ? finfo.filename : F.shortenFilename(finfo.filename)
].join('')
);
opt._finfo = finfo;
if(0===f.compare(currentFinfo, finfo)){
D.attr(opt, 'selected', true);
}
});
}
};
P.selectPreviewMode = function(modeValue, forceEvent){
const s = this.e.selectPreviewMode;
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 ajaxState = {
count: 0,
toDisable: undefined
};
F.fetch.beforesend = function f(){
if(!ajaxState.toDisable){
ajaxState.toDisable = document.querySelectorAll(
'button, input, select, textarea'
);
}
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);
}
};
F.onPageLoad(function() {
P.base = {tag: E('base')};
P.base.originalHref = P.base.tag.href;
P.tabs = new F.TabManager('#fileedit-tabs');
P.e = {
taEditor: E('#fileedit-content-editor'),
taCommentSmall: E('#fileedit-comment'),
taCommentBig: E('#fileedit-comment-big'),
taComment: undefined,
ajaxContentTarget: E('#ajax-target'),
btnCommit: E("#fileedit-btn-commit"),
btnReload: E("#fileedit-tab-content button.fileedit-content-reload"),
selectPreviewMode: E('#select-preview-mode select'),
selectHtmlEmsWrap: E('#select-preview-html-ems'),
selectEolWrap:  E('#select-eol-style'),
selectEol:  E('#select-eol-style select[name=eol]'),
selectFontSizeWrap: E('#select-font-size'),
selectDiffWS:  E('select[name=diff_ws]'),
cbLineNumbersWrap: E('#cb-line-numbers'),
cbAutoPreview: E('#cb-preview-autorefresh'),
previewTarget: E('#fileedit-tab-preview-wrapper'),
manifestTarget: E('#fileedit-manifest'),
diffTarget: E('#fileedit-tab-diff-wrapper'),
cbIsExe: E('input[type=checkbox][name=exec_bit]'),
cbManifest: E('input[type=checkbox][name=include_manifest]'),
editStatus: E('#fileedit-edit-status'),
tabs:{
content: E('#fileedit-tab-content'),
preview: E('#fileedit-tab-preview'),
diff: E('#fileedit-tab-diff'),
commit: E('#fileedit-tab-commit'),
fileSelect: E('#fileedit-tab-fileselect')
}
};
if(D.hasClass(P.e.taCommentSmall, 'hidden')){
P.e.taComment = P.e.taCommentBig;
}else if(D.hasClass(P.e.taCommentBig,'hidden')){
P.e.taComment = P.e.taCommentSmall;
}else{
P.e.taComment = P.e.taCommentSmall;
D.addClass(P.e.taCommentBig, 'hidden');
}
D.removeClass(P.e.taComment, 'hidden');
P.tabs.addCustomWidget( E('#fossil-status-bar') ).addCustomWidget(P.e.editStatus);
let currentTab;
P.tabs.addEventListener(
'before-switch-to', function(ev){
currentTab = ev.detail;
if(ev.detail===P.e.tabs.preview){
P.baseHrefForFile();
if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview();
}else if(ev.detail===P.e.tabs.diff){
D.removeClass(P.e.diffTarget, 'hidden');
}
}
);
P.tabs.addEventListener(
'before-switch-from', function(ev){
if(ev.detail===P.e.tabs.preview){
P.baseHrefRestore();
}else if(ev.detail===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();
}
return false;
}
}, 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('#fileedit-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
);
P.e.btnCommit.addEventListener(
"click",(e)=>P.commit(), false
);
P.tabs.switchToTab(1);
F.confirmer(P.e.btnReload, {
pinSize: true,
confirmText: "Really reload, losing edits?",
onconfirm: (e)=>P.unstashContent().loadFile(),
ticks: F.config.confirmerButtonTicks
});
E('#comment-toggle').addEventListener(
"click",(e)=>P.toggleCommentMode(), false
);
P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
P.e.cbIsExe.addEventListener(
'change', ()=>P.stashContentChange(true), false
);
P.e.selectPreviewMode.addEventListener(
"change", function(e){
const mode = e.target.value,
name = P.previewModes[mode],
hide = [], unhide = [];
P.previewModes.current = name;
if('guess'===name){
unhide.push(P.e.cbLineNumbersWrap,
P.e.selectHtmlEmsWrap);
}else{
if('text'===name) unhide.push(P.e.cbLineNumbersWrap);
else hide.push(P.e.cbLineNumbersWrap);
if('htmlIframe'===name) unhide.push(P.e.selectHtmlEmsWrap);
else hide.push(P.e.selectHtmlEmsWrap);
}
hide.forEach((e)=>e.classList.add('hidden'));
unhide.forEach((e)=>e.classList.remove('hidden'));
}, false
);
P.selectPreviewMode(false, true);
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(
'fileedit-content-replaced',
()=>{
P.previewNeedsUpdate = true;
D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget);
}
);
P.addEventListener(
'fileedit-committed',
(e)=>{
if(!e.detail.dryRun){
D.clearElement(P.e.diffTarget, P.e.previewTarget);
}
}
);
P.fileSelectWidget.init();
P.stashWidget.init(
P.e.tabs.content.lastElementChild
);
});
P.fileContent = function f(){
if(0===arguments.length){
return f.get();
}else{
f.set(arguments[0] || '');
this.dispatchEvent('fileedit-content-replaced', this);
return this;
}
};
P.fileContent.get = function(){return P.e.taEditor.value};
P.fileContent.set = function(content){P.e.taEditor.value = content};
P.setContentMethods = function(getter, setter){
this.fileContent.get = getter;
this.fileContent.set = setter;
return this;
};
P.notifyOfChange = function(){
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.baseHrefForFile = function f(){
const fn = this.finfo ? this.finfo.filename : undefined;
if(!fn) return this;
if(!f.wikiMimeTypes){
f.wikiMimeTypes = ["text/x-fossil-wiki", "text/x-markdown"];
}
if('wiki'===P.previewModes.current
|| ('guess'===P.previewModes.current
&& f.wikiMimeTypes.indexOf(this.finfo.mimetype)>=0)){
const a = fn.split('/');
a.pop();
this.base.tag.href = F.repoUrl(
'doc/'+F.hashDigits(this.finfo.checkin)
+'/'+(a.length ? a.join('/')+'/' : '')
);
}
return this;
};
P.baseHrefRestore = function(){
P.base.tag.href = P.base.originalHref;
};
P.toggleCommentMode = function(){
var s, h, c = this.e.taComment.value;
if(this.e.taComment === this.e.taCommentSmall){
s = this.e.taCommentBig;
h = this.e.taCommentSmall;
}else{
s = this.e.taCommentSmall;
h = this.e.taCommentBig;
c = c.replace(/\r?\n/g,' ');
}
s.value = c;
this.e.taComment = s;
D.addClass(h, 'hidden');
D.removeClass(s, 'hidden');
};
const affirmHasFile = function(quiet){
if(!P.finfo){
if(!quiet) F.error("No file is loaded.");
}
return !!P.finfo;
};
P.updateVersion = function f(file,rev){
if(!f.eLinks){
f.eName = P.e.editStatus.querySelector('span.name');
f.eLinks = P.e.editStatus.querySelector('span.links');
}
if(1===arguments.length){
this.finfo = arguments[0];
file = this.finfo.filename;
rev = this.finfo.checkin;
}else if(0===arguments.length){
if(affirmHasFile()){
file = this.finfo.filename;
rev = this.finfo.checkin;
}
}else{
this.finfo = {filename:file,checkin:rev};
}
const fi = this.finfo;
D.clearElement(f.eName, f.eLinks);
if(!fi){
D.append(f.eName, '(no file loaded)');
return this;
}
const rHuman = F.hashDigits(rev),
rUrl = F.hashDigits(rev,true);
D.append(f.eName,D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file));
D.append(
f.eLinks,
D.append(D.span(), fi.mimetype||'?mimetype?'),
D.a(F.repoUrl('info/'+rUrl), rHuman),
D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"),
D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}),'annotate'),
D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}),'blame')
);
const purlArgs = F.encodeUrlArgs({
filename: this.finfo.filename,
checkin: rUrl
},false,true);
const purl = F.repoUrl('fileedit',purlArgs);
D.append( f.eLinks, D.a(purl,"editor permalink") );
this.setPageTitle("Edit: "+fi.filename);
return this;
};
P.loadFile = function(file,rev){
if(0===arguments.length){
if(!affirmHasFile()) return this;
file = this.finfo.filename;
rev = this.finfo.checkin;
}else if(1===arguments.length){
const arg = arguments[0];
file = arg.filename;
rev = arg.checkin;
}
const self = this;
const onload = (r,headers)=>{
delete self.finfo;
self.updateVersion({
filename: file,
checkin: rev,
branch: headers['x-fileedit-checkin-branch'],
isExe: ('x'===headers['x-fileedit-file-perm']),
mimetype: headers['content-type'].split(';').shift()
});
self.tabs.switchToTab(self.e.tabs.content);
self.e.cbIsExe.checked = self.finfo.isExe;
self.fileContent(r);
P.previewNeedsUpdate = true;
self.dispatchEvent('fileedit-file-loaded', self.finfo);
};
const semiFinfo = {filename: file, checkin: rev};
const stashFinfo = this.getStashedFinfo(semiFinfo);
if(stashFinfo){
this.finfo = stashFinfo;
this.e.cbIsExe.checked = !!stashFinfo.isExe;
onload(this.contentFromStash()||'',{
'x-fileedit-file-perm': stashFinfo.isExe ? 'x' : undefined,
'content-type': stashFinfo.mimetype,
'x-fileedit-checkin-branch': stashFinfo.branch
});
F.message("Fetched from the local-edit storage:",
F.hashDigits(stashFinfo.checkin),
stashFinfo.filename);
return this;
}
F.message(
"Loading content..."
).fetch('fileedit/content',{
urlParams: {
filename:file,
checkin:rev
},
responseHeaders: [
'x-fileedit-file-perm',
'x-fileedit-checkin-branch',
'content-type'],
onload:(r,headers)=>{
onload(r,headers);
F.message('Loaded content for',
F.hashDigits(self.finfo.checkin),
self.finfo.filename);
}
});
return this;
};
P.preview = function f(switchToTab){
if(!affirmHasFile()) return this;
return this._postPreview(this.fileContent(), 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(!affirmHasFile()) return this;
if(!content){
callback(content);
return this;
}
const fd = new FormData();
fd.append('render_mode',this.e.selectPreviewMode.value);
fd.append('filename',this.finfo.filename);
fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0);
fd.append('iframe_height', E('[name=preview_html_ems]').value);
fd.append('content',content || '');
F.message(
"Fetching preview..."
).fetch('ajax/preview-text',{
payload: fd,
responseHeaders: 'x-ajax-render-mode',
onload: (r,header)=>{
P.selectPreviewMode(P.previewModes[header]);
if('wiki'===header) P.baseHrefForFile();
else P.baseHrefRestore();
callback(r);
F.message('Updated preview.');
P.previewNeedsUpdate = false;
P.dispatchEvent('fileedit-preview-updated',{
previewMode: P.previewModes.current,
mimetype: P.finfo.mimetype,
element: P.e.previewTarget
});
},
onerror: (e)=>{
F.fetch.onerror(e);
callback("Error fetching preview: "+e);
}
});
return this;
};
P.diff = function f(sbs){
if(!affirmHasFile()) return this;
const content = this.fileContent(),
self = this,
target = this.e.diffTarget;
const fd = new FormData();
fd.append('filename',this.finfo.filename);
fd.append('checkin', this.finfo.checkin);
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('fileedit/diff',{
payload: fd,
onload: function(c){
D.parseHtml(D.clearElement(target),[
"<div>Diff <code>[",
self.finfo.checkin,
"]</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.commit = function f(){
if(!affirmHasFile()) return this;
const self = this;
const content = this.fileContent(),
target = D.clearElement(P.e.manifestTarget),
cbDryRun = E('[name=dry_run]'),
isDryRun = cbDryRun.checked,
filename = this.finfo.filename;
if(!f.onload){
f.onload = function(c){
const oldFinfo = JSON.parse(JSON.stringify(self.finfo))
if(c.manifest){
D.parseHtml(D.clearElement(target), [
"<h3>Manifest",
(c.dryRun?" (dry run)":""),
": ", F.hashDigits(c.checkin),"</h3>",
"<pre><code class='fileedit-manifest'>",
c.manifest.replace(/</g,'&lt;'),
"</code></pre>"
].join(''));
delete c.manifest;
}
const msg = [
'Committed',
c.dryRun ? '(dry run)' : '',
'[', F.hashDigits(c.checkin) ,'].'
];
if(!c.dryRun){
self.unstashContent(oldFinfo);
self.finfo = c;
self.e.taComment.value = '';
self.updateVersion();
self.fileSelectWidget.loadLeaves();
}
self.dispatchEvent('fileedit-committed', c);
F.message.apply(F, msg);
self.tabs.switchToTab(self.e.tabs.commit);
};
}
const fd = new FormData();
fd.append('filename',filename);
fd.append('checkin', this.finfo.checkin);
fd.append('content',content);
fd.append('dry_run',isDryRun ? 1 : 0);
fd.append('eol', this.e.selectEol.value || 0);
fd.append('comment', this.e.taComment.value);
if(0){
['comment_mimetype'
].forEach(function(name){
var e = E('[name='+name+']');
if(e) fd.append(name,e.value);
});
}
['allow_fork',
'allow_older',
'exec_bit',
'allow_merge_conflict',
'include_manifest',
'prefer_delta'
].forEach(function(name){
var e = E('[name='+name+']');
if(e){
fd.append(name, e.checked ? 1 : 0);
}else{
console.error("Missing checkbox? name =",name);
}
});
F.message(
"Checking in..."
).fetch('fileedit/commit',{
payload: fd,
responseType: 'json',
onload: f.onload
});
return this;
};
P.stashContentChange = function(onlyFinfo){
if(affirmHasFile(true)){
const fi = this.finfo;
fi.isExe = this.e.cbIsExe.checked;
if(!fi.branch) fi.branch = this.fileSelectWidget.checkinBranchName(fi.checkin);
if(onlyFinfo && $stash.hasStashedContent(fi)){
$stash.updateFile(fi);
}else{
$stash.updateFile(fi, P.fileContent());
}
F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename);
$stash.prune();
this.previewNeedsUpdate = true;
}
return this;
};
P.unstashContent = function(){
const finfo = arguments[0] || this.finfo;
if(finfo){
this.previewNeedsUpdate = true;
$stash.unstash(finfo);
F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename);
}
return this;
};
P.clearStash = function(){
$stash.clear();
return this;
};
P.contentFromStash = function(){
return affirmHasFile(true) ? $stash.stashedContent(this.finfo) : undefined;
};
P.getStashedFinfo = function(finfo){
return $stash.getFinfo(finfo);
};
P.$stash = $stash;
})(window.fossil);