[TW5] JS macro: setting list field doesn't yield a change?

85 views
Skip to first unread message

TheDiveO

unread,
Mar 11, 2014, 8:12:31 AM3/11/14
to tiddly...@googlegroups.com
I'm working on a "Sequencer" utility "macro" that runs once when the TW5 gets loaded. Its sole purpose is to look for tiddlers that have been tagged in a particular way ($:/TheDiveO/tags/sequenced) and need to be sequenced into list fields of other tiddlers. I want to use this Sequencer to insert view and edit templates into the list fields of $:/tags/EditTemplate and $:/tags/ViewTemplate in the right places. That's necessary because I want my templates to show up in a certain position with respect to the other edit/view template parts. So, instead of manual editing those template part lists that gets lost when moving my tiddlers over to another TW5 instance, I want to use a Sequencer that does the job and is driven by declarations on the template part tiddlers themselves.

For this, each tiddler that needs to be sequenced into some list does not only need to be tagged (as I wrote above) but also carries to fields "sequence" and "sequence-placement". The former field references the tiddler with the list to be changed (resequenced), while the latter declares where to put this tidder into the sequence.

So far, so good. But now I'm stuck at a totally unexpected place: I successfully retrieve the list to be manipulated, parse it into an array, then resequence it and convert it back into a string:

listtiddler.fields.list = $tw.utils.stringifyList(list);

Here, "listtiddler" references the tiddler instance whose list field is to be resequenced. "list" contains the new list string. When I immediately check the listtiddler.fields.list then it correctly contains the new value.

But when I look at the tiddler by opening it into the story view, then the new value is lost and it still contains the old value.

So what I am doing wrong here?

Jeremy Ruston

unread,
Mar 11, 2014, 8:29:20 AM3/11/14
to TiddlyWikiDev
Hi TheDiveO

So what I am doing wrong here?

The problem is that the $tw.Tiddler object is immutable; you can't reach into it and change the value of fields. If you want to change the value of a tiddler the correct way is to add a new tiddler that overwrites it. (I have been meaning to explore using getters for tiddler fields so that attempting to write to one will raise an error).

There's a second problem though: macros mustn't have side effects beyond returning their resulting text. If you want to modify stuff you would normally write a widget, or conceivably a startup module.

Anyhow, I think a neater way to do this might be to add support for a new field "list-before" (and possibly "list-after"). When we compute the list order for the tiddlers with a particular tag we'd start by positioning the tiddlers specified in the list field of the tag (as we do now). At the moment, any remaining tiddlers are just appended to the end of the list. But we could use the "list-before"/"list-after" fields to position them relative to other tiddlers already in the list.

The code in question is here:


Best wishes

Jeremy









--
You received this message because you are subscribed to the Google Groups "TiddlyWikiDev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywikide...@googlegroups.com.
To post to this group, send email to tiddly...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywikidev.
For more options, visit https://groups.google.com/d/optout.



--
Jeremy Ruston
mailto:jeremy...@gmail.com

TheDiveO

unread,
Mar 11, 2014, 8:42:47 AM3/11/14
to tiddly...@googlegroups.com, jeremy...@gmail.com
Jeremy, thank you for your valuable feedback!


On Tuesday, March 11, 2014 1:29:20 PM UTC+1, Jeremy Ruston wrote:
Hi TheDiveO

So what I am doing wrong here?

The problem is that the $tw.Tiddler object is immutable; you can't reach into it and change the value of fields. If you want to change the value of a tiddler the correct way is to add a new tiddler that overwrites it. (I have been meaning to explore using getters for tiddler fields so that attempting to write to one will raise an error).

After posting it occured to me that I'm trying to modify a shadow object. I understand that I can check for a shadow tiddler using $tw.wiki.isShadowTiddler(title). If I tripped on one, how do I clone it then?
 
There's a second problem though: macros mustn't have side effects beyond returning their resulting text. If you want to modify stuff you would normally write a widget, or conceivably a startup module.

Actually, I'm not really defining a macro with side effect; you're perfectly right on spot that doing so is a really bad practise. What I meant is doing a startup module. At the moment I've packed this into a tiddler of type "application/javascript" with module-type "macro". I suspect the latter setting isn't the correct way to do this? What module-type should I use instead?
 
