0 votes
by (350 points)

Hi.  After some searching, I'm somewhat surprised that I haven't been able to find an elegant solution.  What I'd like is something like:

var myVar = $.wiki("<<print 'hello'>>");

Resulting in the myVar variable containing "hello".

Now, I have been successful with the following technique, but I wonder if there is a less cumbersome way:

$("body").append("<div id='dummy' style='display: none;'/>")
function myFnc()
  $("#dummy").html("").wiki("<<print 'hello'>>");
  var myVar = $("#dummy").html();


1 Answer

+1 vote
by (44.7k points)

If you just want to set the value of a variable in JavaScript you can just do:

var myVar = "hello";

There shouldn't be any need to use jQuery or the wiki() function for that.

I think it might be better if you explained what you were attempting to do in general and why the method I gave isn't what you need, as opposed to trying to come up with solution to your specific question, since there is likely a better/easier way to do what you actually need to do.

by (350 points)
Right.  The reason that this would be useful is that I have a good amount of custom macros that strictly output text.  One particular example is <<article id count>>, which produces the text "a" for "count" times, and then the text "the" thereafter.  So, if you have "<<article dude 3>> dude", it prints "a dude" the first three times and "the dude" thereafter.

Now, if I wanted to write a javascript function "getAlias(charId)" which returns, say, "a dude" the first three times and "the dude" thereafter, it'd be nice and easy if I could store the wiki text in a variable, then parse it in "getAlias(charId)" and return the result.
by (350 points)
By the way, you are the developer of SugarCube?  If so then congratulations on a wonderful platform!  It's very exciting to have such a solid framework to work with.
by (63.1k points)

If all you need is to output text (that may contain markup or macros itself) from inside a custom macro, you can use this.output: 

var myText = "a";

More info: https://www.motoslave.net/sugarcube/2/docs/api-macro.html#macrocontext-api

by (44.7k points)
edited by

OK, I'm still a little hazy on what you're trying to do, but I think I understand it.

If I'm right you want a SugarCube macro like this in your JavaScript section:

