Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Does Story.lookup() accept wildcard characters in JavaScript?

edited September 2016 in Help! with 2.0
I'm using Sugarcube 2.8 and Twine 2!

I'm making myself a macro which indexes all the passages in my story and says whether they are tagged or not. Since my titles all have a space in them except for the Special Name'd ones, I figured searching for all titles with one would be the easiest solution. I'm currently trying to use the JavaScript "*" asterisk wildcard character like so:
new Wikifier(place, Story.lookup("title", "* *" )[0].title);

Which, to my understanding, should bring back any passage title with a space in it. Instead, it throws me this error:
TypeError: Cannot read property 'title' of undefined

I am aware that Story.lookup brings me an array of objects and that it comes out as undefined if nothing matches. However, there should be PLENTY of matches looking over my story map over 30 passages strong. Is there any way to get the method to cooperate and accept wildcards? If not, is there any better way to do what I'm doing?

Comments

  • IcoToner wrote: »
    I'm using Sugarcube 2.8 and Twine 2!
    Thank you.

    IcoToner wrote: »
    I'm making myself a macro which indexes all the passages in my story and says whether they are tagged or not. Since my titles all have a space in them except for the Special Name'd ones, I figured searching for all titles with one would be the easiest solution.
    There is currently no way to search the passage store using globbing wildcards, regular expression patterns, or regular expressions. Matches are made using lazy equality.

    I could add a new lookup method which would take a predicate. That would allow you to filter upon whatever criteria you wanted. I like that idea, actually—probably do something like Story.lookupWith(filterPredicate [, sortKey]).

    Alternatively, you could search manually through the story data, though that wouldn't be ideal.

    IcoToner wrote: »
    I'm currently trying to use the JavaScript "*" asterisk wildcard character like so:
    new Wikifier(place, Story.lookup("title", "* *" )[0].title);
    
    JavaScript does not support any kind of globbing wildcards, which is what you're apparently attempting to do. It does support regular expressions, however, the pattern you tried is invalid even for that.

    IcoToner wrote: »
    Which, to my understanding, should bring back any passage title with a space in it.
    If I may ask, where did you see this? If there's bogus information out there, then I'd like to try to get it corrected. On the other hand, if that was your understanding of the Story.lookup() static method's documentation, then I may need to revisit it.

    IcoToner wrote: »
    Instead, it throws me this error:
    TypeError: Cannot read property 'title' of undefined
    
    The reason you received the TypeError is because you attempted to reference the first element of an empty array, which is nonexistent and thus undefined, and then further attempted to access the title property on that undefined value.

    IcoToner wrote: »
    I am aware that Story.lookup brings me an array of objects and that it comes out as undefined if nothing matches.
    Emphasis mine. Yes to the former, no to the latter. If no passages match, it returns an empty array, not undefined.
  • edited September 2016

    There is currently no way to search the passage store using globbing wildcards, regular expression patterns, or regular expressions. Matches are made using lazy equality.

    I could add a new lookup method which would take a predicate. That would allow you to filter upon whatever criteria you wanted. I like that idea, actually—probably do something like Story.lookupWith(filterPredicate [, sortKey]).

    Alternatively, you could search manually through the story data, though that wouldn't be ideal.

    Oh, if you could, that would be much appreciated. Although, in the meantime I just need a way to make sure changing passage titles in my story map doesn't mess up the variables I have. Right now I'm just tagging each passage with a unique number, that way I can refer to them if I need to change the title for any reason. I know there's a domID system in the Passage object, but it requires the passage's title to even get it in the first place, so it's pretty self-defeating when it comes to the end I need it.
    JavaScript does not support any kind of globbing wildcards, which is what you're apparently attempting to do. It does support regular expressions, however, the pattern you tried is invalid even for that.
    Ah, I see. Would you be kind enough to direct me to a good tutorial on that? I'm pretty much a beginner here and I'm going off documentation and simple examples.
    If I may ask, where did you see this? If there's bogus information out there, then I'd like to try to get it corrected. On the other hand, if that was your understanding of the Story.lookup() static method's documentation, then I may need to revisit it.
    That was not the documentation's fault at all! It was more with my general confusion with JavaScript since it's somewhat new to me. I looked up a supposed tutorial, but it left me more confused than anything.
    The reason you received the TypeError is because you attempted to reference the first element of an empty array, which is nonexistent and thus undefined, and then further attempted to access the title property on that undefined value.
    So what way should I refer to an array with an empty or nonexistant value? I used JavaScripts "== NaN" before in my coding to do so, but that caused my Twine to hang upon testing. This is the code I used to index all the passages with a room name, highlighting any instances where more than one room was associated with a single tag at a time. (Specifically this code in line 12, I would have emboldened it, but I can't do that in code blocks :<)
    macros.indexrooms = {
       handler : function (place, macroName, params) {
             var roomNumMin = params[0];
             var roomNumMax = params[1] + 1;
             if (params[1] == "NaN") {
                   roomNumMax = params[0];
                   roomNumMin = 0;
    	}
    	try {
    		for (var i = roomNumMin; i < roomNumMax; i++) {
    			new Wikifier(place, "Room " + i + ": ");
    		        for(var r = 0, roomObject = Story.lookup("tags", i); roomObject[r] != roomObject[(r+1000000)]; r++) {
    				if (r > 0) {
    					new Wikifier(place,"''//__" + roomObject[r].title + "''//__ | ");
    				}
    				else {
    					new Wikifier(place, roomObject[r].title + " | ");
    				}
    			}
    			new Wikifier(place,"<br>");
    		}
    	}
    	catch(e) {
    		new Wikifier(place, "ERROR: " + e + " INDEXING ERROR");
    	}
       }		
    };
    

    The silly "roomObject[r] != roomObject[(r+1000000)]" was a how I got past the program hang instead of doing it how I mentioned before.

    Thanks so much for your help! I just need a way to give passages a unique identifier which won't change if I mess with titles AND is readable when debugging. Right now I'm using the macro you see above to know which tag goes to which room name, but I fear if I just use numbers or even more macros to get the passage names from the tags when working with the code that it'll become a nightmare to work with.

    EDIT: Also! Is it possible to comment things out in Twine Sugarcube like with JavaScript? Right now I'm going to just comment the passage names next to the tag numbers in the code so I know which is which. There's no syntax highlighting when I use Sugarcube, so I have no idea what the syntax for commenting would be.
  • IcoToner wrote: »
    so I have no idea what the syntax for commenting would be.
    It depends on which editor (Passage, CSS, Javascript) you are in:

    1. Passage:
    /% SugarCube's own multi-line markup. %/
    
    <!-- HTML's multi-line comment -->
    

    2. CSS and Javascript:
    /* Standard multi-line comment. */
    
  • Oh! Thanks, greyelf!
  • edited September 2016
    Maybe check out the JavaScript Guide @MDN if you want a general JavaScript tutorial.

    IcoToner wrote: »
    Although, in the meantime I just need a way to make sure changing passage titles in my story map doesn't mess up the variables I have. Right now I'm just tagging each passage with a unique number, that way I can refer to them if I need to change the title for any reason. I know there's a domID system in the Passage object, but it requires the passage's title to even get it in the first place, so it's pretty self-defeating when it comes to the end I need it.
    I must be missing something, why would changing passage titles affect your variables?

    IcoToner wrote: »
    Ah, I see. Would you be kind enough to direct me to a good tutorial on that? I'm pretty much a beginner here and I'm going off documentation and simple examples.
    Uh. Try regular-expressions.info and the JavaScript Regular Expressions Guide @MDN, I suppose. Sorry, you're aking me for something I haven't required in, well, a really long time.

    IcoToner wrote: »
    I looked up a supposed tutorial, but it left me more confused than anything.
    Not to be unkind to the authors, however, most Twine tutorials I've seen online are little more than cargo culting.

    IcoToner wrote: »
    So what way should I refer to an array with an empty or nonexistant value?
    Before you attempt to index an array, you should always check its length. For example:
    var myArray = /* something which yields an array */;
    
    if (myArray.length > 0) {
    	/* myArray contains at least one element */
    }
    
    Or within a for loop:
    var myArray = /* something which yields an array */;
    
    for (var i = 0; i < myArray.length; ++i) {
    	/* iterate over myArray, ensuring the index is less-than its length */
    }
    

    IcoToner wrote: »
    I used JavaScripts "== NaN" before in my coding to do so, but that caused my Twine to hang upon testing.
    NaN (Not-a-Number) is a Number type value, representing a value which is not a number. Also, by definition, nothing equals the NaN value, not even itself, so you cannot use equality operators to test values for NaN-ness. If you ever did need to test a value to see if it was NaN, you should use the Number.isNaN() method.

    IcoToner wrote: »
    This is the code I used to index all the passages with a room name, highlighting any instances where more than one room was associated with a single tag at a time. (Specifically this code in line 12, I would have emboldened it, but I can't do that in code blocks :<)
    macros.indexrooms = {
       handler : function (place, macroName, params) {
             var roomNumMin = params[0];
             var roomNumMax = params[1] + 1;
             if (params[1] == "NaN") {
                   roomNumMax = params[0];
                   roomNumMin = 0;
    	}
    	try {
    		for (var i = roomNumMin; i < roomNumMax; i++) {
    			new Wikifier(place, "Room " + i + ": ");
    		        for(var r = 0, roomObject = Story.lookup("tags", i); roomObject[r] != roomObject[(r+1000000)]; r++) {
    				if (r > 0) {
    					new Wikifier(place,"''//__" + roomObject[r].title + "''//__ | ");
    				}
    				else {
    					new Wikifier(place, roomObject[r].title + " | ");
    				}
    			}
    			new Wikifier(place,"<br>");
    		}
    	}
    	catch(e) {
    		new Wikifier(place, "ERROR: " + e + " INDEXING ERROR");
    	}
       }		
    };
    
    The silly "roomObject[r] != roomObject[(r+1000000)]" was a how I got past the program hang instead of doing it how I mentioned before.
    First. You should probably be using SugarCube 2's native Macro API, rather than the legacy macro API. I'd rewite your macro as an example, which would include correcting several other issues, however, I'm unsure as to the reasoning behind why you're doing any of this.

    Also, you should be nesting your markup properly. The following:
    ''//__ … ''//__
    
    Should be:
    ''//__ … __//''
    
    Markup blocks should be closed in the reverse of the order used to open them. This is also true for HTML block markup.

    IcoToner wrote: »
    I just need a way to give passages a unique identifier which won't change if I mess with titles AND is readable when debugging.
    This is what I'm talking about. Why do you need a unique identifier other than the passage name/title, which already should be unique? Why are you, apparently, renaming passages with enough regularity to make depending on their names problematic?

    If the reason is that you're worried about wiki/double-square-bracketed links () being broken, then Twine 2 will automatically fix all such when you rename a passage. Beyond that, there's also find and replace.

    IcoToner wrote: »
    EDIT: Also! Is it possible to comment things out in Twine Sugarcube like with JavaScript? Right now I'm going to just comment the passage names next to the tag numbers in the code so I know which is which. There's no syntax highlighting when I use Sugarcube, so I have no idea what the syntax for commenting would be.
    In SugarCube, you may use C-style block comments (/* … */) within normal passages, JavaScript, and CSS. For example:
    /* This is a block comment. */
    
    /*
    	This is also a block comment.
    */
    
    HTML block comments (<!-- … -->) and TiddlyWiki block comments (/% … %/) are also supported within normal passages, however, you may as well simply use C-style block comments since they're supported everywhere. JavaScript also supports an inline comment syntax.
  • Thanks again, TheMadExile!

    As for why I was making redundant unique identifiers, I honestly have no clue, in retrospect. I knew about the find and replace option before, but I guess I wanted to be super careful? I don't know.
    I'd rewite your macro as an example, which would include correcting several other issues, however, I'm unsure as to the reasoning behind why you're doing any of this.
    Well, the main reason I was using the legacy form was that the Wikifier didn't really work. For the exact same code with one minor change to the opening line (which would be the following with one less parenthesis at the closing of the command:
    macros.example = {
    
    Rather than:
    Macro.add("if", {
    
    The former would execute and print out the contents of the Wikifier while the latter would not. I'm not sure why, myself, really. The Wikifier would be written up exactly how you see in the code from my previous post. There's also no real documentation about the Wikifier in the Sugarcube 2.8 documentation other than in passing, so I don't know if I'm just writing something incorrectly.
  • IcoToner wrote: »
    Well, the main reason I was using the legacy form was that the Wikifier didn't really work. For the exact same code with one minor change to the opening line (which would be the following with one less parenthesis at the closing of the command:
    macros.example = {
    
    Rather than:
    Macro.add("if", {
    
    The former would execute and print out the contents of the Wikifier while the latter would not. I'm not sure why, myself, really.
    If you tried exactly what you stated you did, then you, apparently, did not read the SugarCube 2 Macro API documentation very well. I'm not trying to be mean here, there's just really no other way to say it.

    The Macro.add() documentation specifically states that a macro's handler function will be called without arguments/parameters, but with its this set to a macro execution context object. The example given with Macro.add() demonstrates this.

    Thus, the reason you were not getting output from the Wikifier is that you were attempting to output to an undefined value, since place is not defined.

    The reference to the output buffer in a macro execution context object is output, which would be this.output within a macro. For example:
    new Wikifier(this.output, "Who //are// you?");
    
    That said, you should probably be using SugarCube 2's <jQuery>.wiki() extension method rather than making direct calls to the Wikifier. For example:
    $(this.output).wiki("Who //are// you?");
    
  • Ah, thanks for the insight! That should help get my code back into shape.

    Although, I must ask, why shouldn't I make direct calls to the Wikifier? I know little about it since there's no documentation on it on the site. I also have to ask what the "$" is for in that last block of code you typed out.

    Again, thanks for the help!
  • IcoToner wrote: »
    Although, I must ask, why shouldn't I make direct calls to the Wikifier? I know little about it since there's no documentation on it on the site.
    I get the impression that you're reading something extra into what I stated, so let me try again. You should probably be using <jQuery>.wiki() rather than directly calling the Wikifier to produce output. I did not state that you should not call the Wikifier—as in ever.

    As far as why you'd prefer the former. Mostly because <jQuery>.wiki() is documented. Meaning that you have a guarantee that, at least within the same major version (i.e. v2.x.x), it will be available and with the documented interface.

    There are various reasons why I have not documented the Wikifier—which I'm not getting into here. Simply the fact that it is not documented, however, should serve as a warning.

    IcoToner wrote: »
    I also have to ask what the "$" is for in that last block of code you typed out.
    The Emperor documentation provides—look at the special variable names (hint: it's an alias for jQuery).
  • Thanks so much for all the help, TheMadExile! I think that's everything for now. When things break badly and I can't figure out why, I'll come back here for sure!
  • UPDATE
    I could add a new lookup method which would take a predicate. That would allow you to filter upon whatever criteria you wanted. I like that idea, actually—probably do something like Story.lookupWith(filterPredicate [, sortKey]).
    SugarCube v2.11.0 has been released and includes the static method I mentioned previously, Story.lookupWith().
Sign In or Register to comment.