Separate the saver handling out of the syncer

This commit is contained in:
Jermolene 2014-08-14 11:43:07 +01:00
parent 27f1f82a70
commit f75af2c983
3 changed files with 177 additions and 108 deletions

View File

@ -0,0 +1,170 @@
/*\
title: $:/core/modules/saver-handler.js
type: application/javascript
module-type: global
The saver handler tracks changes to the store and handles saving the entire wiki via saver modules.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Instantiate the saver handler with the following options:
wiki: wiki to be synced
*/
function SaverHandler(options) {
var self = this;
this.wiki = options.wiki;
// Make a logger
this.logger = new $tw.utils.Logger("saver-handler");
// Initialise our savers
if($tw.browser) {
this.initSavers();
}
// Compile the dirty tiddler filter
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
// Count of tiddlers that have been changed but not yet saved
this.numTasksInQueue = 0;
// Listen out for changes to tiddlers
this.wiki.addEventListener("change",function(changes) {
var filteredChanges = self.filterFn.call(self.wiki,function(callback) {
$tw.utils.each(changes,function(change,title) {
var tiddler = self.wiki.getTiddler(title);
callback(tiddler,title);
});
});
self.numTasksInQueue += filteredChanges.length;
self.updateDirtyStatus();
});
// Browser event handlers
if($tw.browser) {
// 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
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
self.saveWiki({
template: event.param,
downloadType: "text/plain"
});
});
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
self.saveWiki({
method: "autosave",
template: event.param,
downloadType: "text/plain"
});
});
$tw.rootWidget.addEventListener("tw-download-file",function(event) {
self.saveWiki({
method: "download",
template: event.param,
downloadType: "text/plain"
});
});
}
}
SaverHandler.prototype.titleSyncFilter = "$:/config/SyncFilter";
SaverHandler.prototype.titleAutoSave = "$:/config/AutoSave";
SaverHandler.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
/*
Select the appropriate saver modules and set them up
*/
SaverHandler.prototype.initSavers = function(moduleType) {
moduleType = moduleType || "saver";
// Instantiate the available savers
this.savers = [];
var self = this;
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
if(module.canSave(self)) {
self.savers.push(module.create(self.wiki));
}
});
// Sort the savers into priority order
this.savers.sort(function(a,b) {
if(a.info.priority < b.info.priority) {
return -1;
} else {
if(a.info.priority > b.info.priority) {
return +1;
} else {
return 0;
}
}
});
};
/*
Save the wiki contents. Options are:
method: "save" or "download"
template: the tiddler containing the template to save
downloadType: the content type for the saved file
*/
SaverHandler.prototype.saveWiki = function(options) {
options = options || {};
var self = this,
method = options.method || "save",
template = options.template || "$:/core/save/all",
downloadType = options.downloadType || "text/plain",
text = this.wiki.renderTiddler(downloadType,template),
callback = function(err) {
if(err) {
alert("Error while saving:\n\n" + err);
} else {
$tw.notifier.display(self.titleSavedNotification);
if(options.callback) {
options.callback();
}
}
};
// Ignore autosave if disabled
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
return false;
}
// Call the highest priority saver that supports this method
for(var t=this.savers.length-1; t>=0; t--) {
var saver = this.savers[t];
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);
// Clear the task queue if we're saving (rather than downloading)
if(method !== "download") {
this.numTasksInQueue = 0;
this.updateDirtyStatus();
}
return true;
}
}
return false;
};
/*
Checks whether the wiki is dirty (ie the window shouldn't be closed)
*/
SaverHandler.prototype.isDirty = function() {
return this.numTasksInQueue > 0;
};
/*
Update the document body with the class "tw-dirty" if the wiki has unsaved/unsynced changes
*/
SaverHandler.prototype.updateDirtyStatus = function() {
if($tw.browser) {
$tw.utils.toggleClass(document.body,"tw-dirty",this.isDirty());
}
};
exports.SaverHandler = SaverHandler;
})();

View File