Anyhow, I think a neater way to do this might be to add support for a new field "list-before" (and possibly "list-after"). When we compute the list order for the tiddlers with a particular tag we'd start by positioning the tiddlers specified in the list field of the tag (as we do now). At the moment, any remaining tiddlers are just appended to the end of the list. But we could use the "list-before"/"list-after" fields to position them relative to other tiddlers already in the list.

Doesn't this still lack the indication of which list to modify? My idea is to have a tiddler tagged as needing processing and then I need to specify which list is going to be affected. So I still need a field for referencing the tiddler and its list, such as "list-in: $:/tags/EditTemplate". Then, I can add either a "list-after: ..." or "list-before: ...". That's a good idea to use the fields instead of the operator syntax.
 

Jeremy Ruston

unread,
Mar 11, 2014, 8:48:43 AM3/11/14
to TheDiveO, TiddlyWikiDev
Hi TheDiveO

After posting it occured to me that I'm trying to modify a shadow object. I understand that I can check for a shadow tiddler using $tw.wiki.isShadowTiddler(title). If I tripped on one, how do I clone it then?

It makes no difference; you can call wiki.addTiddler() to replace a shadow tiddler with an ordinary tiddler.
 

Actually, I'm not really defining a macro with side effect; you're perfectly right on spot that doing so is a really bad practise. What I meant is doing a startup module. At the moment I've packed this into a tiddler of type "application/javascript" with module-type "macro". I suspect the latter setting isn't the correct way to do this? What module-type should I use instead?

You can put it in a "browser-startup" module but that would mean that it would only execute in the browser.
 
Anyhow, I think a neater way to do this might be to add support for a new field "list-before" (and possibly "list-after"). When we compute the list order for the tiddlers with a particular tag we'd start by positioning the tiddlers specified in the list field of the tag (as we do now). At the moment, any remaining tiddlers are just appended to the end of the list. But we could use the "list-before"/"list-after" fields to position them relative to other tiddlers already in the list.

Doesn't this still lack the indication of which list to modify?

Under the approach I'm suggesting we wouldn't need to actually modify the list field. We'd be dynamically placing the leftover tiddlers when wiki.sortByList() is called.

Anyhow, sortByList is passed the list of tiddlers that have the specified tag, and the value of the tag itself. So there's enough information to get the right list.

Best wishes

Jeremy

TheDiveO

unread,
Mar 11, 2014, 9:13:51 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com
Hi Jeremy,


On Tuesday, March 11, 2014 1:48:43 PM UTC+1, Jeremy Ruston wrote:
Hi TheDiveO

After posting it occured to me that I'm trying to modify a shadow object. I understand that I can check for a shadow tiddler using $tw.wiki.isShadowTiddler(title). If I tripped on one, how do I clone it then?

It makes no difference; you can call wiki.addTiddler() to replace a shadow tiddler with an ordinary tiddler.

Would be $tw.wiki.addTiddler(new $tw.Tiddler(listtiddler)) a correct way to clone a tiddler? I'm still not accustomed to TW hacking, so please bear with me asking seemingly simple and stupid questions.

You can put it in a "browser-startup" module but that would mean that it would only execute in the browser.

Hmmm ... that wouldn't be exactly what we need, would it? When I'm making the tiddler of module-type: library, then my script crashes because there is (yet?) no getTiddlersWithTag() defined by the time my library gets called. In contrast, macros get instantiated only later when all things are in place. Is there something missing in between "library" and "macros" or is it just me not understanding the idea behind this sequence and modularization (which I suspect)?
 
 Under the approach I'm suggesting we wouldn't need to actually modify the list field. We'd be dynamically placing the leftover tiddlers when wiki.sortByList() is called.

Ahh ... I'm beginning to see where this should lead to. I have to admit that before this explanation I didn't understood why you are mentioning wiki.sortByList(). But now, it really makes sense. Improving sortByList would avoid having to run my "fixing" module and would never need to modify the lists permanently, thus avoiding any cloning of shadow tiddlers and thus changing the overall persistent state. That surely is the right way to do...

Jeremy Ruston

