Copy link to clipboard
Copied
I've been trying to request information from an API in JavaScript using this, but ExtendScript keeps giving me an error saying that..
var xhr = new XMLHttpRequest();
..does not have a constructor.
Is it possible to do this from within Photoshop/Are there any workarounds?
1 Correct answer
Hi,
the reply you're getting isn't anything fancy, just a regular String.
Try this:
$.writeln(reply.substr(reply.indexOf("\r\n\r\n")+4));
(it finds "\r\n\r\n" in the reply and start reading from the end of it to the end of the string)
Hope this helps!
Davide
UPDATE (removed an unnecessary "this." in the code)
Explore related tutorials & articles
Copy link to clipboard
Copied
ExtendScript does not support XMLHttpRequest. It does have a Socket class that you can do HTTP over. They provide an example in the JS Tools Guide.
In theory, you might be able to take a JS implementation (intended for use in a browser page) and port it to use an HTTP implementation based on the Socket class. I don't know if it really is possible but it does sound like an interesting problem for someone to tackle.
Copy link to clipboard
Copied
Thanks for your answer! This looks like it has potential, although I don't have a clue where to start. I'm very new to JS..
Copy link to clipboard
Copied
You also may want to take a look at this: mozilla-1.9.2: content/base/src/nsXMLHttpRequest.cpp@d175c6ec8784
Copy link to clipboard
Copied
Wow, I don't know what any of that means. What is that?
Copy link to clipboard
Copied
Hi,
this is a basic example from my own library:
reply = "";
conn = new Socket();
// conn.encoding = "binary";
if (conn.open ("www.currentmillis.com:80", "binary")) { // 188.121.45.1
$.writeln("Connected!");
conn.write ("GET http://www.currentmillis.com/api/millis-since-unix-epoch.php HTTP/1.0\r\nHost:www.currentmillis.com\r\nConnection: close\r\n\r\n");
reply = conn.read(999999);
$.writeln(reply); // FULL REPLY
conn.close();
}
The Socket object is quite low level... you basically need to write your own HTTP header (info here: https://code.tutsplus.com/tutorials/http-headers-for-dummies--net-8039‌);
Hope this helps as a starting point!
Davide Barranca
---
www.davidebarranca.com
www.cs-extensions.com
Copy link to clipboard
Copied
Hi DBarranca,
Thanks for this. I see that this is using the example found in the JS Tools Guide (but with your url). I ran your code and it worked great. I was worried that perhaps taking a JS implementation and porting it to HTTP (as xbtyor2 suggested) one would be overly complicated and above my knowledge level, but perhaps it's just as simple as replacing your URL in the conn.write () with the URL for the API I'm trying to access (and removing the if statement). It can only be accessed on the local network at work, so I will try it out when I there on Thursday and see what happens! The information it returns is very simple (just one line of text in a JSON structure), so if I can just get that and throw it into the variable reply, I'll be all set.
Copy link to clipboard
Copied
Hi,
if JSON is what you're after, parsing the reply in order to get rid of the header should work. Let us know if you succeed!
-Davide
Copy link to clipboard
Copied
Hi Davide,
So I'm testing it in ExtendScript Toolkit, and have tried it two ways (have just replace the actual website with 'website' for here):
reply = "";
conn = new Socket();
conn.write ("GET http://website.com/api/products/600570/for-studio?content-type=application/json");
reply = conn.read(999999);
$.writeln(reply);
conn.close();
Just returns 'Result: true' in the console, and:
reply = "";
conn = new Socket();
conn.write ("GET http://website.com/api/products/600570/for-studio?content-type=application/json");
reply = conn.read(999999);
var result = JSON.parse(reply)
$.writeln(result);
conn.close();
Gives me an error: 'JSON is undefined'.
Any idea what I might be doing wrong? When I enter the API's url in my browser, this is the information that it display:
{"designer":"Aurélie Bidermann","season":"FW15","comments":["Merge with PID: 551550","Merge with PID: 551550","Merge with PID: 551550"],"business":"NAP"
Seems pretty simple, so I'm a bit confused as to how to pass that information into a variable for me to use..
Copy link to clipboard
Copied
Hi,
JSON is undefined because we're in the beautiful world of ExtendScript, which has many extras compared to current ECMAScript compliant JS, but - compared to current JS - lacks a lot. Basically you're stuck at ES3 (and now we're at ES6).
No Array.indexOf, no native JSON support, not to mention promises, etc.
Include this file: JSON-js/json2.js at master · douglascrockford/JSON-js · GitHub and you're ready to go with JSON in ExtendScript.
Hope this helps,
-Davide
Copy link to clipboard
Copied
Hi Davide,
Thanks again for your reply. How do I include the file in my script? I copied and pasted the code on that page into it and tried to run it and it gave me an error saying
Cannot execute script in target engine 'main'!
(#23) ) does not have a value
Copy link to clipboard
Copied
Hi,
have you used a minified version? Mind you, aggressive minification can be harmful for the ExtendScript engine (see: http://www.davidebarranca.com/2013/08/testing-minified-js-libraries-in-extendscript/)
Try using this one:
if(typeof JSON!=='object'){JSON={};}(function(){'use strict';function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf();};}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta;return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+strin...
If it still shows an error, it must be somewhere else 🙂
I've added this feature request:
[PS Scripting] ExtendScript support for ECMAScript 6th version
please add your vote!
-Davide
Copy link to clipboard
Copied
Voted! Thanks for requesting this 🙂
I've added your minified version, and it seems that my code was indeed the problem and I've narrowed it down to this line:
var result = JSON.parse(reply) |
If I remove this line, the script runs, so I'm not sure how to parse it? This is the whole code:
if(typeof JSON!=='object'){JSON={};}(function(){'use strict';function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf();};}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta;return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+strin...
reply = "";
conn = new Socket();
conn.write ("GET http://website.com/api/products/600570/for-studio?content-type=application/json");
reply = conn.read();
var result = JSON.parse(reply)
$.writeln(result);
conn.close();
Running this as is gives me that same error as before. When I take that line out, it runs fine and just returns true. Not sure what I'm doing wrong? Weird thing is, if I run it like this:
reply = "";
conn = new Socket();
conn.write ("GET http://website.com/api/products/600570/for-studio?content-type=application/json");
reply = conn.read();
$.writeln(reply);
conn.close();
Nothing gets printed to the console and it just returns true. Surely should at least be getting some HTTP headers or something?
Really stuck here. I know exactly what I need to do with the API's info after I manage to get it into a variable, I just have no idea to actually get this part to work.
Copy link to clipboard
Copied
Your code lacks some important parts:
reply = "";
conn = new Socket();
conn.open ("website.com:80", "binary")
conn.write ("GET http://website.com/api/products/600570/for-studio?content-type=application/json HTTP/1.0\r\nHost:website.com\r\nConnection: close\r\n\r\n");
reply = conn.read(999999);
//var result = JSON.parse(reply)
$.writeln(reply);
conn.close();
The above results in:
HTTP/1.1 301 Moved Permanently
Content-Type: text/html; charset=UTF-8
Location: http://www.website.com/api/products/600570/for-studio?content-type=application/json
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Thu, 23 Jul 2015 13:46:29 GMT
Connection: close
Content-Length: 206
<head><title>Document Moved</title></head>
<body><h1>Object Moved</h1>This document may be found <a HREF="http://www.website.com/api/products/600570/for-studio?content-type=application/json">here</a></body>
Result: true
From this point onwards I can't help - it seems you're not referring a proper JSON file in the GET request, this is possibly the reason...
Davide
Copy link to clipboard
Copied
Sorry, I removed the actual website and just replaced it with website.com in the API's url just to protect the identity of it when posting on these forums.
I used your code with the correct url for the API and it works great! I'm getting HTTP headers back along with the info the API has in the console. Thank you!
However, when I then un-comment the JSON.parse(reply) it throws this error:
Uncaught JavaScript exception: JSON.parse
I have the json script at the start of my code.
Copy link to clipboard
Copied
Hi,
the reply consists of the Header + the actual data, so you have to get rid of the "HTTP/1.1 blabla" before feeding it to the JSON.parse() function.
Davide
Copy link to clipboard
Copied
Sorry to a be a pain, Davide - you have already been extremely helpful. I've tried searching for ways to remove headers from a response but I can't seem to find anything. Do you know how I would go about doing this?
Copy link to clipboard
Copied
Hi,
the reply you're getting isn't anything fancy, just a regular String.
Try this:
$.writeln(reply.substr(reply.indexOf("\r\n\r\n")+4));
(it finds "\r\n\r\n" in the reply and start reading from the end of it to the end of the string)
Hope this helps!
Davide
UPDATE (removed an unnecessary "this." in the code)
Copy link to clipboard
Copied
Davide, I could kiss you. That's done it! Thank you for being so patient with me, I really appreciate it.
Copy link to clipboard
Copied
You're welcome That's what the forums are for!
Best regards
-Davide

