Can I filter all the tree of tags under a root element as JSON?

I know that I can create a list of all the tags and Tiddlers under the current Tiddler as the following:

<div class="tc-table-of-contents">
    <$macrocall $name="toc" tag=<<currentTiddler>> />
</div>

I also know that I can send parameters to an iframe:

<iframe 
    width="100%"
    height="800"
    src='http://mypage.com/tiddlywiki?tagTree={"rootTag":{"childTag":["tiddler1","tiddler2","tiddler3"],"childTag2":["tiddler1","tiddler2","tiddler3"],"childTag3":["tiddler1","tiddler2","tiddler3"]}}'
    allowfullscreen="allowfullscreen"
    allowpaymentrequest
    frameborder="0">
</iframe>

The JSON here has the following structure:

{
  "rootTag": {
    "childTag": [
      "tiddler1",
      "tiddler2",
      "tiddler3"
    ],
    "childTag2": [
      "tiddler1",
      "tiddler2",
      "tiddler3"
    ],
    "childTag3": [
      "tiddler1",
      "tiddler2",
      "tiddler3"
    ]
  }
}

With this iframe structure, I’m sending the JSON declared in the tagTree variable to the page http://mypage.com/tiddlywiki… This way I can receive the data inside the iframe and build a visualization tool with Javascript. My doubt is if it’s possible to convert the first command used with toc to JSON, just so I can put it on my iframe. A pseudocode of what I have in mind would be:

<iframe 
    width="100%"
    height="800"
    src='http://mypage.com/tiddlywiki?tagTree='tocToJSON(<div class="tc-table-of-contents"> <$macrocall $name="toc" tag=<<currentTiddler>> /> </div>)'
    allowfullscreen="allowfullscreen"
    allowpaymentrequest
    frameborder="0">
</iframe>

Is there any easy way of doing something like that? My goal here is to build some alternative visualization perspectives of my tree of Tiddlers on a Javascript page…


Edit

After researching for a while I found this jsontiddlers Macro https://tiddlywiki.com/#jsontiddlers%20Macro on the documentation… It works fine when I’m using filters:

<$macrocall $name="jsontiddlers" filter=[prefix[Hello]]	 $output="text/raw"/>

The result here is like I expected on my original question… However, I’m not sure how I can filter a list of Tiddlers just like toc does. Or if instead of using the filter I should be trying to use <div class="tc-table-of-contents"><$macrocall $name="toc" tag=<<currentTiddler>> /></div> as a parameter o the jsontiddlers macro. Can I filter a JSON that will follow the structure of my tags just like toc does?

@Rafael a few notes that may help you, Although I am not sure of your overall idea, I look forward to seeing what you come up with.

I think the quickest solution to what I perceive is what you are after, is getting a list of tiddlers in a TOC hierarchy is to make use of the Kin Filter

Related information

  • the Toc macros have a specific function and they are great in so far as they build a hierarchy from the the tiddlers tagging other tiddlers, as created with the new here button. The TOC macros use a recursive process to return the whole tree, perhaps you could say drilling down into the hierarchy.
  • Also in Tiddlywiki the jsontiddlers are a way to store tiddlers (not a standard JSON) and this is effectively a flat list of tiddlers, however within that flat list of course each of the tiddlers can have tags and fields that describe a more complex relationship between the tiddlers.
  • Data Tiddlers can be JSON and can also contain multiple field/value pairs.
  • The “JSON mangler plugin” lets you read and manipulate JSON tiddlers more as fully featured JSON and a handful of other tools exist for this.
  • The Kin filter can iterate a list of tiddlers from a hierarchy rather than trying to use the TOC macro which is possibly not fit for the function you want.
  • Building your own recursive lists/macro is also trivial, you only have to watch for infinite loops something in the tree treating something above it as a child (Kin handles this)

Thanks @TW_Tones,

After experimenting with the kin filter for a while I’ve concluded that it’ll be enough for me, it won’t list my Tiddler hierarchy as I originally expected but it gives me the values of all the tags of the filtered Tiddlers (so I can extract their relationship from this flat list). The last problem that I have now is how I can put the results of a macro inside an iframe :thinking:… The macro that I need is the following:

<$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]]	 $output="text/raw"/>

It generates a JSON output the way I want. However, I also need it to be inside the src parameter from my iframe:

<iframe 
    src='http://mypage.com/tiddlywiki?tagTree=<$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]]	 $output="text/raw"/>'
    width="100%"
    height="800">
</iframe>

