mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-17 23:34:50 +00:00
23fec9e390
* Fix problems with building single-file wiki using external-js template
* core/templates/external-js/tiddlywiki5-external-js.html.tid,
core/templates/external-js/save-all-external-js.tid,
core/templates/external-js/save-offline-external-js.tid
core/templates/external-js/load-external-js.tid:
Fix #5343. Exclude client-server plugins in tiddler imports and to
specify a working URL for loading tiddlywiki5.js from local disk.
Mirror save/all and save/offline templates in the regular server
edition.
Fix #4717 (tiddlywiki5-external-js.html.tid)
* core/modules/saver-handler.js:
Need the change to make single file autosave work with the external-js
template.
* editions/server-external-js/tiddlywiki.info:
Provide external-js related build targets.
* core/language/en-GB/Snippets/ExtJSReadme.tid:
Temporary doc to supplement TW5.com's external-js section. Demonstrate
that upgrade could be done on single-file wikis with an externalized
TW core.
* core/language/en-GB/Snippets/GetTiddlyWikiJS.tid:
Documentation. Meant to be included in every wiki and to help end
users acquire tiddlywiki5.js.
* Pre-configure save-wiki template for end-users
* Remove the newline character at the end of the file.
* Trim "template" value in saveWiki()
* Safeguard the code from extraneous whitespaces in transcluded result.
* Rename and add versioning to downloaded tiddlywiki core JS
* Rename "tiddlywiki5.js" to "twcore-VERSION.js"
* Preload $:/config/SaveWikiButton/Template tiddler with the required
external-js template value.
* Update external-js user documentation
* Add "download tiddlywiki core JS" menu item to the "cloud" button.
* Update build's target defintions associated with external-js template.
* Move the user doc to the tw5.com edition.
* Coding style update
* Undo template name changes
* Correct text & fill colors on some disabled buttons
* Add new "export tiddlywiki core" button under page control tools
This new button can export tiddlywiki's core JS from user's wiki as
long as the wiki is served with the regular "root" template. The
button will be ineffective, thus disabled, if the core has already been
externalized by the "external-js" template.
With this button, a full standalone html wiki can obtain the matching
core JS without TiddlyWiki on node.js. Once this is done, the html wiki
can be converted to using the "external-js" template.
* Alternate version of "save tiddlywiki core for offline use"
This version will fire up a "Save File" dialogue box when clicked,
instead of directing the user to a helper doc for further instruction.
It achieves this by using the "download" attribute of the <a> html tag.
It works on most modern desktop browsers, but older browsers (e.g. IE)
may display the file instead.
* Adjust font-weight to match other menu items
* Merge two user documentations into one
* Add user-browser-cache=yes to --listen command
* Update "export tiddlywiki core" button hint
* Simpler implementation for switching btw online/offline core URL
Shave off one template by using filtered transclusion to control
online/offline core URL.
* Update user doc
Update the user doc to clarify that build index step is not needed to
initialize a new wiki.
* Rename twcore to tiddlywikicore
* Reformat the user doc
* Rework export-tiddlywikicore button
Popup an error message instead of disabling the button when export
core cannot be performed.
* Revert "Correct text & fill colors on some disabled buttons"
This reverts commit e7dbb7e712
.
213 lines
6.4 KiB
JavaScript
213 lines
6.4 KiB
JavaScript
/*\
|
|
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
|
|
dirtyTracking: true if dirty tracking should be performed
|
|
*/
|
|
function SaverHandler(options) {
|
|
var self = this;
|
|
this.wiki = options.wiki;
|
|
this.dirtyTracking = options.dirtyTracking;
|
|
this.preloadDirty = options.preloadDirty || [];
|
|
this.pendingAutoSave = false;
|
|
// Make a logger
|
|
this.logger = new $tw.utils.Logger("saver-handler");
|
|
// Initialise our savers
|
|
if($tw.browser) {
|
|
this.initSavers();
|
|
}
|
|
// Only do dirty tracking if required
|
|
if($tw.browser && this.dirtyTracking) {
|
|
// Compile the dirty tiddler filter
|
|
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
|
|
// Count of changes that have not yet been saved
|
|
var filteredChanges = self.filterFn.call(self.wiki,function(iterator) {
|
|
$tw.utils.each(self.preloadDirty,function(title) {
|
|
var tiddler = self.wiki.getTiddler(title);
|
|
iterator(tiddler,title);
|
|
});
|
|
});
|
|
this.numChanges = filteredChanges.length;
|
|
// Listen out for changes to tiddlers
|
|
this.wiki.addEventListener("change",function(changes) {
|
|
// Filter the changes so that we only count changes to tiddlers that we care about
|
|
var filteredChanges = self.filterFn.call(self.wiki,function(iterator) {
|
|
$tw.utils.each(changes,function(change,title) {
|
|
var tiddler = self.wiki.getTiddler(title);
|
|
iterator(tiddler,title);
|
|
});
|
|
});
|
|
// Adjust the number of changes
|
|
self.numChanges += filteredChanges.length;
|
|
self.updateDirtyStatus();
|
|
// 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
|
|
if(self.numChanges > 0) {
|
|
self.saveWiki({
|
|
method: "autosave",
|
|
downloadType: "text/plain"
|
|
});
|
|
}
|
|
self.pendingAutoSave = false;
|
|
}
|
|
});
|
|
// Listen for the autosave event
|
|
$tw.rootWidget.addEventListener("tm-auto-save-wiki",function(event) {
|
|
// Do the autosave unless there are outstanding tiddler change events
|
|
if(self.wiki.getSizeOfTiddlerEventQueue() === 0) {
|
|
// Check if we're dirty
|
|
if(self.numChanges > 0) {
|
|
self.saveWiki({
|
|
method: "autosave",
|
|
downloadType: "text/plain"
|
|
});
|
|
}
|
|
} else {
|
|
// Otherwise put ourselves in the "pending autosave" state and wait for the change event before we do the autosave
|
|
self.pendingAutoSave = true;
|
|
}
|
|
});
|
|
// Set up our beforeunload handler
|
|
$tw.addUnloadTask(function(event) {
|
|
var confirmationMessage;
|
|
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",
|
|
variables: event.paramObject
|
|
});
|
|
});
|
|
$tw.rootWidget.addEventListener("tm-download-file",function(event) {
|
|
self.saveWiki({
|
|
method: "download",
|
|
template: event.param,
|
|
downloadType: "text/plain",
|
|
variables: event.paramObject
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
SaverHandler.prototype.titleSyncFilter = "$:/config/SaverFilter";
|
|
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", "autosave" 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";
|
|
// Ignore autosave if disabled
|
|
if(method === "autosave" && ($tw.config.disableAutoSave || this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes")) {
|
|
return false;
|
|
}
|
|
var variables = options.variables || {},
|
|
template = (options.template ||
|
|
this.wiki.getTiddlerText("$:/config/SaveWikiButton/Template","$:/core/save/all")).trim(),
|
|
downloadType = options.downloadType || "text/plain",
|
|
text = this.wiki.renderTiddler(downloadType,template,options),
|
|
callback = function(err) {
|
|
if(err) {
|
|
alert($tw.language.getString("Error/WhileSaving") + ":\n\n" + err);
|
|
} else {
|
|
// Clear the task queue if we're saving (rather than downloading)
|
|
if(method !== "download") {
|
|
self.numChanges = 0;
|
|
self.updateDirtyStatus();
|
|
}
|
|
$tw.notifier.display(self.titleSavedNotification);
|
|
if(options.callback) {
|
|
options.callback();
|
|
}
|
|
}
|
|
};
|
|
// 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,{variables: {filename: variables.filename}})) {
|
|
this.logger.log("Saving wiki with method",method,"through saver",saver.info.name);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/*
|
|
Checks whether the wiki is dirty (ie the window shouldn't be closed)
|
|
*/
|
|
SaverHandler.prototype.isDirty = function() {
|
|
return this.numChanges > 0;
|
|
};
|
|
|
|
/*
|
|
Update the document body with the class "tc-dirty" if the wiki has unsaved/unsynced changes
|
|
*/
|
|
SaverHandler.prototype.updateDirtyStatus = function() {
|
|
var self = this;
|
|
if($tw.browser) {
|
|
$tw.utils.toggleClass(document.body,"tc-dirty",this.isDirty());
|
|
$tw.utils.each($tw.windows,function(win) {
|
|
$tw.utils.toggleClass(win.document.body,"tc-dirty",self.isDirty());
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.SaverHandler = SaverHandler;
|
|
|
|
})();
|