unread,
Mar 11, 2014, 9:25:30 AM3/11/14
to TheDiveO, TiddlyWikiDev
Hi TheDiveO
 
Would be $tw.wiki.addTiddler(new $tw.Tiddler(listtiddler)) a correct way to clone a tiddler? I'm still not accustomed to TW hacking, so please bear with me asking seemingly simple and stupid questions.

Yes that's right.
 
You can put it in a "browser-startup" module but that would mean that it would only execute in the browser.

Hmmm ... that wouldn't be exactly what we need, would it? When I'm making the tiddler of module-type: library, then my script crashes because there is (yet?) no getTiddlersWithTag() defined by the time my library gets called. In contrast, macros get instantiated only later when all things are in place. Is there something missing in between "library" and "macros" or is it just me not understanding the idea behind this sequence and modularization (which I suspect)?

The problem is just that I haven't implemented any other startup hooks because they haven't yet be needed. But the plan would be to have a few more module types that are called at specified points in the startup sequence.
 
 Under the approach I'm suggesting we wouldn't need to actually modify the list field. We'd be dynamically placing the leftover tiddlers when wiki.sortByList() is called.

Ahh ... I'm beginning to see where this should lead to. I have to admit that before this explanation I didn't understood why you are mentioning wiki.sortByList(). But now, it really makes sense. Improving sortByList would avoid having to run my "fixing" module and would never need to modify the lists permanently, thus avoiding any cloning of shadow tiddlers and thus changing the overall persistent state. That surely is the right way to do...

Pull requests welcome :)

Best wishes

Jeremy

TheDiveO

unread,
Mar 11, 2014, 9:32:19 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com
On Tuesday, March 11, 2014 2:25:30 PM UTC+1, Jeremy Ruston wrote:
Pull requests welcome :)

Oh, damn. I'm so much better at Nag requests... :)  I'll try to implement the idea and then we'll ... I don't have a git account yet. All I intented was to simply use TW5. Look what's happening right now...!

TheDiveO

unread,
Mar 11, 2014, 10:15:35 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com
Is there a way to hack just the sortByLis() function by supplying my own wikimethod tiddler? I've created a tiddler named "$:/TheDiveO/modules/sortByList.js" of type "application/javascript" and module-type "wikimethod".

In it, I'm doing this:

(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/*
Sorts an array of tiddler titles according to an ordered list
*/
exports.sortByList = function(array,listTitle) {
...
}; })();

It gets loaded and I see the module getting executed. However, it doesn't seem to replace the (existing) sortByList() function. Is this possible at all or am I doing something wrong here?

Jeremy Ruston

unread,
Mar 11, 2014, 10:25:42 AM3/11/14
to TheDiveO, TiddlyWikiDev
Hi TheDiveO

> odule getting executed. However, it doesn't seem to replace the (existing) sortByList() function. Is this possible at all or am I doing something wrong here?

There's no way at the moment to control the load order of "wikimethod" modules, so yours is probably being loaded before the core wiki.js.

If you're working under Node.js the normal way to do all of this would be to fork the repo and then modify your copy of wiki.js. Doing it that way makes it easy to then create a pull request.

If you're working in the browser things aren't so simple. You'd probably be best off cloning $:/core/modules/wiki.js and modifying your copy. To make a pull request you'd clone the repo and then paste your modified wiki.js directly on github.com.

If all of this is sounding horrendously complex, don't panic! Pull requests can wait.

Best wishes

Jeremy






Stephan Hradek

unread,
Mar 11, 2014, 10:30:39 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com


Am Dienstag, 11. März 2014 15:25:42 UTC+1 schrieb Jeremy Ruston:
Hi TheDiveO
If you're working in the browser things aren't so simple. You'd probably be best off cloning $:/core/modules/wiki.js and modifying your copy. To make a pull request you'd clone the repo and then paste your modified wiki.js directly on github.com.

I did it that way that I cloned your repo on github and then cloned that on my local harddrive.

Then I did the changes either in the browser and copied them to my local repo or I edited in jEdit my local copy and pasted the changed stuff to the tiddlers in the browser.

When everything worked, I pushed the changes to my repo on github and created a pull request there.

TheDiveO

