1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 05:19:57 +00:00

Add automatic saving and warning on exit with unsaved changes

We re-use some of the existing syncer mechanism. It was already keeping
track of changes to tiddlers in the store when working with a tiddler
syncadaptor. Now it also tracks changes when there is no syncadaptor,
allowing us to provide a warning if there are unsaved changes.
This commit is contained in:
Jermolene 2014-02-06 21:36:30 +00:00
parent 8e8e31fb9f
commit a01bbd4b9c
19 changed files with 216 additions and 174 deletions

View File

@ -56,6 +56,15 @@ exports.startup = function() {
$tw.wiki.addTiddler({title: storyTitle, text: "", list: story},$tw.wiki.getModificationFields()); $tw.wiki.addTiddler({title: storyTitle, text: "", list: story},$tw.wiki.getModificationFields());
// Host-specific startup // Host-specific startup
if($tw.browser) { if($tw.browser) {
// Set up our beforeunload handler
window.addEventListener("beforeunload",function(event) {
var confirmationMessage = null;
if($tw.syncer.isDirty()) {
confirmationMessage = "You have unsaved changes in TiddlyWiki";
event.returnValue = confirmationMessage; // Gecko
}
return confirmationMessage; // Webkit, Safari, Chrome etc.
});
// Install the popup manager // Install the popup manager
$tw.popup = new $tw.utils.Popup({ $tw.popup = new $tw.utils.Popup({
rootElement: document.body rootElement: document.body
@ -86,22 +95,21 @@ exports.startup = function() {
$tw.pageScroller.handleEvent(event); $tw.pageScroller.handleEvent(event);
}); });
// Install the save action handler // Install the save action handler
$tw.wiki.initSavers();
$tw.rootWidget.addEventListener("tw-save-wiki",function(event) { $tw.rootWidget.addEventListener("tw-save-wiki",function(event) {
$tw.wiki.saveWiki({ $tw.syncer.saveWiki({
template: event.param, template: event.param,
downloadType: "text/plain" downloadType: "text/plain"
}); });
}); });
$tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) { $tw.rootWidget.addEventListener("tw-auto-save-wiki",function(event) {
$tw.wiki.saveWiki({ $tw.syncer.saveWiki({
method: "autosave", method: "autosave",
template: event.param, template: event.param,
downloadType: "text/plain" downloadType: "text/plain"
}); });
}); });
$tw.rootWidget.addEventListener("tw-download-file",function(event) { $tw.rootWidget.addEventListener("tw-download-file",function(event) {
$tw.wiki.saveWiki({ $tw.syncer.saveWiki({
method: "download", method: "download",
template: event.param, template: event.param,
downloadType: "text/plain" downloadType: "text/plain"

View File

@ -3,7 +3,7 @@ title: $:/core/modules/syncer.js
type: application/javascript type: application/javascript
module-type: global module-type: global
The syncer transfers content to and from data sources using syncadaptor modules. The syncer tracks changes to the store. If a syncadaptor is used then individual tiddlers are synchronised through it. If there is no syncadaptor then the entire wiki is saved via saver modules.
\*/ \*/
(function(){ (function(){
@ -28,46 +28,16 @@ function Syncer(options) {
self.syncadaptor = new module.adaptorClass(self); self.syncadaptor = new module.adaptorClass(self);
} }
}); });
// Only do anything if we've got a syncadaptor // Initialise our savers
if(this.syncadaptor) { if($tw.browser) {
this.init(); this.initSavers();
} }
} // Compile the dirty tiddler filter
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
/*
Error handling
*/
Syncer.prototype.showError = function(error) {
this.log("Error: " + error);
};
/*
Constants
*/
Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn";
Syncer.prototype.titleUserName = "$:/status/UserName";
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...
Syncer.prototype.fallbackInterval = 10 * 1000; // Unless the task is older than 10s
Syncer.prototype.pollTimerInterval = 60 * 1000; // Interval for polling for changes from the adaptor
/*
Initialise the syncer
*/
Syncer.prototype.init = function() {
var self = this;
// Hashmap by title of {revision:,changeCount:,adaptorInfo:}
this.tiddlerInfo = {};
// Record information for known tiddlers // Record information for known tiddlers
this.wiki.forEachTiddler({includeSystem: true},function(title,tiddler) { this.readTiddlerInfo();
self.tiddlerInfo[title] = {
revision: tiddler.fields["revision"],
adaptorInfo: self.syncadaptor.getTiddlerInfo(tiddler),
changeCount: self.wiki.getChangeCount(title)
}
});
// Tasks are {type: "load"/"save"/"delete", title:, queueTime:, lastModificationTime:} // Tasks are {type: "load"/"save"/"delete", title:, queueTime:, lastModificationTime:}
this.taskQueue = {}; // Hashmap of tasks to be performed this.taskQueue = {}; // Hashmap of tasks yet to be performed
this.taskInProgress = {}; // Hash of tasks in progress this.taskInProgress = {}; // Hash of tasks in progress
this.taskTimerId = null; // Timer for task dispatch this.taskTimerId = null; // Timer for task dispatch
this.pollTimerId = null; // Timer for polling server this.pollTimerId = null; // Timer for polling server
@ -76,9 +46,11 @@ Syncer.prototype.init = function() {
self.syncToServer(changes); self.syncToServer(changes);
}); });
// Listen out for lazyLoad events // Listen out for lazyLoad events
this.wiki.addEventListener("lazyLoad",function(title) { if(this.syncadaptor) {
self.handleLazyLoadEvent(title); this.wiki.addEventListener("lazyLoad",function(title) {
}); self.handleLazyLoadEvent(title);
});
}
// Listen out for login/logout/refresh events in the browser // Listen out for login/logout/refresh events in the browser
if($tw.browser) { if($tw.browser) {
document.addEventListener("tw-login",function(event) { document.addEventListener("tw-login",function(event) {
@ -98,6 +70,120 @@ Syncer.prototype.init = function() {
self.syncFromServer(); self.syncFromServer();
} }
}); });
}
/*
Error handling
*/
Syncer.prototype.showError = function(error) {
this.log("Error: " + error);
};
/*
Constants
*/
Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn";
Syncer.prototype.titleUserName = "$:/status/UserName";
Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
Syncer.prototype.titleAutoSave = "$:/config/AutoSave";
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...
Syncer.prototype.fallbackInterval = 10 * 1000; // Unless the task is older than 10s
Syncer.prototype.pollTimerInterval = 60 * 1000; // Interval for polling for changes from the adaptor
/*
Read (or re-read) the latest tiddler info from the store
*/
Syncer.prototype.readTiddlerInfo = function() {
// Hashmap by title of {revision:,changeCount:,adaptorInfo:}
this.tiddlerInfo = {};
// Record information for known tiddlers
var self = this;
this.wiki.forEachTiddler({includeSystem: true},function(title,tiddler) {
self.tiddlerInfo[title] = {
revision: tiddler.fields["revision"],
adaptorInfo: self.syncadaptor && self.syncadaptor.getTiddlerInfo(tiddler),
changeCount: self.wiki.getChangeCount(title)
}
});
};
/*
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 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("$:/messages/Saved");
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.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 = {};
}
return true;
}
}
return false;
};
/*
Checks whether the wiki is dirty (ie the window shouldn't be closed)
*/
Syncer.prototype.isDirty = function() {
return (this.numTasksInQueue() > 0) || (this.numTasksInProgress() > 0);
}; };
/* /*
@ -118,7 +204,7 @@ Syncer.prototype.storeTiddler = function(tiddlerFields) {
Syncer.prototype.getStatus = function(callback) { Syncer.prototype.getStatus = function(callback) {
var self = this; var self = this;
// Check if the adaptor supports getStatus() // Check if the adaptor supports getStatus()
if(this.syncadaptor.getStatus) { if(this.syncadaptor && this.syncadaptor.getStatus) {
// Mark us as not logged in // Mark us as not logged in
this.wiki.addTiddler({title: this.titleIsLoggedIn,text: "no"}); this.wiki.addTiddler({title: this.titleIsLoggedIn,text: "no"});
// Get login status // Get login status
@ -130,7 +216,7 @@ Syncer.prototype.getStatus = function(callback) {
// Set the various status tiddlers // Set the various status tiddlers
self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"}); self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"});
if(isLoggedIn) { if(isLoggedIn) {
self.wiki.addTiddler({title: self.titleUserName,text: username}); self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
} else { } else {
self.wiki.deleteTiddler(self.titleUserName); self.wiki.deleteTiddler(self.titleUserName);
} }
@ -148,7 +234,7 @@ Syncer.prototype.getStatus = function(callback) {
Synchronise from the server by reading the skinny tiddler list and queuing up loads for any tiddlers that we don't already have up to date Synchronise from the server by reading the skinny tiddler list and queuing up loads for any tiddlers that we don't already have up to date
*/ */
Syncer.prototype.syncFromServer = function() { Syncer.prototype.syncFromServer = function() {
if(this.syncadaptor.getSkinnyTiddlers) { if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
this.log("Retrieving skinny tiddler list"); this.log("Retrieving skinny tiddler list");
var self = this; var self = this;
if(this.pollTimerId) { if(this.pollTimerId) {
@ -198,10 +284,11 @@ Synchronise a set of changes to the server
*/ */
Syncer.prototype.syncToServer = function(changes) { Syncer.prototype.syncToServer = function(changes) {
var self = this, var self = this,
now = new Date(); now = new Date(),
filteredChanges = this.filterFn.call(this.wiki,changes);
$tw.utils.each(changes,function(change,title,object) { $tw.utils.each(changes,function(change,title,object) {
// Ignore the change if it is a shadow tiddler // Ignore the change if it is a shadow tiddler
if((change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) || (!change.deleted && self.wiki.tiddlerExists(title))) { if((change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) || (!change.deleted && filteredChanges.indexOf(title) !== -1)) {
// Queue a task to sync this tiddler // Queue a task to sync this tiddler
self.enqueueSyncTask({ self.enqueueSyncTask({
type: change.deleted ? "delete" : "save", type: change.deleted ? "delete" : "save",
@ -215,6 +302,7 @@ Syncer.prototype.syncToServer = function(changes) {
Lazily load a skinny tiddler if we can Lazily load a skinny tiddler if we can
*/ */
Syncer.prototype.handleLazyLoadEvent = function(title) { Syncer.prototype.handleLazyLoadEvent = function(title) {
console.log("Lazy loading",title)
// Queue up a sync task to load this tiddler // Queue up a sync task to load this tiddler
this.enqueueSyncTask({ this.enqueueSyncTask({
type: "load", type: "load",
@ -314,7 +402,7 @@ Syncer.prototype.enqueueSyncTask = function(task) {
} }
// Check if this tiddler is already in the queue // Check if this tiddler is already in the queue
if($tw.utils.hop(this.taskQueue,task.title)) { if($tw.utils.hop(this.taskQueue,task.title)) {
this.log("Re-queueing up sync task with type:",task.type,"title:",task.title); // this.log("Re-queueing up sync task with type:",task.type,"title:",task.title);
var existingTask = this.taskQueue[task.title]; var existingTask = this.taskQueue[task.title];
// If so, just update the last modification time // If so, just update the last modification time
existingTask.lastModificationTime = task.lastModificationTime; existingTask.lastModificationTime = task.lastModificationTime;
@ -323,12 +411,14 @@ Syncer.prototype.enqueueSyncTask = function(task) {
existingTask.type = task.type; existingTask.type = task.type;
} }
} else { } else {
this.log("Queuing up sync task with type:",task.type,"title:",task.title); // this.log("Queuing up sync task with type:",task.type,"title:",task.title);
// If it is not in the queue, insert it // If it is not in the queue, insert it
this.taskQueue[task.title] = task; this.taskQueue[task.title] = task;
} }
// Process the queue // Process the queue
$tw.utils.nextTick(function() {self.processTaskQueue.call(self);}); if(this.syncadaptor) {
$tw.utils.nextTick(function() {self.processTaskQueue.call(self);});
}
}; };
/* /*

View File

@ -52,6 +52,7 @@ NavigatorWidget.prototype.execute = function() {
// Get our parameters // Get our parameters
this.storyTitle = this.getAttribute("story"); this.storyTitle = this.getAttribute("story");
this.historyTitle = this.getAttribute("history"); this.historyTitle = this.getAttribute("history");
this.autosave = this.getAttribute("autosave","yes");
// Construct the child widgets // Construct the child widgets
this.makeChildWidgets(); this.makeChildWidgets();
}; };
@ -61,7 +62,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/ */
NavigatorWidget.prototype.refresh = function(changedTiddlers) { NavigatorWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(); var changedAttributes = this.computeAttributes();
if(changedAttributes.story || changedAttributes.history) { if(changedAttributes.story || changedAttributes.history || changedAttributes.autosave) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
} else { } else {
@ -290,7 +291,9 @@ NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
this.saveStoryList(storyList); this.saveStoryList(storyList);
} }
// Send a notification event // Send a notification event
this.dispatchEvent({type: "tw-auto-save-wiki"}); if(this.autosave === "yes") {
this.dispatchEvent({type: "tw-auto-save-wiki"});
}
} }
} }
} }

View File

@ -877,62 +877,6 @@ exports.renderTiddler = function(outputType,title,options) {
return outputType === "text/html" ? container.innerHTML : container.textContent; return outputType === "text/html" ? container.innerHTML : container.textContent;
}; };
/*
Select the appropriate saver modules and set them up
*/
exports.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));
}
});
// 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
*/
exports.saveWiki = function(options) {
options = options || {};
var method = options.method || "save",
template = options.template || "$:/core/save/all",
downloadType = options.downloadType || "text/plain",
text = this.renderTiddler(downloadType,template),
callback = function(err) {
if(err) {
alert("Error while saving:\n\n" + err);
} else {
$tw.notifier.display("$:/messages/Saved");
}
};
// 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)) {
return true;
}
}
return false;
};
/* /*
Return an array of tiddler titles that match a search string Return an array of tiddler titles that match a search string
text: The text string to search for text: The text string to search for

View File

@ -12,6 +12,11 @@ http://$(userName)$.tiddlyspot.com/backup/
</$set> </$set>
</$reveal> </$reveal>
\end \end
! Saving
|[[Autosave|$:/config/AutoSave]] |{{$:/snippets/autosavestatus}} |
! TiddlySpot ! TiddlySpot
|[[Wiki name|$:/UploadName]] |<$edit-text tiddler="$:/UploadName" default="" tag="input"/> | |[[Wiki name|$:/UploadName]] |<$edit-text tiddler="$:/UploadName" default="" tag="input"/> |

View File

@ -0,0 +1,10 @@
title: $:/snippets/autosavestatus
<$reveal type="match" state="$:/config/AutoSave" text="yes">
Autosave is currently enabled
<$linkcatcher to="$:/config/AutoSave"><$link to="no">Disable</$link></$linkcatcher>
</$reveal>
<$reveal type="nomatch" state="$:/config/AutoSave" text="yes">
Autosave is currently disabled
<$linkcatcher to="$:/config/AutoSave"><$link to="yes">Enable</$link></$linkcatcher>
</$reveal>

View File

@ -0,0 +1,3 @@
title: $:/config/AutoSave
yes

View File

@ -0,0 +1,3 @@
title: $:/config/SyncFilter
[is[tiddler]] -[[$:/HistoryList]] -[[$:/StoryList]] -[[$:/isEncrypted]] -[prefix[$:/status]] -[prefix[$:/state]] -[prefix[$:/temp]]

View File

@ -0,0 +1,11 @@
created: 20140206211715540
modified: 20140206212334833
tags: features
title: AutoSave
type: text/vnd.tiddlywiki
If there is a SaverModule available that supports it, TiddlyWiki will automatically trigger a save of the current document on clicking {{$:/core/images/done-button}} ''tick'' after editing a tiddler.
You should see a yellow notification at the top right of the window to confirm that an automatic save has taken place.
Automatic saving can be enabled or disabled through the ''Saving'' tab of the [[control panel|$:/ControlPanel]]. Behind the scenes, it is controlled through the configuration tiddler [[$:/config/AutoSave]], which must have the value ''yes'' to enable automatic saving.

View File

@ -9,11 +9,6 @@
"tiddlywiki/vanilla", "tiddlywiki/vanilla",
"tiddlywiki/snowwhite" "tiddlywiki/snowwhite"
], ],
"doNotSave": [
"$:/HistoryList",
"$:/status/IsLoggedIn",
"$:/status/UserName"
],
"includeWikis": [ "includeWikis": [
"../tw5.com" "../tw5.com"
] ]

View File

@ -6,8 +6,6 @@
"tiddlywiki/vanilla", "tiddlywiki/vanilla",
"tiddlywiki/snowwhite" "tiddlywiki/snowwhite"
], ],
"doNotSave": [
],
"includeWikis": [ "includeWikis": [
] ]
} }

View File

@ -6,12 +6,6 @@
"tiddlywiki/vanilla", "tiddlywiki/vanilla",
"tiddlywiki/snowwhite" "tiddlywiki/snowwhite"
], ],
"doNotSave": [
"$:/StoryList",
"$:/HistoryList",
"$:/status/IsLoggedIn",
"$:/status/UserName"
],
"includeWikis": [ "includeWikis": [
] ]
} }

View File

@ -6,8 +6,6 @@
"tiddlywiki/vanilla", "tiddlywiki/vanilla",
"tiddlywiki/snowwhite" "tiddlywiki/snowwhite"
], ],
"doNotSave": [
],
"includeWikis": [ "includeWikis": [
] ]
} }

View File

@ -6,8 +6,6 @@
"tiddlywiki/vanilla", "tiddlywiki/vanilla",
"tiddlywiki/snowwhite" "tiddlywiki/snowwhite"
], ],
"doNotSave": [
],
"includeWikis": [ "includeWikis": [
] ]
} }

View File

@ -1,5 +1,5 @@
created: 20131129090249275 created: 20131129090249275
modified: 20140126130155435 modified: 20140206212022007
tags: introduction tags: introduction
title: GettingStarted title: GettingStarted
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki

View File

@ -1,5 +1,5 @@
created: 20140127143652456 created: 20140127143652456
modified: 20140127151028534 modified: 20140206191028534
tags: releasenote tags: releasenote
title: Release 5.0.8-beta title: Release 5.0.8-beta
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -18,7 +18,8 @@ These are changes that might affect users upgrading from previous betas.
!! Improvements !! Improvements
* * Added [[automatic saving|AutoSave]] on editing a tiddler
* Added a warning when attempting to close the window with unsaved changes
!! Bug Fixes !! Bug Fixes

View File

@ -1,5 +1,5 @@
created: 20130825214200000 created: 20130825214200000
modified: 20140128194546203 modified: 20140206174804112
tags: dev tags: dev
title: TiddlyWikiFolders title: TiddlyWikiFolders
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -21,7 +21,6 @@ Only the ''tiddlywiki.info'' file is required, the ''tiddlers'' and ''plugins''
The `tiddlywiki.info` file in a wiki folder contains a JSON object comprising the following fields: The `tiddlywiki.info` file in a wiki folder contains a JSON object comprising the following fields:
* ''plugins'' - an array of plugin names to be included in the wiki * ''plugins'' - an array of plugin names to be included in the wiki
* ''doNotSave'' - an array of tiddler titles that should not be saved by the FileSystemAdaptorPlugin
* ''includeWikis'' - an array of relative paths to external wiki folders to be included in the wiki * ''includeWikis'' - an array of relative paths to external wiki folders to be included in the wiki
For example: For example:
@ -32,11 +31,6 @@ For example:
"tiddlywiki/tiddlyweb", "tiddlywiki/tiddlyweb",
"tiddlywiki/filesystem" "tiddlywiki/filesystem"
], ],
"doNotSave": [
"$:/HistoryList",
"$:/status/IsLoggedIn",
"$:/status/UserName"
],
"includeWikis": [ "includeWikis": [
"../tw5.com" "../tw5.com"
] ]

View File

@ -1,5 +1,5 @@
created: 20130823203800000 created: 20130823203800000
modified: 20140124204012349 modified: 20140206174012349
tags: planning tags: planning
title: RoadMap title: RoadMap
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -12,10 +12,6 @@ During the beta TiddlyWiki will be practical for cautious everyday use but as we
The following additional features are planned or under consideration for implementation during the beta: The following additional features are planned or under consideration for implementation during the beta:
* Features affecting user data integrity
** Warning when attempting to close window without saving
** Use browser local storage to preserve changes in case browser crashes before saving/sync
** Automatic saving for saver modules that can support it
* Features required for large scale adoption * Features required for large scale adoption
** Multilanguage support ** Multilanguage support
** Proper use of ARIA roles ** Proper use of ARIA roles

View File

@ -140,10 +140,6 @@ FileSystemAdaptor.prototype.saveTiddler = function(tiddler,callback) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
if($tw.boot.wikiInfo.doNotSave && $tw.boot.wikiInfo.doNotSave.indexOf(tiddler.fields.title) !== -1) {
// Don't save the tiddler if it is on the blacklist
return callback(null,{},0);
}
if(self.watchers[fileInfo.filepath]) { if(self.watchers[fileInfo.filepath]) {
self.watchers[fileInfo.filepath].close(); self.watchers[fileInfo.filepath].close();
delete self.watchers[fileInfo.filepath]; delete self.watchers[fileInfo.filepath];
@ -197,34 +193,29 @@ FileSystemAdaptor.prototype.deleteTiddler = function(title,callback) {
fileInfo = $tw.boot.files[title]; fileInfo = $tw.boot.files[title];
// Only delete the tiddler if we have writable information for the file // Only delete the tiddler if we have writable information for the file
if(fileInfo) { if(fileInfo) {
if($tw.boot.wikiInfo.doNotSave && $tw.boot.wikiInfo.doNotSave.indexOf(title) !== -1) { if(this.watchers[fileInfo.filepath]) {
// Don't delete the tiddler if it is on the blacklist this.watchers[fileInfo.filepath].close();
callback(null); delete this.watchers[fileInfo.filepath];
} else {
if(this.watchers[fileInfo.filepath]) {
this.watchers[fileInfo.filepath].close();
delete this.watchers[fileInfo.filepath];
}
delete this.pending[fileInfo.filepath];
// Delete the file
fs.unlink(fileInfo.filepath,function(err) {
if(err) {
return callback(err);
}
self.log("Deleted file",fileInfo.filepath);
// Delete the metafile if present
if(fileInfo.hasMetaFile) {
fs.unlink(fileInfo.filepath + ".meta",function(err) {
if(err) {
return callback(err);
}
callback(null);
});
} else {
callback(null);
}
});
} }
delete this.pending[fileInfo.filepath];
// Delete the file
fs.unlink(fileInfo.filepath,function(err) {
if(err) {
return callback(err);
}
self.log("Deleted file",fileInfo.filepath);
// Delete the metafile if present
if(fileInfo.hasMetaFile) {
fs.unlink(fileInfo.filepath + ".meta",function(err) {
if(err) {
return callback(err);
}
callback(null);
});
} else {
callback(null);
}
});
} else { } else {
callback(null); callback(null);
} }