Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Using XMLHttpRequest within Photoshop script

Participant ,
Jul 21, 2015 Jul 21, 2015

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?

TOPICS
Actions and scripting
9.9K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Advocate , Jul 23, 2015 Jul 23, 2015

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)

Translate
Adobe
Advisor ,
Jul 21, 2015 Jul 21, 2015

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 21, 2015 Jul 21, 2015

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..

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advisor ,
Jul 21, 2015 Jul 21, 2015

You also may want to take a look at this: mozilla-1.9.2: content/base/src/nsXMLHttpRequest.cpp@d175c6ec8784

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 21, 2015 Jul 21, 2015

Wow, I don't know what any of that means. What is that?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 21, 2015 Jul 21, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 21, 2015 Jul 21, 2015

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 22, 2015 Jul 22, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

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..

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

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


Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015

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)

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2015 Jul 23, 2015

Davide, I could kiss you. That's done it! Thank you for being so patient with me, I really appreciate it.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jul 23, 2015 Jul 23, 2015
LATEST

You're welcome That's what the forums are for!

Best regards


-Davide

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines