I've taken a crack at modifying the refresh mechanism slightly
(/very/ slightly) to better accommodate tiddlers whose display depends
on the contents of other tiddlers. For example, when tiddler A
includes tiddler B via the <<tiddler>> macro, and tiddler B's contents
are changed, you'd really like tiddler A to be redisplayed.
(This is especially important for those of us working on plugins that
make automatic changes to tiddlers based on the user clicking on
elements that are rendered as part of the display. When tiddler B
contains a call to my <<task>> macro [1], and the user double-clicks on
the task's bullet to mark the task complete, tiddler B must be
redisplayed regardless of whether it is being displayed on its own or
within tiddler A. Otherwise, the automatic changes we make can corrupt
the tiddler, because they are operating on an outdated idea of the
tiddler's contents.)
I've included the code below, so you can see exactly what I'm
proposing. But first let me explain it in words.
The bulk of the change is simply to extend the set of standard
refreshers with a new one, called fullContent. The idea is that the
view template's viewer div will add refresh="fullContent" so that this
refresher will be called whenever a refresh is propagating through the
DOM. This refresher checks a list of tiddler titles against the
changed tiddler -- the list of tiddlers this one's display depends on
-- and if any match it forces a full redisplay of the tiddler.
The slight change to the mechanism is simply to have the refreshers
return a boolean. True means that the refresher has handled the
element under consideration, and there is no need to recurse. False
means that recursion should continue. (Currently just having a refresh
attribute stops the recursion, which would mean that no links were ever
updated if the viewer div had this new refresh="fullContent"
attribute.)
The next question is where the list of dependent tiddlers comes from.
For the purposes of my plugin, as shown in the code below, I have
hijacked the wikify function, and if a tiddler is being wikified in
full then it is added to the list associated with the output element.
The list is added as a javascript property of the nearest DOM element
ancestor that contains refresh="fullContent". I think this is the best
approach for a plugin, but for the core you might want to take a more
explicit approach, by calling addDisplayDependency from within the
various macros that do add display dependencies, ie the view, tiddler,
slider, and tab macros. The virtue of doing it the way I did it is
that the common case of full inclusion automatically establishes the
display dependency.
Jeremy, I'm sure you're up to your eyebrows with the upcoming 2.1
release. So I'm not lobbying for adding display dependencies to 2.1.
But I hope you'll consider adding this to some future release.
Cheers,
Luke
---
Here's a sample view template (the stock 2.0.10 view template, plus
refresh='fullContent' added to the viewer div):
<div class='toolbar' macro='toolbar -closeTiddler closeOthers
+editTiddler permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span
macro='view modified date [[DD MMM YYYY]]'></span> (created <span
macro='view created date [[DD MMM YYYY]]'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'
refresh='fullContent'></div>
<div class='tagClear'></div>
And here is the code. If you add this as a systemConfig tiddler to a
stock 2.0.10 TW, you can see it in action. Modify ViewTemplate as
above. Then try creating TiddlerB, then create TiddlerA that contains
<<tiddler TiddlerB>>, and then edit TiddlerB and watch TiddlerA's
display get updated as well.
//{{{
window.wikify_orig_TaskMacroPlugin = window.wikify
window.wikify = function(source,output,highlightRegExp,tiddler)
{
if ( tiddler && tiddler.text === source )
addDisplayDependency( output, tiddler.title )
wikify_orig_TaskMacroPlugin.apply( this, arguments )
}
config.refreshers_orig_TaskMacroPlugin = config.refreshers
config.refreshers = {
link: function() {
config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
return true
},
content: function() {
config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments
)
return true
},
fullContent: function( e, changeList ) {
var tiddlers = e.tiddlers
if ( changeList == null || tiddlers == null )
return false
for ( var i=0; i < tiddlers.length; ++i )
if ( changeList.find(tiddlers[i]) != null ) {
var title = tiddlers[0]
story.refreshTiddler( title, null, true )
return true
}
return false
}
}
function refreshElements(root,changeList)
{
var nodes = root.childNodes;
for(var c=0; c<nodes.length; c++)
{
var e = nodes[c],type;
if(e.getAttribute)
type = e.getAttribute("refresh");
else
type = null;
var refresher = config.refreshers[type];
if ( ! refresher || ! refresher(e, changeList) )
{
if(e.hasChildNodes())
refreshElements(e,changeList);
}
}
}
// Add the tiddler whose title is given to the list of tiddlers whose
// changing will cause a refresh of the tiddler containing the given
element.
function addDisplayDependency( element, title ) {
while ( element && element.getAttribute ) {
if ( element.getAttribute("refresh") == "fullContent" ) {
var list = element.tiddlers
if ( list == null )
element.tiddlers = [title]
else
list.pushUnique( title )
return
}
element = element.parentNode
}
}
//}}}
------
[1] See http://labwiki.sourceforge.net for the TaskMacroPlugin. The
code given above is not yet part of the released version of the plugin.
//{{{
window.wikify_orig_TaskMacroPlugin = window.wikify
window.wikify = function(source,output,highlightRegExp,tiddler)
{
if ( tiddler && tiddler.text === source )
addDisplayDependency( output, tiddler.title )
wikify_orig_TaskMacroPlugin.apply( this, arguments )
}
config.refreshers_orig_TaskMacroPlugin = config.refreshers
config.refreshers = {
link: function() {
config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
return true
},
content: function() {
config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments
)
return true
},
fullContent: function( e, changeList ) {
var tiddlers = e.refreshTiddlers
var idAttr = element.getAttribute("id"), tiddlerAttr =
element.getAttribute("tiddler")
if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr )
{
var list = element.refreshTiddlers
if ( list == null ) {
list = [tiddlerAttr]
element.refreshTiddlers = list
element.setAttribute( "refresh", "fullContent" )
Here's an update that doesn't require changing the view template.
//{{{
window.wikify_orig_TaskMacroPlugin = window.wikify
window.wikify = function(source,output,highlightRegExp,tiddler)
{
if ( tiddler && tiddler.text === source )
addDisplayDependency( output, tiddler.title )
wikify_orig_TaskMacroPlugin.apply( this, arguments )
}
config.refreshers_orig_TaskMacroPlugin = config.refreshers
config.refreshers = {
link: function() {
config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
return true
},
content: function() {
config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments
)
return true
},
fullContent: function( e, changeList ) {
var tiddlers = e.refreshTiddlers
if ( changeList == null || tiddlers == null )
return false
for ( var i=0; i < tiddlers.length; ++i )
if ( changeList.find (tiddlers[i]) != null ) {