19 Replies Latest reply on Jul 23, 2015 10:41 AM by DBarranca

    Using XMLHttpRequest within Photoshop script

    Whatevermajorloser Level 1

      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. Re: Using XMLHttpRequest within Photoshop script
          xbytor2 Level 4

          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.

          • 2. Re: Using XMLHttpRequest within Photoshop script
            Whatevermajorloser Level 1

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

            • 4. Re: Using XMLHttpRequest within Photoshop script
              Whatevermajorloser Level 1

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

              • 5. Re: Using XMLHttpRequest within Photoshop script
                DBarranca Level 4

                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

                • 6. Re: Using XMLHttpRequest within Photoshop script
                  Whatevermajorloser Level 1

                  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.

                  • 7. Re: Using XMLHttpRequest within Photoshop script
                    DBarranca Level 4

                    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

                    • 8. Re: Using XMLHttpRequest within Photoshop script
                      Whatevermajorloser Level 1

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

                      • 9. Re: Using XMLHttpRequest within Photoshop script
                        DBarranca Level 4

                        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

                        • 10. Re: Using XMLHttpRequest within Photoshop script
                          Whatevermajorloser Level 1

                          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


                           

                          • 11. Re: Using XMLHttpRequest within Photoshop script
                            DBarranca Level 4

                            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[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==='string'){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());;
                            
                            

                             

                            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

                            • 12. Re: Using XMLHttpRequest within Photoshop script
                              Whatevermajorloser Level 1

                              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[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==='string'){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());;  
                              
                                  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.

                              • 13. Re: Using XMLHttpRequest within Photoshop script
                                DBarranca Level 4

                                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

                                • 14. Re: Using XMLHttpRequest within Photoshop script
                                  Whatevermajorloser Level 1

                                  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.

                                  • 15. Re: Using XMLHttpRequest within Photoshop script
                                    DBarranca Level 4

                                    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

                                    • 16. Re: Using XMLHttpRequest within Photoshop script
                                      Whatevermajorloser Level 1

                                      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?

                                      • 17. Re: Using XMLHttpRequest within Photoshop script
                                        DBarranca Level 4

                                        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)

                                        • 18. Re: Using XMLHttpRequest within Photoshop script
                                          Whatevermajorloser Level 1

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

                                          • 19. Re: Using XMLHttpRequest within Photoshop script
                                            DBarranca Level 4

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

                                            Best regards


                                            -Davide