@ -69,9 +69,11 @@ exports.startup = function() {
$tw.syncadaptor = new module.adaptorClass({wiki: $tw.wiki});
}
});
// Set up the syncer object
// Set up the syncer object if we've got a syncadaptor, otherwise setup the saverhandler
if($tw.syncadaptor) {
$tw.syncer = new $tw.Syncer({wiki: $tw.wiki, syncadaptor: $tw.syncadaptor});
} else {
$tw.saverHandler = new $tw.SaverHandler({wiki: $tw.wiki});
}
// Host-specific startup
if($tw.browser) {

View File

@ -23,10 +23,6 @@ function Syncer(options) {
this.syncadaptor = options.syncadaptor
// Make a logger
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : ""));
// Initialise our savers
if($tw.browser) {
this.initSavers();
}
// Compile the dirty tiddler filter
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
// Record information for known tiddlers
@ -61,34 +57,11 @@ function Syncer(options) {
$tw.rootWidget.addEventListener("tw-server-refresh",function() {
self.handleRefreshEvent();
});
// Install the save action handlers
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
self.saveWiki({
template: event.param,
downloadType: "text/plain"
});
});
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
self.saveWiki({
method: "autosave",
template: event.param,
downloadType: "text/plain"
});
});
$tw.rootWidget.addEventListener("tw-download-file",function(event) {
self.saveWiki({
method: "download",
template: event.param,
downloadType: "text/plain"
});
});
}
// Listen out for lazyLoad events
if(this.syncadaptor) {
this.wiki.addEventListener("lazyLoad",function(title) {
self.handleLazyLoadEvent(title);
});
}
this.wiki.addEventListener("lazyLoad",function(title) {
self.handleLazyLoadEvent(title);
});
// Get the login status
this.getStatus(function (err,isLoggedIn) {
// Do a sync from the server
@ -102,7 +75,6 @@ Constants
Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn";
Syncer.prototype.titleUserName = "$:/status/UserName";
Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
Syncer.prototype.titleAutoSave = "$:/config/AutoSave";
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
@ -129,79 +101,6 @@ Syncer.prototype.readTiddlerInfo = function() {
});
};
/*
Select the appropriate saver modules and set them up
*/
Syncer.prototype.initSavers = function(moduleType) {
moduleType = moduleType || "saver";
// Instantiate the available savers
this.savers = [];
var self = this;
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
if(module.canSave(self)) {
self.savers.push(module.create(self.wiki));
}
});
// Sort the savers into priority order
this.savers.sort(function(a,b) {
if(a.info.priority < b.info.priority) {
return -1;
} else {
if(a.info.priority > b.info.priority) {
return +1;
} else {
return 0;
}
}
});
};
/*
Save the wiki contents. Options are:
method: "save" or "download"
template: the tiddler containing the template to save
downloadType: the content type for the saved file
*/
Syncer.prototype.saveWiki = function(options) {
options = options || {};
var self = this,
method = options.method || "save",
template = options.template || "$:/core/save/all",
downloadType = options.downloadType || "text/plain",
text = this.wiki.renderTiddler(downloadType,template),
callback = function(err) {
if(err) {
alert("Error while saving:\n\n" + err);
} else {
$tw.notifier.display(self.titleSavedNotification);
if(options.callback) {
options.callback();
}
}
};
// Ignore autosave if we've got a syncadaptor or autosave is disabled
if(method === "autosave") {
if(this.syncadaptor || this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
return false;
}
}
// Call the highest priority saver that supports this method
for(var t=this.savers.length-1; t>=0; t--) {
var saver = this.savers[t];
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);
// Clear the task queue if we're saving (rather than downloading)
if(method !== "download") {
this.readTiddlerInfo();
this.taskQueue = {};
this.updateDirtyStatus();
}
return true;
}
}
return false;
};
/*
Checks whether the wiki is dirty (ie the window shouldn't be closed)
*/
@ -453,9 +352,7 @@ Syncer.prototype.enqueueSyncTask = function(task) {
this.updateDirtyStatus();
}
// Process the queue
if(this.syncadaptor) {
$tw.utils.nextTick(function() {self.processTaskQueue.call(self);});
}
$tw.utils.nextTick(function() {self.processTaskQueue.call(self);});
};
/*