unread,
Mar 11, 2014, 10:31:30 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com
On Tuesday, March 11, 2014 3:25:42 PM UTC+1, Jeremy Ruston wrote:
If all of this is sounding horrendously complex, don't panic! Pull requests can wait.

Nah. Doesn't sound too complex. I'm just slowly trying to find my way. I didn't thought of the loading sequence, so this might be the culprit here. I hoped to be able to simplify my hacking by isolating things to a small sortByList.js module.

TheDiveO

unread,
Mar 11, 2014, 10:45:35 AM3/11/14
to tiddly...@googlegroups.com, TheDiveO, jeremy...@gmail.com
Hi Jeremy,

I did hack my core module wiki.js and it works. I'm simply amazed over and over again how well thought-out TW5 has become. I've implemented the "list-before" and "list-after" fields. I also tried to make my implementation error-resilent in that it silently ignores inconsistent settings. Due to its implementation, "list-before" will take precedence over "list-after" in case both fields should be present. Any comments welcome, there's so much to learn for me in therms of TW5 hacking. I tried to cling to your coding style, at least to what I see in wiki.js.

exports.sortByList = function(array,listTitle) {
    var list = this.getTiddlerList(listTitle);
    if(!array || array.length === 0) {
        return [];
    } else if(list) {
        var titles = [], t, title;
        // First place any entries that are present in the list
        for(t=0; t<list.length; t++) {
            title = list[t];
            if(array.indexOf(title) !== -1) {
                titles.push(title);
            }
        }
        // Then place any remaining entries
        for(t=0; t<array.length; t++) {
            title = array[t];
            if(list.indexOf(title) === -1) {
                // Entry isn't in the list yet, so either append or insert;
                // obey list-before and list-after if present for relative insertion point.
                var tiddler = this.getTiddler(title);
                var pos = -1;
                if(tiddler) {
                    var beforeTitle = tiddler.fields["list-before"];
                    if(beforeTitle && beforeTitle !== "") {
                        pos = list.indexOf(beforeTitle);
                    } else {
                        var afterTitle = tiddler.fields["list-after"];
                        if(afterTitle && afterTitle !== "") {
                            pos = list.indexOf(afterTitle);
                            if(pos >= 0) {
                                ++pos;
                            }
                        }
                    }
                }
                if(pos >= 0) {
                    titles.splice(pos, 0, title);
                } else {
                    titles.push(title);
                }
            }
        }
        return titles;
    } else {
        return array;
    }
};

Thank you for all your help!

Jeremy Ruston

unread,
Mar 12, 2014, 10:21:50 AM3/12/14
to TheDiveO, TiddlyWikiDev
Hi TheDiveO

I've committed a slightly modified version of your contribution. I've updated the list-before handling so that specifying an empty string causes the tiddler to be added to the top of the list. I also added documentation and tests (which always takes much longer than the code, sadly!)

https://github.com/Jermolene/TiddlyWiki5/commit/0d18f3cc5d242e2c993c5917b8fbb34fe9fac211

Sorry for the admin, but would you able to sign the CLA as described here:


Many thanks,

Jeremy.



TheDiveO

unread,
Mar 12, 2014, 3:52:28 PM3/12/14
to tiddly...@googlegroups.com
No need to apologise for ensuring correct administration. You've a pull request for the CLA. Never thought I would ever be able to contribute anything to TW... :)

Jeremy Ruston

unread,
Mar 12, 2014, 4:07:23 PM3/12/14
to TiddlyWikiDev
Hi TheDiveO

Thanks for doing the CLA.

Never thought I would ever be able to contribute anything to TW... :)

The more the merrier. Accepting contributions is a pleasure.

Best wishes

Jeremy




On Wed, Mar 12, 2014 at 7:52 PM, TheDiveO <harald....@gmx.net> wrote:
No need to apologise for ensuring correct administration. You've a pull request for the CLA. Never thought I would ever be able to contribute anything to TW... :)
--
You received this message because you are subscribed to the Google Groups "TiddlyWikiDev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywikide...@googlegroups.com.
To post to this group, send email to tiddly...@googlegroups.com.
Visit this group at http://groups.google.com/group/tiddlywikidev.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages