Use JQuery or JSON or Ajax in scripts


#1

Is there a way to use a JSON file? We tried with jQuery but it’s not included.
Is there a way to include jQuery in an Entity script?
Maybe Ajax?

We’re managing a way to show different text based on interface settings (different languages - different text).

Thanks for the support :slight_smile:


#2

Sometimes you’ll need to author a script to accomplish this. Others (like plain .js files) you can load from disk


#3

Can you better explain this? I tried to add jQuery via persistent script (https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js) but log reports:

[06/29 18:58:02] [WARNING] [UncaughtException] ReferenceError: Can’t find variable: jQuery in (EntityID:{1af9ea01-d0c9-4bee-b3c0-1aac36f5b024}, https://dl.dropboxusercontent.com/u/7497773/lastScene.js):23


#4

Heyja,

Jquery may need to be modified, but you can include it in the script, but id advice against using it unless you are making something with QML. jquery is meant for manipulating (quering) webpage DOM and CSS and isnt build for HighFidelity. So there is way too much stuff, that necessarily is not needed, especually since jquery is most of a patch work solution to fix alot of cross browser (cough, IE) issues, which have been slowly adressed by modern JS engines and polyfills.

Instead I suggest getting familiar with the functions of Javascript and its native functionality.

AJAX (Asyncronous Javascript and XML) and JSON (Javascript Object Notation) are both standard with Javascript related functions thus come built in with javascript (qT) engine that runs in High Fidelity.

So todo AJAX, simply use XmlHttpRequest and you can use the related functions to read JSON. You may assume that most functionality from modern JS is present, so you do not have to do any polyfill or browser checks.

Hopefully that helps.
Feel free to ask for more examples.


#5

This is no DOM ===O .


#6

We tried with xhr in an entityScript.

function getJSON(url){
	print("getJSON " + url);
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onreadystatechange = function() {  //callback
    	print('req status:: ' + JSON.stringify(xhr.status))
    	print('req status2:: ' + xhr.status)
    	if (xhr.readyState == 4 && xhr.status == 200) {
                var result = JSON.parse(xhr.responseText);
                print('parsed JSON: ', result);
        }
    };
    xhr.send();
};

The JSON url is https://dl.dropboxusercontent.com/u/7497773/dialogs.json but the XHR status returns 999 :fearful:

How can we load it from local disk?


#7

The 999 is a “Request Denied” response used by some companies to deny responses.

If I recall correctly, Dropbox does occasionally block the requests from Accounts that have multiple files downloaded at the same time. This is especially apparent if you have multiple assets in the same dropbox account and you load them up in High Fidelity.

You could use github raw for it… but I suspect theyll do the same eventually.

Unfortunately, JS XHR requests does have the security limitation that you cannot load local files with it. But I havent tested it in High Fidelity just yet. Basically just file://<path> should be sufficient, but it may throw you a XSS error in High Fidelity. Only way to go forward is use another service instead.
You could try using ATP instead. Not sure if that works with scripts / json yet, so Ill try it out later today when I get home and give you instructions if it works…

As a side note, your result is undefined, you may want to switch result to myArray.


#8

Yes, result undefined is a copy/paste mistake :wink:


#9

I just tested this with ATP and file protocols, unfortunately they do not work over XHR. I submitted a Github Issue Feature Request to support XHR requests over atp, as that has slightly less security issues that giving file access, but still allow files to be hosted using atp.

For now I suggest using another host for the JSON files, or setting up a simple NodeJS/Flask/Go/ http host solution on the same host as your domain.


#10

Yeah I hope someone pursues that feature request because having the different types of URLs “just work” everywhere URLs should would be awesome.

In this case, is the XHR interface itself critical though?

Because at least with ATP the remote data can be retrieved another way:

Assets.downloadData('atp:012345689abcdef',
  function(data) { print('retrieved ' + data.length + ' bytes of ATP data'); }
)

This API method doesn’t seem to support error handling callbacks yet, so what I do for now is wrap it in a Promise + timeout chain (this requires using a polyfill for Promise support and assumes after N seconds some kind of error has occurred):

function fetchATP(atpURL) {
    return new Promise(function(resolve, reject) {
      Assets.downloadData(atpURL, resolve);
    }).timeout(5000);
}

fetchATP('atp:012345689abcdef')
  .then(function(data) { print('data', data.length); })
  ['catch'](function(err) { print('error retrieving ATP: ', err); });

#11

That is good information. Didnt know that existed. Then the XHR change is unneccessary. File protocol should not work though as that is a big security issue.
In anycase,
But it would still be good to have an error message on the protocol appear in the console with suggestions what to use instead


#12

Just searched through HF’s docs for Assets.downloadData and nada. Grr…

Looked in HF’s git repo and yes: https://github.com/highfidelity/hifi/search?utf8=✓&q=downloadData

there is some documentation there. I thought you folks were using some codedoc tools like doxygen as it would help us become productive,especially if an auto-generated list were present in the docs section.


#13

To be fair, it might not be an “official” API method yet – I only discovered it in passing the other day when running
print(Object.keys(Assets).join('\n')) in the JS Console. :nerd:


#14

Understood, but there are quite a number of undocumented APIs used in example scripts. I do see effort to update the docs and give context to the calls, but it sure would help to have an auto-generated API list dump.


#15

currentAPI.js lists most of the API. (Includes Assets.downloadData().) If you run it in the Script Editor window it outputs a clean list to the script editor’s debug window (i.e., without the program log’s datestamp and “[DEBUG]” at the start of each line).


#16

I went through @humbletim code and tried it in world. No dice. Perhaps us scripters should test the code we post before we post in the environment its supposed to work in :slight_smile: .

According to source code it also only accepts hash, and not the mapped values from the Asset Browser.

Probably undocumented because it doesnt work. Also Qt doesnt support ES6 conventions only ES5, so Promises would be needed to be polyfilled.


#17

Aww man, sorry to get you all excited and then have it not work out. Although I did test the code and “worked for me” ;). (however – see below re: Promises)