Macro.add("a", {
	tags	: null,
	handler	: function () {
		if (State.temporary.articleStatus === undefined) {
			// make sure that _articleStatus is initialized
			State.temporary.articleStatus = {};
		var articleStatus = State.temporary.articleStatus;
		var txt = this.payload[0].contents;
		var noun = txt.toLowerCase();
		if (this.args.length > 0) {
			// handle parameters
			var count = this.args[0];
			if (typeof count === 'number') {
				if (articleStatus[ noun ] === undefined) {
					articleStatus[ noun ] = {};
				articleStatus[ noun ].count = count;
				if (this.args[1] === undefined) {
					articleStatus[ noun ].initial = "a";
				} else {
					articleStatus[ noun ].initial = this.payload[0].args[1];
				if (this.args[2] === undefined) {
					articleStatus[ noun ].secondary = "the";
				} else {
					articleStatus[ noun ].secondary = this.payload[0].args[2];
			} else {
				throw new Error("First parameter of 'a' macro must be either a number or undefined.");
		var article;
		if (hasOwnProperty.call(articleStatus, noun)) {
			// noun exists, so set article appropriately
			if (articleStatus[ noun ].count > 0) {
				article = articleStatus[ noun ].initial;
				--articleStatus[ noun ].count;
			} else {
				article = articleStatus[ noun ].secondary;
		} else {
			// default article
			article = "a";
		if (txt.charAt(0) === txt.charAt(0).toUpperCase()) {
			// swap upperase from noun to article
			article = article.charAt(0).toUpperCase() + article.slice(1);
			txt = txt.charAt(0).toLowerCase() + txt.slice(1);
		// output noun with appropriate article in front of it
		$(this.output).wiki(article + " " + txt);

So if you used the above with this in a Twine passage:

<<a 3>>dude<</a>> - <<a 2 "an" "this">>orange<</a>>
<<a>>dude<</a>> - <<a>>orange<</a>>
<<a>>dude<</a>> - <<a>>Orange<</a>>
<<a>>Dude<</a>> - <<a>>dude<</a>>
<<a>>orange<</a>> - <<a>>Dude<</a>>

It would output this:

a dude - an orange
a dude - an orange
a dude - This orange
The dude - the dude
this orange - The dude

So, basically that "a" macro up top works like this:

<<a [count] [initialArticle = "a"] [secondaryArticle = "the"]>>noun<</a>>

The parameters in brackets are optional, and only need to be included the first time for each noun it's used on in a passage.  If there is a "count" parameter, then that parameter must be a number.  If the other two parameters are not included, "initialArticle" will be set to "a", and "secondaryArticle" will be set to "the".  It then stores the noun in a temporary object ("_articleStatus"), along with the count, initialArticle, and secondaryArticle.

Then, if "count" > 0 for that noun, it will display the value of initialArticle followed by the noun and decrement count, otherwise it will put secondaryArticle before the noun (if the noun was capitalized, then the article will become capitalized instead).  If that noun was never initialized with any parameters, then it defaults to putting the article "a" in front of the noun.

It will automatically keep track of each noun separately, so you can mix them together, as I showed above.

If you would like that information to work across passages then you could change "State.temporary" to "State.variables" (which makes it use "$articleStatus" instead), though keep in mind that the information will now take up space in your game's history.

If that wasn't what you were looking for, I'll need a bit clearer example of what you were trying to do, since I wasn't quite sure how it works the subsequent times you call to your <<article>> widget.

Also, I'm not the developer of SugarCube.  That would be TheMadExile, so all thanks go to him for it.

In any case, I hope that helps! smiley

P.S. You can change the name of that macro if you want to.  I just called it "a" since you already had a widget called "article", "a" was shorter, and "a" is the default article that it prefaces things with.  That said, if you do keep it, make sure you don't confuse the <<a>> macro with the <a> HTML element.

P.P.S. I've made multiple edits to the above over the last 20 minutes, so if you didn't see this P.P.S. before, please re-read the above.

by (350 points)
edited by
Thank you for putting so much effort into helping me.  I really appreciate that.  I also wish that I had been more clear to not waste your time, though I did learn some good techniques from your code.

I'll try to be more clear now.

I first worked in Harlowe.  At that time all of the systems code for my stories (copied verbatim to each story) was in passages.  I found it frustrating to find bugs or enhancements and to copy/paste each adjusted passage from story to story each time.  I moved to Sugarcube to be able to shift all of the systems code entirely to JavaScript, with the passages left exclusively for  the story-specific work.

One JS system that I have implemented is "characters".  I keep a state on each character in a story-state object variable (keyed using a unique charId string, like "dean").  This state includes the character's current alias, which changes as you meet the characters.  I have a JS function "getAlias(charId)" to get their current alias string (with a macro wrapper for passage logic).

I also have two JS functions "showChar(charId)" and "hideChar(charId)" (also with macro wrappers) which toggle a captioned portrait of the given character above the passage.  The "showChar(charId)" function calls "getAlias(charId)" to update the portrait's caption to the latest alias.

Finally, I have a macro, "<<article [id] [count]>>", which lets me add a sense of familiarity by changing from the indefinite article 'a' to the definite article 'the' over time (and 'an', though I haven't gotten around to implementing that).

So, I'd like to embed the "article" macro in character's alias strings and have it be parsed by "getAlias(charId)" to show up both within passages and in the portrait captions for the characters.

So, currently, "getAlias(charId)" parses the wiki code in aliases through a hidden div.  It calls the JQuery "wiki" method on the div (passing the alias value), then it pulls out the result and returns it.  My reason for posting thsi question was that I was wondering if there was an easier way to parse a string with wiki code in JavaScript.  I'm totally ok with keeping things as-is because it works just fine, it was just surprising that wiki parsing wasn't more directly available on the JavaScript side.

I hope that this is clear enough and not overly long.  I think that I've been working with my code base long enough that I made some assumptions of understanding in my previous explanations.  I also wanted to avoid the gory details, but I now recognize that they are essential to understanding my question.