I'm using a basic loader to load some js on demand in my pages. On one or two machines (I don't have access to those machines) I'm getting errors like "Uncaught SyntaxError: Invalid or unexpected token in xxx", or "Uncaught SyntaxError: Unexpected token ILLEGAL".
Of course the source files are ok and this is working on thousands of clients. For sure this must be some antivirus that it is modifying my js files, but I want to know what's happening to see if I can prevent or at least notify the client about this.
My problem is that I'm loading the scripts using XMLHttpRequest, and onload I'm attaching the text into the header, like
request.onload = function () {
var txt = request.responseText + "\n//# sourceURL=" + file + "\n";
try {
var script = domDocument.createElement("script"),
head = domDocument.head || domDocument.getElementsByTagName("head")[0];
script.defer = true;
script.text = txt;
head.appendChild(script);
} catch (e) {
domWindow.onerror("Error loading js.", file, 0, 0, e, txt);
}
request.onload = function () { };
};
Where "file" points to the file on the server. The thing is, that the catch never executes as the browser seems to happily execute the above code and THEN parse/throws the actual error.
I would like to know if I can, in some way, catch the error inside my loading script so I can report back what the browser really received instead of my js.
try catch alone can not catch syntax errors, which occur on parsing time. As far as I know, the only way to catch a syntax error is to use eval.
try{
var txt = request.responseText + "\n//# sourceURL=" + file + "\n";
eval(txt);
var script = domDocument.createElement("script"),
head = domDocument.head || domDocument.getElementsByTagName("head")[0];
script.defer = true;
script.text = txt;
head.appendChild(script);
}catch(er){
console.log(er);
}
I don't recommend this approach. It's better to just load the script file via its url then listening to the error events on both the script tag and window object;
window.onerror = function(msg, url, lineNo, columnNo, error){
console.log(msg, url, lineNo, columnNo, error);
}
var script = domDocument.createElement("script"),
head = domDocument.head || domDocument.getElementsByTagName("head")[0];
script.defer = true;
script.onerror = function(er){
console.log(er);
};
script.onload = function(){
console.log('script loaded');
};
script.src = '/path/to/script.js';
head.appendChild(script);
Related
Is there any way to capture all type of console errors?
Actually, I want to store all console errors to the database so I can fix serious issues of my PHP website.
Here is my current code to catch errors but this code is not capturing internal server errors and some other kind of errors like
www-widgetapi.js:99 Failed to execute 'postMessage' on 'DOMWindow':
The target origin provided ('https://www.youtube.com') does not match
the recipient window's origin
Current script that I have
function ErrorSetting(msg, file_loc, line_no) {
var e_msg=msg;
var e_file=file_loc;
var e_line=line_no;
var error_d = "Error in file: " + file_loc +
"\nline number:" + line_no +
"\nMessage:" + msg;
if(logJsErrors){
theData = "file="+file_loc+"&line="+line_no+"&err="+msg;
ajaxCtrl(
function(){
return true;
},Helpers.RootURL()+"/logs/index",theData
);
}
if(isDebugging){
console.error(error_d);
}
return true;
}
window.onerror = ErrorSetting;
I really appreciate your efforts.
Thank you :)
You could perform a kind of prototype of console.log, when you call console.log an ajax is fired, so you could do somethig like this:
(function () {
var postCons = console.log;
console.log = function (err) {
var xhttp = new XMLHttpRequest();
postCons.apply(this, arguments);
xhttp.open("POST", "log.php", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {//you could avoid this step
alert(this.responseText);//response from php
}
};
xhttp.send("data="+encodeURI(err));
}
})()
In log.php you could do whatever you want with $_POST['data'] , you could use something like urldecode($_POST['data']) .
I have a script that capture the errors and send them to my server. But I'm worried that if I have a lot of users, and every user gets a couple errors, it may collapse my server.
This is my code:
window.onerror = function(msg, url, num) {
try {
var clientSideErrorInfo = {
message: msg,
url: url,
num: num
};
var xhr = new XMLHttpRequest();
xhr.open("POST", 'http://domain/api/v1/browser/', true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(clientSideErrorInfo));
console.log(clientSideErrorInfo)
} catch (e) {
console.log(e);
// Don't allow for infinitely recursively unhandled errors
return true;
}
};
Is there a way to send a group of logs instead of sending them one by one?
Thanks
Instead of sending the error at the moment you get it, you could collect all errors into a global variable and you send them using an interval. This way you can limit how many errors you want to send at a same time and you could increase your interval as well.
var errorSend = {};
errorSend.listErrors = [];
errorSend.maxErrors = 50;
errorSend.interval = 100;
window.onerror = function(msg, url, num) {
var clientSideErrorInfo = {
message: msg,
url: url,
num: num
};
listErrors.push(clientSideErrorInfo);
console.log(clientSideErrorInfo)
};
function sendErrors() {
if (errorSend.listErrors>errorSend.maxErrors) {
console.log("Too many errors to send");
return;
}
var errors = {list: errorSend.listErrors};
var xhr = new XMLHttpRequest();
xhr.open("POST", 'http://domain/api/v1/browser/', true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(errors));
}
setInterval(sendErrors,errorSend.interval);
Something very simple,umm:
var body =[];
setInterval(function(){
//Using a copy of the error queue
let batch = body.slice();
var xhr = new XMLHttpRequest();
xhr.open("POST", 'http://domain/api/v1/browser/', true);
xhr.setRequestHeader("Content-Type", "application/json");
let myJson = JSON.stringify(body);
xhr.send({
data:{
param: myJson
}
});
//Updating the main queue to contain only unsent error messages
body=body.slice(batch.length,body.length);
},time_you_specify);
window.onerror = function(msg, url, num) {
try {
var clientSideErrorInfo = {
message: msg,
url: url,
num: num
};
body.push(clientSideErrorInfo);
console.log(clientSideErrorInfo)
} catch (e) {
console.log(e);
// Don't allow for infinitely recursively unhandled errors
return true;
}
};
Is not problem for this job but if you have a lot of errors in you web app can be problem. First you will need to setup your javascript code to the perfect state. In that case you idea is good ( catch every possible error ) . Just put it in some table on server .
Heres from mozilla dev site about param :
window.onerror = function (msg, url, lineNo, columnNo, error) {
var string = msg.toLowerCase();
var substring = "script error";
if (string.indexOf(substring) > -1){
alert('Script Error: See Browser Console for Detail');
} else {
var message = [
'Message: ' + msg,
'URL: ' + url,
'Line: ' + lineNo,
'Column: ' + columnNo,
'Error object: ' + JSON.stringify(error)
].join(' - ');
alert(message);
}
return false;
};
Very important use (userAgent) detectBrowser data , you must know which device is used also which browser is used -just add data intro your function. In 90% your client error will happend only on specific platforms . For example on android chrome ver < 23 ...
Please don't use interval for this kind of tasks , just on error catch event send error log to the server just like you already did!
It is better to use message queueing infrastructure, if you are expecting millions of messages
some sample
I want to send request parameters to other domain
I already know that Cross Scripting needs JsonP and I have used JsonP with Jquery ajax
but i do not figure out how to do Cross Scripting as using XMLHttpRequest
following code my basic XMLHttpRequest code.
i guess i need to chage xhr.setRequestHeader() and i have to add parsing code
please give me any idea
var xhr;
function createXMLHttpRequest(){
if(window.AtiveXObject){
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}else{
xhr = new XMLHttpRequest();
}
var url = "http://www.helloword.com";
}
function openRequest(){
createXMLHttpRequest();
xhr.onreadystatechange = getdata;
xhr.open("POST",url,true);
xhr.setRequestHeader("Content-Type",'application/x-www-form-urlencoded');
xhr.send(data);
}
function getdata(){
if(xhr.readyState==4){
if(xhr.status==200){
var txt = xhr.responseText;
alert(txt);
}
}
}
JSONP does not use XMLHttpRequests.
The reason JSONP is used is to overcome cross-origin restrictions of XHRs.
Instead, the data is retrieved via a script.
function jsonp(url, callback) {
var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
window[callbackName] = function(data) {
delete window[callbackName];
document.body.removeChild(script);
callback(data);
};
var script = document.createElement('script');
script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
document.body.appendChild(script);
}
jsonp('http://www.helloword.com', function(data) {
alert(data);
});
In interest of simplicity, this does not include error handling if the request fails. Use script.onerror if you need that.
I know you already got the answer for but if anyone else out here wanted an example of one using promises here's one.
function jsonp(url) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script')
const name = "_jsonp_" + Math.round(100000 * Math.random());
//url formatting
if (url.match(/\?/)) url += "&callback="+name
else url += "?callback="+name
script.src = url;
window[name] = function(data) {
resolve(data);
document.body.removeChild(script);
delete window[name];
}
document.body.appendChild(script);
});
}
var data = jsonp("https://www.google.com");
data.then((res) => {
console.log(res);
});
For google api I was forced to add callback and also v=1.0 parameter to url. Without v=1.0 parameter I get CORB error (for my version and also other answers code - however jQuery $.ajax with dataType: "jsonp" add this parameter - so I add it too and start working )
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://ajax.googleapis.com/ajax/services/feed/load?callback=jsonp1555427800677 with MIME type text/javascript. See https://www.chromestatus.com/feature/5629709824032768 for more details.
Below is promise version of my solution
function jsonp(url) {
return new Promise(function(resolve, reject) {
var s = document.createElement('script');
var f="jsonp"+(+new Date()), b=document.body;
window[f] = d=>{ delete window[f]; b.removeChild(s); resolve(d); };
s.src=`${url}${url.includes('?')?'&':'?'}callback=${f}&v=1.0`;
b.appendChild(s);
})
}
async function send() {
let r = await jsonp("http://ajax.googleapis.com/ajax/services/feed/load");
console.log(r);
}
<button onclick="send()">Send JSONP</button>
function JsonpHttpRequest(url, callback) {
var e = document.createElement('script');
e.src = url;
document.body.appendChild(script); // fyi remove this element later /assign temp class ..then .remove it later
//insetead of this you may also create function with callback value and use it instead
window[callback] = (data) => {
console.log(data); // heres you data
}
}
// heres how to use
function HowTouse(params) {
JsonpHttpRequest("http://localhost:50702/api/values/Getm?num=19&callback=www", "www")
}
You can not able to do Cross Scripting using XMLHttpRequest.If you want to cross domain with out Jquery, you must create a new script node and set the src attribute of it.
I have a site that loads information using the XMLHttpRequest when a user clicks a link. The system works well but I would like to be able to execute JavaScript gathered in this process.
This is a problem as I would like to download the scripts 'on demand' if it were, rather than loading them all when the page is loaded.
Thanks for any help
I believe the recommended solution is something like this:
function include(scriptUrl)
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", scriptUrl);
xmlhttp.onreadystatechange = function()
{
if ((xmlhttp.status == 200) && (xmlhttp.readyState == 4))
{
eval(xmlhttp.responseText);
}
};
xmlhttp.send();
}
Or something like it.
However, be wary of this approach. It's vulnerable to cross-site scripting, which can open you (and your users) up to all sorts of nastiness. You'll want to take suitable precautions.
Recently I found the answer (It works in Chrome, in another browsers it was not tested).
You can create dataURL string and put it into src attribute of script element.
var xhr = XMLHttpRequest(),
doc = document;
xhr.open('GET', pathToJSFile, true);
xhr.onload = function () {
var script = doc.createElement('script'),
base64 = 'data:application/javascript;base64,';
try {
base64 += btoa(data.responseText);
} catch (e) {
// script file may contain characters that not included in Latin1
var symbols = data.responseText.split('');
for (var i = 0, l = symbols.length; i < l; i++) {
var symbol = symbols[i];
// here we are trying to find these symbols in catch branch
try {
btoa(symbol);
} catch (e) {
var code = symbol.charCodeAt(0).toString(16);
while (code.length < 4) {
code = '0' + code;
}
// replace original symbol to unicode character
symbols[i] = '\\u' + code;
}
}
// create new base64 string from string with replaced characters
base64 += btoa(symbols.join(''));
} finally {
script.src = base64;
// run script
doc.body.appendChild(script);
}
};
xhr.send();
You can subscribe to xhr.onprogress to show progress bar.
Update. You can download your script file as blob, and then create blob-url.
var xhr = XMLHttpRequest(),
doc = document;
xhr.responseType = 'blob';
xhr.open('GET', pathToJSFile, true);
xhr.onload = function () {
var script = doc.createElement('script'),
src = URL.createObjectURL(xhr.response);
script.src = src;
doc.body.appendChild(script);
};
xhr.send();
You can run script downloaded in form of a string using
eval()
However I would recommend you to add new
<script src='..'></script>
to your document and have a callback which will be called when it will be downloaded. There are many utils and jquery plug-ins for that.
I had the challenge on a mobile web-project, the magic was to set "overrideMimeType".
This has been verified to work on Android 4.1 to Android 6.0.
var head = document.getElementsByTagName('head');
var injectedScript = document.createElement('script');
head[0].appendChild(injectedScript);
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
if (myRequest.readyState == 4 && myRequest.status == 200) {
injectedScript.innerHTML = myRequest.responseText;
//run a function in the script to load it
}
};
function start(){
myRequest.open('GET', 'javascript-url-to-download', true);
myRequest.overrideMimeType('application/javascript');
myRequest.send();
}
start();
You would need to use eval to parse the javascript from the XHR, note that this is EXTREMELY dangerous if you don't have absolute trust in the source of the javascript.
I'm trying to build a javascript bookmarklet for a special URL shortening service we've built at http://esv.to for shortening scripture references (i.e. "Matthew 5" becomes "http://esv.to/Mt5". The bookmarklet is supposed to do a GET request to http://api.esv.to/Matthew+5, which returns a text/plain response of http://esv.to/Mt5.
The code for the bookmarklet itself looks like this (expanded for readability):
var body = document.getElementsByTagName('body')[0], script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://esv.to/media/js/bookmarklet.js';
body.appendChild(script);
void(0);
The code from http://esv.to/media/js/bookmarklet.js looks like this:
(function() {
function shorten(ref, callback) {
var url = "http://esv.to/api/" + escape(ref);
var req = new XMLHttpRequest();
req.onreadystatechange = function shortenIt() {
if ( this.readyState == 4 && this.status == 200 ) {
callback(req.responseText);
};
};
req.open( "GET", url );
req.send();
};
function doBookmarklet() {
var ref = prompt("Enter a scripture reference or keyword search to link to:", "")
shorten(ref, function (short) {
prompt("Here is your shortened ESV URL:", short);
});
};
doBookmarklet();
})();
When called from http://esv.to itself, the bookmarklet works correctly. But when used on another page, it does not. The strange thing is, when I watch the request from Firebug, the response is 200 OK, the browser downloads 17 bytes (the length of the returned string), but the response body is empty! No error is thrown, just an empty responseText on the XmlHttpRequest object.
Now, according to Ajax call from Bookmarklet, GET shouldn't violate the same origin policy. Is this a bug? Is there a workaround?
Cross-site XMLHttpRequests can only be done in browsers that implement the W3C Cross-Origin Resource Sharing spec and if the server returns the appropriate access control headers (see MDC article), e.g.:
Access-Control-Allow-Origin: *
But this is not implemented by all browsers. The only sure-fire way to do cross-site requests is to use JSONP, for (untested) example:
(function() {
function shorten(ref, callback){
var callbackFuncName = 'esvapiJSONPCallback' + (new Date()).valueOf();
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "http://esv.to/api/" + escape(ref) + "?callback=" + callbackFuncName;
window[callbackFuncName] = function(shorturl){
script.parentNode.removeChild(script);
window.callbackFuncName = null;
delete window[callbackFuncName];
callback(shorturl);
};
document.getElementsByTagName("head")[0].appendChild(script);
}
var ref = prompt("Enter a scripture reference or keyword search to link to:", "");
shorten(ref, function(shorturl) {
prompt("Here is your shortened ESV URL:", shorturl);
});
})();
When the server sees the callback parameter it would then need to return text/javascript instead of text/plain, and the response body would need to be wrapped in an invocation of the provided callback, for example:
<?php
#... after $shorturl is set ...
if(isset($_GET['callback'])){
header('Content-Type: text/javascript');
$callback = preg_replace('/\W+/', '', $_GET['callback']); #sanitize
print $callback . "(" . json_encode($shorturl) . ");";
}
else {
header("Content-Type: text/plain");
print $shorturl;
}
?>