I only use raw ATP hashes right now… so can’t speak much to the mapped ATP names aspect.

You might want to check out some of the internal ATP methods being referenced from JavaScript in the Asset-related QML dialogs – like functions for setting up the mapped names and removing ATP assets, which might be useful to suggest they expose to Scripting in general at some point.

Granted it might not work the way you want yet, but I’ve been successfully using ATP for round-trip storage from Scrpiting for a while now. Before that the only place I could find to “stash” dynamic JSON blobs was Interface.ini (by way of the Settings API).

For Promises I’m actually using bluebird… but there’s also a lighter-weight polyfill at /script-archive/libraries/promise.js (that at least used to work from within Interface).

If interest I could put my polyfills for bluebird and SystemJS into a gist somewhere.


#18

I was using raw hash as well. Infact i was using the example (archive script ) and the callback simply didnt work.


#19

… not sure if you are referring to a Promise resolve not working or Assets.XXXLoadData callback not firing, but in any event here’s a round-trip ATP example that works for me:

Assets.uploadData(JSON.stringify(MyAvatar.position)+'@'+new Date, function(url) { 
  print('got back ATP url:', url);
  verify(url);
});

function verify(url) {
  print('verifying ATP url: ', url);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
  Assets.downloadData(url, function(data) {
    print('data:', data);
  });
}

debug log output:

[DEBUG] Successfully uploaded asset to asset-server - SHA256 hash is  "b0b530961b7015d28bfdf8261b3406394f4212fd87391f680f7985b91c559fe3"
[DEBUG] "atp:b0b530961b7015d28bfdf8261b3406394f4212fd87391f680f7985b91c559fe3" saved to disk cache
[DEBUG] script:print()<< got back ATP url: atp:b0b530961b7015d28bfdf8261b3406394f4212fd87391f680f7985b91c559fe3
[DEBUG] script:print()<< verifying ATP url:  atp:b0b530961b7015d28bfdf8261b3406394f4212fd87391f680f7985b91c559fe3
[DEBUG] "atp:b0b530961b7015d28bfdf8261b3406394f4212fd87391f680f7985b91c559fe3" loaded from disk cache.
[DEBUG] script:print()<< data: {"x":6.442423909902573e-7,"y":-0.8926454782485962,"z":-0.00003089383244514465}@Fri Jul 01 2016 13:22:56 GMT-0400 (EDT)

#20

Specifically i was using Assets. downloadData via just running https://github.com/highfidelity/hifi/blob/5042561e80367ee2ebefa7d56728246e2d037e9d/script-archive/example/assetsExample.js technically same as above. upload worked, download callback didnt.

Your script however did. so it is a big damn puzzle.

Regardless, if we use asset browser, we can retrive the hash.