mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-08-07 22:33:50 +00:00
Fix for autosave
Re-introduces the “tw-auto-save-wiki” message. The previous approach of automatically triggering autosave whenever a tiddler changed meant that changing configuration changes in control panel was triggering an autosave. Using the explicit message gives us better control of the situations in which we’ll autosave. Now we solve the earlier problem of there being outstanding tiddler change events at the time that we process the “tw-auto-save-wiki” by deferring the autosave until the outstanding change event comes in.
This commit is contained in:
parent
48312272ad
commit
2952afe7af
@ -18,64 +18,86 @@ wiki: wiki to be synced
|
|||||||
dirtyTracking: true if dirty tracking should be performed
|
dirtyTracking: true if dirty tracking should be performed
|
||||||
*/
|
*/
|
||||||
function SaverHandler(options) {
|
function SaverHandler(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.wiki = options.wiki;
|
this.wiki = options.wiki;
|
||||||
this.dirtyTracking = options.dirtyTracking;
|
this.dirtyTracking = options.dirtyTracking;
|
||||||
// Make a logger
|
this.pendingAutoSave = false;
|
||||||
this.logger = new $tw.utils.Logger("saver-handler");
|
// Make a logger
|
||||||
// Initialise our savers
|
this.logger = new $tw.utils.Logger("saver-handler");
|
||||||
if($tw.browser) {
|
// Initialise our savers
|
||||||
this.initSavers();
|
if($tw.browser) {
|
||||||
}
|
this.initSavers();
|
||||||
// Only do dirty tracking if required
|
}
|
||||||
if($tw.browser && this.dirtyTracking) {
|
// Only do dirty tracking if required
|
||||||
// Compile the dirty tiddler filter
|
if($tw.browser && this.dirtyTracking) {
|
||||||
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
// Compile the dirty tiddler filter
|
||||||
// Count of tiddlers that have been changed but not yet saved
|
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
||||||
this.numTasksInQueue = 0;
|
// Count of tiddlers that have been changed but not yet saved
|
||||||
// Listen out for changes to tiddlers
|
this.numTasksInQueue = 0;
|
||||||
this.wiki.addEventListener("change",function(changes) {
|
// Listen out for changes to tiddlers
|
||||||
var filteredChanges = self.filterFn.call(self.wiki,function(callback) {
|
this.wiki.addEventListener("change",function(changes) {
|
||||||
$tw.utils.each(changes,function(change,title) {
|
var filteredChanges = self.filterFn.call(self.wiki,function(callback) {
|
||||||
var tiddler = self.wiki.getTiddler(title);
|
$tw.utils.each(changes,function(change,title) {
|
||||||
callback(tiddler,title);
|
var tiddler = self.wiki.getTiddler(title);
|
||||||
});
|
callback(tiddler,title);
|
||||||
});
|
});
|
||||||
self.numTasksInQueue += filteredChanges.length;
|
});
|
||||||
self.updateDirtyStatus();
|
self.numTasksInQueue += filteredChanges.length;
|
||||||
if(self.numTasksInQueue > 0) {
|
self.updateDirtyStatus();
|
||||||
self.saveWiki({method: "autosave"});
|
// Do any autosave if one is pending and there's no more change events
|
||||||
}
|
if(self.pendingAutoSave && self.wiki.getSizeOfTiddlerEventQueue() === 0) {
|
||||||
});
|
// Check if we're dirty
|
||||||
// Browser event handlers
|
if(self.numTasksInQueue > 0) {
|
||||||
if($tw.browser) {
|
self.saveWiki({
|
||||||
// Set up our beforeunload handler
|
method: "autosave",
|
||||||
window.addEventListener("beforeunload",function(event) {
|
downloadType: "text/plain"
|
||||||
var confirmationMessage = undefined;
|
});
|
||||||
if(self.isDirty()) {
|
}
|
||||||
confirmationMessage = $tw.language.getString("UnsavedChangesWarning");
|
self.pendingAutoSave = false;
|
||||||
event.returnValue = confirmationMessage; // Gecko
|
}
|
||||||
}
|
});
|
||||||
return confirmationMessage;
|
// Listen for the autosave event
|
||||||
});
|
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
|
||||||
}
|
// Do the autosave unless there are outstanding tiddler change events
|
||||||
}
|
if(self.wiki.getSizeOfTiddlerEventQueue() === 0) {
|
||||||
// Install the save action handlers
|
// Check if we're dirty
|
||||||
if($tw.browser) {
|
if(self.numTasksInQueue > 0) {
|
||||||
$tw.rootWidget.addEventListener("tm-save-wiki",function(event) {
|
self.saveWiki({
|
||||||
self.saveWiki({
|
method: "autosave",
|
||||||
template: event.param,
|
downloadType: "text/plain"
|
||||||
downloadType: "text/plain"
|
});
|
||||||
});
|
}
|
||||||
});
|
} else {
|
||||||
$tw.rootWidget.addEventListener("tm-download-file",function(event) {
|
// Otherwise put ourselves in the "pending autosave" state and wait for the change event before we do the autosave
|
||||||
self.saveWiki({
|
self.pendingAutoSave = true;
|
||||||
method: "download",
|
}
|
||||||
template: event.param,
|
});
|
||||||
downloadType: "text/plain"
|
// Set up our beforeunload handler
|
||||||
});
|
window.addEventListener("beforeunload",function(event) {
|
||||||
});
|
var confirmationMessage = undefined;
|
||||||
}
|
if(self.isDirty()) {
|
||||||
|
confirmationMessage = $tw.language.getString("UnsavedChangesWarning");
|
||||||
|
event.returnValue = confirmationMessage; // Gecko
|
||||||
|
}
|
||||||
|
return confirmationMessage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Install the save action handlers
|
||||||
|
if($tw.browser) {
|
||||||
|
$tw.rootWidget.addEventListener("tm-save-wiki",function(event) {
|
||||||
|
self.saveWiki({
|
||||||
|
template: event.param,
|
||||||
|
downloadType: "text/plain"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$tw.rootWidget.addEventListener("tm-download-file",function(event) {
|
||||||
|
self.saveWiki({
|
||||||
|
method: "download",
|
||||||
|
template: event.param,
|
||||||
|
downloadType: "text/plain"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SaverHandler.prototype.titleSyncFilter = "$:/config/SaverFilter";
|
SaverHandler.prototype.titleSyncFilter = "$:/config/SaverFilter";
|
||||||
@ -86,86 +108,86 @@ SaverHandler.prototype.titleSavedNotification = "$:/language/Notifications/Save/
|
|||||||
Select the appropriate saver modules and set them up
|
Select the appropriate saver modules and set them up
|
||||||
*/
|
*/
|
||||||
SaverHandler.prototype.initSavers = function(moduleType) {
|
SaverHandler.prototype.initSavers = function(moduleType) {
|
||||||
moduleType = moduleType || "saver";
|
moduleType = moduleType || "saver";
|
||||||
// Instantiate the available savers
|
// Instantiate the available savers
|
||||||
this.savers = [];
|
this.savers = [];
|
||||||
var self = this;
|
var self = this;
|
||||||
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
|
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
|
||||||
if(module.canSave(self)) {
|
if(module.canSave(self)) {
|
||||||
self.savers.push(module.create(self.wiki));
|
self.savers.push(module.create(self.wiki));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Sort the savers into priority order
|
// Sort the savers into priority order
|
||||||
this.savers.sort(function(a,b) {
|
this.savers.sort(function(a,b) {
|
||||||
if(a.info.priority < b.info.priority) {
|
if(a.info.priority < b.info.priority) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
if(a.info.priority > b.info.priority) {
|
if(a.info.priority > b.info.priority) {
|
||||||
return +1;
|
return +1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Save the wiki contents. Options are:
|
Save the wiki contents. Options are:
|
||||||
method: "save", "autosave" or "download"
|
method: "save", "autosave" or "download"
|
||||||
template: the tiddler containing the template to save
|
template: the tiddler containing the template to save
|
||||||
downloadType: the content type for the saved file
|
downloadType: the content type for the saved file
|
||||||
*/
|
*/
|
||||||
SaverHandler.prototype.saveWiki = function(options) {
|
SaverHandler.prototype.saveWiki = function(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var self = this,
|
var self = this,
|
||||||
method = options.method || "save",
|
method = options.method || "save",
|
||||||
template = options.template || "$:/core/save/all",
|
template = options.template || "$:/core/save/all",
|
||||||
downloadType = options.downloadType || "text/plain",
|
downloadType = options.downloadType || "text/plain",
|
||||||
text = this.wiki.renderTiddler(downloadType,template),
|
text = this.wiki.renderTiddler(downloadType,template),
|
||||||
callback = function(err) {
|
callback = function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
alert("Error while saving:\n\n" + err);
|
alert("Error while saving:\n\n" + err);
|
||||||
} else {
|
} else {
|
||||||
// Clear the task queue if we're saving (rather than downloading)
|
// Clear the task queue if we're saving (rather than downloading)
|
||||||
if(method !== "download") {
|
if(method !== "download") {
|
||||||
self.numTasksInQueue = 0;
|
self.numTasksInQueue = 0;
|
||||||
self.updateDirtyStatus();
|
self.updateDirtyStatus();
|
||||||
}
|
}
|
||||||
$tw.notifier.display(self.titleSavedNotification);
|
$tw.notifier.display(self.titleSavedNotification);
|
||||||
if(options.callback) {
|
if(options.callback) {
|
||||||
options.callback();
|
options.callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Ignore autosave if disabled
|
// Ignore autosave if disabled
|
||||||
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
|
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Call the highest priority saver that supports this method
|
// Call the highest priority saver that supports this method
|
||||||
for(var t=this.savers.length-1; t>=0; t--) {
|
for(var t=this.savers.length-1; t>=0; t--) {
|
||||||
var saver = this.savers[t];
|
var saver = this.savers[t];
|
||||||
if(saver.info.capabilities.indexOf(method) !== -1 && saver.save(text,method,callback)) {
|
if(saver.info.capabilities.indexOf(method) !== -1 && saver.save(text,method,callback)) {
|
||||||
this.logger.log("Saving wiki with method",method,"through saver",saver.info.name);
|
this.logger.log("Saving wiki with method",method,"through saver",saver.info.name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
||||||
*/
|
*/
|
||||||
SaverHandler.prototype.isDirty = function() {
|
SaverHandler.prototype.isDirty = function() {
|
||||||
return this.numTasksInQueue > 0;
|
return this.numTasksInQueue > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Update the document body with the class "tc-dirty" if the wiki has unsaved/unsynced changes
|
Update the document body with the class "tc-dirty" if the wiki has unsaved/unsynced changes
|
||||||
*/
|
*/
|
||||||
SaverHandler.prototype.updateDirtyStatus = function() {
|
SaverHandler.prototype.updateDirtyStatus = function() {
|
||||||
if($tw.browser) {
|
if($tw.browser) {
|
||||||
$tw.utils.toggleClass(document.body,"tc-dirty",this.isDirty());
|
$tw.utils.toggleClass(document.body,"tc-dirty",this.isDirty());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.SaverHandler = SaverHandler;
|
exports.SaverHandler = SaverHandler;
|
||||||
|
@ -71,6 +71,8 @@ exports.repackPlugin = function(title,additionalTiddlers,excludeTiddlers) {
|
|||||||
$tw.wiki.deleteTiddler(title);
|
$tw.wiki.deleteTiddler(title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Trigger an autosave
|
||||||
|
$tw.rootWidget.dispatchEvent({type: "tw-auto-save-wiki"});
|
||||||
// Return a heartwarming confirmation
|
// Return a heartwarming confirmation
|
||||||
return "Plugin " + title + " successfully saved";
|
return "Plugin " + title + " successfully saved";
|
||||||
}
|
}
|
||||||
|
@ -243,6 +243,8 @@ NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
|
|||||||
// Remove the closed tiddler from the story
|
// Remove the closed tiddler from the story
|
||||||
this.removeTitleFromStory(storyList,title);
|
this.removeTitleFromStory(storyList,title);
|
||||||
this.saveStoryList(storyList);
|
this.saveStoryList(storyList);
|
||||||
|
// Trigger an autosave
|
||||||
|
$tw.rootWidget.dispatchEvent({type: "tw-auto-save-wiki"});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -331,6 +333,8 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
|
|||||||
if(draftTitle !== this.storyTitle) {
|
if(draftTitle !== this.storyTitle) {
|
||||||
this.saveStoryList(storyList);
|
this.saveStoryList(storyList);
|
||||||
}
|
}
|
||||||
|
// Trigger an autosave
|
||||||
|
$tw.rootWidget.dispatchEvent({type: "tw-auto-save-wiki"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,6 +482,8 @@ NavigatorWidget.prototype.handlePerformImportEvent = function(event) {
|
|||||||
}));
|
}));
|
||||||
// Navigate to the $:/Import tiddler
|
// Navigate to the $:/Import tiddler
|
||||||
this.addToHistory([IMPORT_TITLE]);
|
this.addToHistory([IMPORT_TITLE]);
|
||||||
|
// Trigger an autosave
|
||||||
|
$tw.rootWidget.dispatchEvent({type: "tw-auto-save-wiki"});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.navigator = NavigatorWidget;
|
exports.navigator = NavigatorWidget;
|
||||||
|
@ -152,6 +152,10 @@ exports.enqueueTiddlerEvent = function(title,isDeleted) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.getSizeOfTiddlerEventQueue = function() {
|
||||||
|
return $tw.utils.count(this.changedTiddlers);
|
||||||
|
};
|
||||||
|
|
||||||
exports.clearTiddlerEventQueue = function() {
|
exports.clearTiddlerEventQueue = function() {
|
||||||
this.changedTiddlers = Object.create(null);
|
this.changedTiddlers = Object.create(null);
|
||||||
this.changeCount = Object.create(null)
|
this.changeCount = Object.create(null)
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
title: $:/config/SaverFilter
|
title: $:/config/SaverFilter
|
||||||
|
|
||||||
[!is[shadow]] -[[$:/HistoryList]] -[[$:/StoryList]] -[[$:/Import]] -[[$:/isEncrypted]] -[[$:/UploadName]] -[prefix[$:/status]] -[prefix[$:/state]] -[prefix[$:/temp]] -[has[draft.of]]
|
[all[]] -[[$:/HistoryList]] -[[$:/StoryList]] -[[$:/Import]] -[[$:/isEncrypted]] -[[$:/UploadName]] -[prefix[$:/state]] -[prefix[$:/temp]] -[has[draft.of]]
|
Loading…
x
Reference in New Issue
Block a user