This last iframe doesn’t work. Is there any way of using this macro inside the iframe tag? That’d be the last step to make it work for me… After that I can receive the data from the iframed page and do I what I want to do with Javascript.

I am no expert on iframe’s and intercommunication but can you define the JSON or the list outside the iframe and then reference it in the iframe html statement?

This should read filter="[kin::to[Root Tag]]" with quotes.

Some notes

  • Although it goes quite deep the innerwiki plugin shows how to generate a wiki based on content in the current wiki and uses data statements to pass tiddlers into the wiki so created so It may be a source of hacks, however hopefully some one can give you some more stright forward answers.
  • I believe in recent versions of tiddlywiki reference has being made to iframe sandboxes and related matters I am not so well versed in.

Best of luck

I’m not sure if I’m missing some steps, but I’ve tried using different hardcoded variables like {{!!title}} and <<currentTiddler>> inside the iframe. Something like:

<iframe 
    src="http://mypage.com/tiddlywiki?tagTree={{!!title}}"
    width="100%"
    height="800">
</iframe>

The goal here was just to check if I managed to pass a Tiddler variable to my iframe. But none of them worked, I received the literal strings on both of them ("{{!!title}}" and "<<currentTiddler>>" as the tagTree variable in the iframe page). So even if I declare the JSON outside the iframe I’m not sure how I’d use it inside the src attribute. :thinking:

Read the section on Attributes: https://tiddlywiki.com/#HTML%20in%20WikiText

What you likely want is filtered attributes values.

Thanks @saqimtiaz

I think that for my case the filtered attributes values would apply if I wasn’t using the jsontiddlers macro… It seems to me that the filtered attributes accept only filters as input, and I’d need a macro as input.

But looking to the Variable Attribute Values on the link, I’ve created this macro:

\define rootTag()
http://mypage.com/tiddlywiki?tagTree=<$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]]	 $output="text/raw"/>
\end

<<rootTag>>

When I look at the results of this macro on a Tiddler it seems to be doing exactly what I want… I see something like:

http://mypage.com/tiddlywiki?tagTree=[{"title":"title1","created":"20211031015007746","modified":"20211102045254789","tags":"Linux","type":"text/vnd.tiddlywiki","text":"","revision":"0","bag":"default"},{"title":"title2","created":"20211030213138347","modified":"20211102045219842","tags":"Linux","type":"text/vnd.tiddlywiki","text":"","revision":"0","bag":"default"}]

However, even though it seems to be working fine, when I try to use it inside my iframe:

<iframe 
    src=<<rootTag>>
    width="100%"
    height="800">
</iframe>

What I actually receive inside the iframe is <$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]] $output="text/raw"/>. It seems that the macro inside the rootTag macro that I’ve created is executed when I call the iframe, and for some reason what is inside the iframe is not executing a macro that is inside another macro. Is there any workaround for making the execution of a macro inside a macro possible inside an HTML attribute?

I haven’t had the opportunity to understand the big picture of what you are trying to achieve, but it sounds like what you need to do is wikify your macrocall and save the output as a variable, then use that variable directly as an HTML attribute or in a filtered attribute.

See https://tiddlywiki.com/#WikifyWidget

See also here Tones GitWiki — For Designers from TW Tones (AKA TonyM)
(not updated for 5.2.0) but still mostly true.

@saqimtiaz 's suggestion is similar to mine, previously and documented at the link.

It’s almost 100% functional I think… I’ve done the following now:

\define rootTag()
http://localhost:1990/tiddly?tagTree=<$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]] $output="text/raw"/>
\end

<$wikify name=wikified-input text=<<rootTag>>>
	<iframe 
			src=<<wikified-input>>
			width="100%"
			height="800">
	</iframe>
</$wikify>

It works for cases where I don’t have a lot of tiddlers (I mean, I managed to get the data from inside the iframe)… But there are still some edge cases that I’m still trying to figure out what happens, I think it’s some problem with quotes or special characters since the text inside the Tiddlers can vary a lot in special character content.

Screenshot_20211103_095010

The jsontiddlers macro gets more parameters than I need though, I only need the tags and title keys… if I could at least find a way of excluding the text key from the JSON in TiddlyWiki before sending it in my iframe I think the special character’s problem would be solved since I don’t have special characters on my titles and tags.


Trying to delete the text key from the jsontiddlers macro:

I’m not sure if I can edit core Javascript macros… I’ve found the macro $:/core/modules/macros/jsontiddlers.js and I tried to change the export part to:

