2

I'm writing a InDesign Script and I'm using moments.js for calculating dates.

When I'm using the date-format "Do", which should return fore example "1st, 2nd, 3rd, 4th...", but the calculation the function does, returns a wrong result and the result is this:

1rd 2rd 3rd 4th 5th 6th 7th 8th 9th 10rd 11rd 12rd 13rd 14rd 15rd 16rd 17rd 18rd 19rd 20th 21rd 22rd 23rd 24th 25th 26th 27th 28th 29th 30th 31rd

This is the function:

function returnOrdinal(number) {
  var b = number % 10,
  output = (parseInt(number % 100 / 10) === 1) ? 'th' :
   (b === 1) ? 'st' :
   (b === 2) ? 'nd' :
   (b === 3) ? 'rd' : 'th';
  return number + output;
}

I created a JSFiddle that uses the same function and it returns the correct results.

So is this a known issue in Extendscript? Do you know any other way to return the ordinals? Any workaround?

Thanks in advance

der-lukas
  • 936
  • 9
  • 13

5 Answers5

3

Your code works fine with http://www.w3schools.com/tags/tryit.asp?filename=tryhtml_basic too, therefore I suspect a side effect due to the callback environment. I suggest trying a different writing that might be less sensitive to side effects.

function returnOrdinal(number) {
  switch((number % 100 / 10).toFixed(0) == 1?0:number%10) {
  case 1:  return number+'st';
  case 2:  return number+'nd';
  case 3:  return number+'rd';
  default: return number+'th';
  }
}
  • Thanks! Your function works. I also just tried it with [this function here](http://stackoverflow.com/questions/13627308/add-st-nd-rd-and-th-ordinal-suffix-to-a-number/31615643#31615643), which looks really neat and is used by Shopify! I still don't see why your and the function I linked work, and the one I posted - which is the function used by moments.js - doesn't... – der-lukas Aug 29 '16 at 21:05
3

It looks like chained ternary operators are not supported by ExtendScript.As far as I remember, it is ECMA 3. Another option working in InDesign - just use if else or switch

function returnOrdinal(number) {
    var b = number % 10;
    var output;
    if (parseInt(number % 100 / 10) === 1) {
        output = 'th';
    } else {
        switch (number) {
            case 1:
                output = 'st';
                break;
            case 2:
                output = 'nd';
                break;
            case 3:
                output = 'rd';
                break;
            default:
                output = 'th';
        }
    }
    return number + output;
}
Nicolai Kant
  • 1,391
  • 1
  • 9
  • 23
  • The weird issue is that it doesn't throw an error, but instead just returns a wrong result. (I would expect an error instead of a wrong result). Your function and the ones from the other answers above work, but this still doesn't really answer my question completely since I wanted to know WHY Extendscript returns wrong values and if this is a known issue in Extendscript. I'm going to use [this function](http://stackoverflow.com/questions/13627308/add-st-nd-rd-and-th-ordinal-suffix-to-a-number/31615643#31615643), since it's the most condensed version and works perfectly! – der-lukas Aug 30 '16 at 11:25
  • I just found [this thread](https://forums.adobe.com/thread/1147245), where someones reporting the same issue with chained ternary operators, so it seems you were right that this is an issue with Extendscript handling those wrong... so I'm gonna approve your answer! Would be great if you could improve your answer with a little more info about this error in extendscript! – der-lukas Aug 30 '16 at 11:32
  • The link you posted suggests that the problem is with ESTK. I tried two different versions of ESTK and the results are the same. On the other hand, I tried to run the script in InDesign without ESTK and on InDesign Server and the results are still incorrect. I believe, the problem is actually ECMA 3 or Extend Script implimentation – Nicolai Kant Aug 30 '16 at 13:58
1

According to the documentation your result is correct and your code is wrong (surprisingly, this means that your tests with other JavaScript engines would also be wrong).

Common operator associativity in ECMAScript is left-to-right. However, the ternary test is right to left. That means that your

output = (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th';

(somewhat simplified) first selects on (b === 1), with the possible results 'rd' and 'th' – the final two.

See also the example usage of ? .. : in Mozilla's reference, where the associativity is explicitly mentioned:

Multiple ternary evaluations are also possible (note: the conditional operator is right associative):

var firstCheck = false,
    secondCheck = false,
    access = firstCheck ? "Access denied" : secondCheck ?
    "Access denied" : "Access granted";

console.log( access ); // logs "Access granted"

Overriding the precedence with parentheses makes it work as expected:

function returnOrdinal(number) {
  var b = number % 10,
  output = (parseInt(number % 100 / 10) === 1) ? 'th' :
    ((b === 1) ? 'st' :
    ((b === 2) ? 'nd' :
    ((b === 3) ? 'rd' : 'th')));
  return number + output;
}
Jongware
  • 22,200
  • 8
  • 54
  • 100
  • I am sorry, Rad, but I think it is a wrong answer. I do not think JSFiddle and few other similar engines produce bad result, because I am sure they use JavaScript to evaluate it. I am going to post some code as another answer to support my case. – Nicolai Kant Aug 30 '16 at 14:32
  • @NicolaiKant: is my interpretation of right-to-left wrong? I made sure to test both the original and my adjusted code (with forced precedence) in InDesign. – Jongware Aug 30 '16 at 15:40
  • 1
    No, you solution with parenthesis work fine in InDesign. I did not criticize your code, but challenged linking the problem to right-to-left test for ternary operators. I think it will produce the same result as JSFiddle. I posted another answer with a simple sample code to prove my point. All JS engines render it as OP originally suggested, so I think your logic is incorrect, sorry. – Nicolai Kant Aug 30 '16 at 16:24
1

To counter Rad Lexus answer suggesting there is a problem with JSFiddle and InDesign giving the correct answer.

I have the following test page:

<html>

<head>
    <script>
        function test(num) {
            var b = num;   
            var output = (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th';    
            document.getElementById("ordinals").innerHTML = b + output;    
        }
    </script>
</head>

<body onload="test(1);">
    <div id="ordinals">test
    </div>
</body>

</html>

Rad's post suggests that this should evaluate to '1rd','2rd','4rd' according to the documentation. I saved it as a page and loaded in Chrome, IE and Safati. All three give me 1st, 2nd and 4th as output in responce to 1,2 and 4 as input.

Can you post some code to support your answer? According to you there is a problem with vanilla JavaScript engine or an error in documentation.

Nicolai Kant
  • 1,391
  • 1
  • 9
  • 23
  • I *only* tested in InDesign. I'll rerun the test later on with another version, though, just to be sure. I'm fully open to the suggestion Adobe's own internal JS Engine may be to blaim. At the very least, that would explain the different results with various implementations. – Jongware Aug 30 '16 at 16:55
  • Well, seems like I judged Rad's answer too fast! It was so well explained and documented that I just assumed it's correct..! Thank you @NicolaiKant for the effort! Seems like I came across some rarely occured bug in Extendscript... – der-lukas Aug 30 '16 at 19:11
  • The important thing is, you know where the problem is and there are a few good workaround suggested here. Rad also highlighted something, I did not know. – Nicolai Kant Aug 30 '16 at 23:20
  • Rad, on my experience, if something does not work, it is a problem with Adobe implementation. I think, they based the implementation on ECMA 3 and even that was not fully implemented and had some bugs and issues. As far as I remember, the engine has not been updated for a while, although I did not use the latest versions of the Adobe products. – Nicolai Kant Aug 30 '16 at 23:24
0

May this line works ?

String(number).replace(/1$/, "1st").replace (/2$/, "nd").replace(/3$/, "rd").replace(/[4-9]$/, "th");
Loic
  • 2,173
  • 10
  • 13