1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-20 08:44:52 +00:00
TiddlyWiki5/core/modules/widgets/navigator.js

387 lines
12 KiB
JavaScript
Executable File

/*\
title: $:/core/modules/widgets/navigator.js
type: application/javascript
module-type: widget
Navigator widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var NavigatorWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
this.addEventListeners([
{type: "tw-navigate", handler: "handleNavigateEvent"},
{type: "tw-edit-tiddler", handler: "handleEditTiddlerEvent"},
{type: "tw-delete-tiddler", handler: "handleDeleteTiddlerEvent"},
{type: "tw-save-tiddler", handler: "handleSaveTiddlerEvent"},
{type: "tw-cancel-tiddler", handler: "handleCancelTiddlerEvent"},
{type: "tw-close-tiddler", handler: "handleCloseTiddlerEvent"},
{type: "tw-close-all-tiddlers", handler: "handleCloseAllTiddlersEvent"},
{type: "tw-close-other-tiddlers", handler: "handleCloseOtherTiddlersEvent"},
{type: "tw-new-tiddler", handler: "handleNewTiddlerEvent"},
{type: "tw-import-tiddlers", handler: "handleImportTiddlersEvent"},
]);
};
/*
Inherit from the base widget class
*/
NavigatorWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
NavigatorWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
NavigatorWidget.prototype.execute = function() {
// Get our parameters
this.storyTitle = this.getAttribute("story");
this.historyTitle = this.getAttribute("history");
// Construct the child widgets
this.makeChildWidgets();
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
NavigatorWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.story || changedAttributes.history) {
this.refreshSelf();
return true;
} else {
return this.refreshChildren(changedTiddlers);
}
};
NavigatorWidget.prototype.getStoryList = function() {
this.storyList = this.wiki.getTiddlerList(this.storyTitle);
};
NavigatorWidget.prototype.saveStoryList = function() {
var storyTiddler = this.wiki.getTiddler(this.storyTitle);
this.wiki.addTiddler(new $tw.Tiddler({
title: this.storyTitle
},storyTiddler,{list: this.storyList}));
};
NavigatorWidget.prototype.findTitleInStory = function(title,defaultIndex) {
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === title) {
return t;
}
}
return defaultIndex;
};
/*
Handle a tw-navigate event
*/
NavigatorWidget.prototype.handleNavigateEvent = function(event) {
if(this.storyTitle) {
// Update the story tiddler if specified
this.getStoryList();
// See if the tiddler is already there
var slot = this.findTitleInStory(event.navigateTo,-1);
// If not we need to add it
if(slot === -1) {
// First we try to find the position of the story element we navigated from
slot = this.findTitleInStory(event.navigateFromTitle,-1) + 1;
// Add the tiddler
this.storyList.splice(slot,0,event.navigateTo);
// Save the story
this.saveStoryList();
}
}
// Add a new record to the top of the history stack
if(this.historyTitle) {
var historyList = this.wiki.getTiddlerData(this.historyTitle,[]);
historyList.push({title: event.navigateTo, fromPageRect: event.navigateFromClientRect});
this.wiki.setTiddlerData(this.historyTitle,historyList);
}
return false;
};
// Close a specified tiddler
NavigatorWidget.prototype.handleCloseTiddlerEvent = function(event) {
this.getStoryList();
// Look for tiddlers with this title to close
var slot = this.findTitleInStory(event.tiddlerTitle,-1);
if(slot !== -1) {
this.storyList.splice(slot,1);
this.saveStoryList();
}
return false;
};
// Close all tiddlers
NavigatorWidget.prototype.handleCloseAllTiddlersEvent = function(event) {
this.storyList = [];
this.saveStoryList();
return false;
};
// Close other tiddlers
NavigatorWidget.prototype.handleCloseOtherTiddlersEvent = function(event) {
this.storyList = [event.tiddlerTitle];
this.saveStoryList();
return false;
};
// Place a tiddler in edit mode
NavigatorWidget.prototype.handleEditTiddlerEvent = function(event) {
this.getStoryList();
// Replace the specified tiddler with a draft in edit mode
var draftTiddler = this.getDraftTiddler(event.tiddlerTitle),
gotOne = false;
for(var t=this.storyList.length-1; t>=0; t--) {
// Replace the first story instance of the original tiddler name with the draft title
if(this.storyList[t] === event.tiddlerTitle) {
if(!gotOne) {
this.storyList[t] = draftTiddler.fields.title;
gotOne = true;
} else {
this.storyList.splice(t,1);
}
} else if(this.storyList[t] === draftTiddler.fields.title) {
// Remove any existing references to the draft
this.storyList.splice(t,1);
}
}
this.saveStoryList();
return false;
};
// Delete a tiddler
NavigatorWidget.prototype.handleDeleteTiddlerEvent = function(event) {
// Get the tiddler we're deleting
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
// Check if the tiddler we're deleting is in draft mode
if(tiddler.hasField("draft.title")) {
// Delete the original tiddler
this.wiki.deleteTiddler(tiddler.fields["draft.of"]);
}
// Delete this tiddler
this.wiki.deleteTiddler(event.tiddlerTitle);
// Remove the closed tiddler from the story
this.getStoryList();
// Look for tiddler with this title to close
var slot = this.findTitleInStory(event.tiddlerTitle,-1);
if(slot !== -1) {
this.storyList.splice(slot,1);
this.saveStoryList();
}
return false;
};
/*
Create/reuse the draft tiddler for a given title
*/
NavigatorWidget.prototype.getDraftTiddler = function(targetTitle) {
// See if there is already a draft tiddler for this tiddler
var drafts = [];
this.wiki.forEachTiddler(function(title,tiddler) {
if(tiddler.fields["draft.title"] && tiddler.fields["draft.of"] === targetTitle) {
drafts.push(tiddler);
}
});
if(drafts.length > 0) {
return drafts[0];
}
// Get the current value of the tiddler we're editing
var tiddler = this.wiki.getTiddler(targetTitle),
draftTitle = this.generateDraftTitle(targetTitle);
// Save the initial value of the draft tiddler
var draftTiddler = new $tw.Tiddler(
{text: "Type the text for the tiddler '" + targetTitle + "'"},
tiddler,
{
title: draftTitle,
"draft.title": targetTitle,
"draft.of": targetTitle
},
this.wiki.getModificationFields()
);
this.wiki.addTiddler(draftTiddler);
return draftTiddler;
};
/*
Generate a title for the draft of a given tiddler
*/
NavigatorWidget.prototype.generateDraftTitle = function(title) {
var c = 0;
do {
var draftTitle = "Draft " + (c ? (c + 1) + " " : "") + "of '" + title + "'";
c++;
} while(this.wiki.tiddlerExists(draftTitle));
return draftTitle;
};
// Take a tiddler out of edit mode, saving the changes
NavigatorWidget.prototype.handleSaveTiddlerEvent = function(event) {
this.getStoryList();
var storyTiddlerModified = false; // We have to special case saving the story tiddler itself
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === event.tiddlerTitle) {
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
if(tiddler) {
var draftTitle = (tiddler.fields["draft.title"] || "").trim(),
draftOf = (tiddler.fields["draft.of"] || "").trim();
if(draftTitle) {
var isRename = draftOf !== draftTitle,
isConfirmed = true;
if(isRename && this.wiki.tiddlerExists(draftTitle)) {
isConfirmed = confirm("Do you wish to overwrite the tiddler '" + draftTitle + "'?");
}
if(isConfirmed) {
// Save the draft tiddler as the real tiddler
this.wiki.addTiddler(new $tw.Tiddler(this.wiki.getCreationFields(),tiddler,{
title: draftTitle,
"draft.title": undefined,
"draft.of": undefined
},this.wiki.getModificationFields()));
// Remove the draft tiddler
this.wiki.deleteTiddler(event.tiddlerTitle);
// Remove the original tiddler if we're renaming it
if(isRename) {
this.wiki.deleteTiddler(draftOf);
}
// Make the story record point to the newly saved tiddler
this.storyList[t] = draftTitle;
// Check if we're modifying the story tiddler itself
if(draftTitle === this.storyTitle) {
storyTiddlerModified = true;
}
}
}
}
}
}
if(!storyTiddlerModified) {
this.saveStoryList();
}
return false;
};
// Take a tiddler out of edit mode without saving the changes
NavigatorWidget.prototype.handleCancelTiddlerEvent = function(event) {
this.getStoryList();
var storyTiddlerModified = false;
for(var t=0; t<this.storyList.length; t++) {
if(this.storyList[t] === event.tiddlerTitle) {
var tiddler = this.wiki.getTiddler(event.tiddlerTitle);
if(tiddler && tiddler.hasField("draft.title")) {
// Remove the draft tiddler
this.wiki.deleteTiddler(event.tiddlerTitle);
// Make the story record point to the original tiddler
this.storyList[t] = tiddler.fields["draft.title"];
// Check if we're modifying the story tiddler itself
if(tiddler.fields["draft.title"] === this.storyTitle) {
storyTiddlerModified = true;
}
}
}
}
if(!storyTiddlerModified) {
this.saveStoryList();
}
return false;
};
// Create a new draft tiddler
NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
// Get the story details
this.getStoryList();
// Get the template tiddler if there is one
var templateTiddler = this.wiki.getTiddler(event.param);
// Create the new tiddler
var baseTitle = (templateTiddler && templateTiddler.fields.title) || "New Tiddler",
title;
for(var t=0; true; t++) {
title = baseTitle + (t ? " " + t : "");
if(!this.wiki.tiddlerExists(title)) {
break;
}
}
var tiddler = new $tw.Tiddler(this.wiki.getCreationFields(),{
text: "Newly created tiddler",
title: title
},this.wiki.getModificationFields());
this.wiki.addTiddler(tiddler);
// Create the draft tiddler
var draftTitle = this.generateDraftTitle(title),
draftTiddler = new $tw.Tiddler({
text: "Type the text for the new tiddler"
},templateTiddler,{
title: draftTitle,
"draft.title": title,
"draft.of": title
},this.wiki.getModificationFields());
this.wiki.addTiddler(draftTiddler);
// Update the story to insert the new draft at the top
var slot = this.findTitleInStory(event.navigateFromTitle,-1) + 1;
this.storyList.splice(slot,0,draftTitle);
// Save the updated story
this.saveStoryList();
// Add a new record to the top of the history stack
var history = this.wiki.getTiddlerData(this.historyTitle,[]);
history.push({title: draftTitle});
this.wiki.setTiddlerData(this.historyTitle,history);
return false;
};
// Import JSON tiddlers
NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
var self = this;
// Get the story and history details
this.getStoryList();
var history = this.wiki.getTiddlerData(this.historyTitle,[]);
// Get the tiddlers
var tiddlers = [];
try {
tiddlers = JSON.parse(event.param);
} catch(e) {
}
// Process each tiddler
$tw.utils.each(tiddlers,function(tiddlerFields) {
// Generate a unique title for the tiddler
var title = self.wiki.generateNewTitle(tiddlerFields.title);
// Add it to the store
self.wiki.addTiddler(new $tw.Tiddler(
self.wiki.getCreationFields(),
self.wiki.getModificationFields(),
tiddlerFields,
{title: title}
));
// Add it to the story
if(self.storyList.indexOf(title) === -1) {
self.storyList.unshift(title);
}
// And to history
history.push({title: title});
});
// Save the updated story and history
this.saveStoryList();
this.wiki.setTiddlerData(this.historyTitle,history);
return false;
};
exports.navigator = NavigatorWidget;
})();