exports.run = function(filter,spaces) {
	let json = this.wiki.getTiddlersAsJson(filter,$tw.utils.parseInt(spaces));
	for (let i = 0, len = json.length; i < len; i++) {
		delete json[i].text
	}  
	return json
};

Logically I think it’s right, but even if I comment the return json on this Tiddler, the behavior of the macro jsontiddlers remains the same. Can I alter the behavior of the jsontiddlers macro by modifying the $:/core/modules/macros/jsontiddlers.js Tiddler?

This is a start on a recursive macro

\define recurse( tag cnt idx )
<$list filter="[tag<__tag__>count[]compare:number:gt[0]]" variable="count">
{<<__tag__>> : [
<$list filter="[tag<__tag__>]" variable="tag" counter="index"
 >
<$macrocall $name=recurse tag=<<tag>> cnt=<<count>> idx=<<index>> />
</$list>
] }<$list filter="[<__cnt__>compare:number:ne<__idx__>]">,</$list>
</$list>
<$list filter="[tag<__tag__>count[]compare:number:eq[0]]">
"<<__tag__>>"<$list filter="[<__cnt__>compare:number:ne<__idx__>]">,</$list>
</$list>
\end

<<recurse About>>

Which on TiddlyWiki.com yields

{About : [ "Acknowledgements", "Contributors", "History of TiddlyWiki", "License", {Releases : [ "AlphaReleases", "BetaReleases", "TiddlyDesktop Releases", "TiddlyWiki Releases", "TiddlyWiki5 Versioning" ] }, "RoadMap" ] }

Thanks @Mark_S , I’ll still take the time to understand better the wikitext syntax…

Actually, I managed to solve this issue with the different answers that I received here so far… To sum up all the steps that I did in a single answer:

  1. I’ve installed the Kin Filter on my TiddlyWiki in order to be able to filter a list of tags recursively with [kin::to[Root Tag]]
  2. I’ve used the jsontiddlers macro to convert the Kin Filter to a flat JSON list. And then I called the iframe as the following:
\define rootTag()
http://localhost:1990/tiddly?tagTree=<$macrocall $name="jsontiddlers" filter=[kin::to[Root Tag]] $output="text/raw"/>
\end

<$wikify name=wikified-input text=<<rootTag>>>
	<iframe 
			src=<<wikified-input>>
			width="100%"
			height="800">
	</iframe>
</$wikify>
  1. I’ve modified the contents of $:/core/modules/macros/jsontiddlers.js in order to exclude the text key from its JSON output, just so I don’t have to deal with special character bugs:
/*\
title: $:/core/modules/macros/jsontiddlers.js
type: application/javascript
module-type: macro

Macro to output tiddlers matching a filter to JSON

\*/
(function(){

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

/*
Information about this macro
*/

exports.name = "jsontiddlers";

exports.params = [
    {name: "filter"},
    {name: "spaces"}
];

/*
Run the macro
*/
exports.run = function(filter,spaces) {
    let json = JSON.parse(this.wiki.getTiddlersAsJson(filter,$tw.utils.parseInt(spaces)))
    for (let i = 0, len = json.length; i < len; i++) {
        delete json[i].text
    }  
    return JSON.stringify(json)
};

})();
  1. I’ve restarted the TiddlyWiki service and reloaded the page.

Well, that’s all… Now I’m receiving the data from my TiddlyWiki filter as a JSON inside the iframe. And I can focus more on the Javascript part from now on.

Thanks everyone for the answers. It was a long thread… :slight_smile:

Very interesting, do share back the result, and if you can share modular solutions that other can use please do.

I wouldn’t have thought that a flat list met your requirements. I thought you needed a structured, nested list.

Overwriting a core macro and changing its behaviour is a recipe for problems.

Instead, change the tiddler title (in the metadata at the start of the tiddler as well), as well as the exports.name property to something like jsontiddlers-skinnyto have this as a custom macro along side the core macro.

2 Likes

@Mark_S You’re right… I needed it to be in a nested structure, but since I have the title and tags value I can solve this kind of stuff with Javascript. Actually, I was originally thinking on redistributing it inside the iframe but I ended up creating a Javascript macro for that:

$ _core_modules_macros_jsontree.js.tid (2.1 KB)

It works fine… The only possible edge case that I haven’t considered is the possibility of creating multiple tags for a Tiddler. It can be handled but since in my case I just use one tag for each Tiddler, I didn’t develop a condition for handling that.

@saqimtiaz Thanks for the tip, I’ve made the changes in a new macro here.