File: //opt/code/src/ajax.js
//===== AJAX
/*
* Get cross browser xhr object
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is>
* More: https://gist.github.com/993585
*/
var ajax = function()
{
for(var a=0; a<4; a++)
try
{
return a // try returning
? new ActiveXObject( // a new ActiveXObject
[ // reflecting
, // (elided)
"Msxml2", // the various
"Msxml3", // working
"Microsoft" // options
][a] + // for Microsoft implementations, and
".XMLHTTP" // the appropriate suffix,
) // but make sure to
: new XMLHttpRequest;// try the w3c standard first, and
}
catch(e){} // ignore when it fails.
};
// DATA must be encoded - EncodeURIComponent
function ajaxReq(method, url, ok_cb, err_cb, data, instance, prog_cb)
{
var xhr = ajax(), timeout = null;
xhr.onreadystatechange = function my_ajax_ready()
{
if (xhr.readyState != 4) return;
if (xhr.status >= 200 && xhr.status < 300)
{
//console.log("XHR done:", method, url, "->", xhr.status);
if(instance && instance.$root) instance.$root.admin = xhr.getResponseHeader('X-ADMIN');
ok_cb(xhr.responseText, xhr);
}
else
{
if(xhr.status != 401) console.log("XHR ERR: " + method, url + " ==> HTTP Status " + xhr.status);
if(xhr.status != 404 && xhr.status != 401)
{
console.log(xhr.responseText);
}
err_cb(xhr.status, xhr.responseText);
}
xhr = null;
};
xhr.onerror = function my_ajax_error()
{
err_cb(-2,'Network error');
};
if(typeof prog_cb == "function")
{
var fprog_dn = function my_ajax_progress_dn(e)
{
if (e.lengthComputable) prog_cb.call(instance,false,e.loaded,e.total);
}, fprog_up = function my_ajax_progress_up(e)
{
if (e.lengthComputable) prog_cb.call(instance,true,e.loaded,e.total);
};
xhr.onprogress = fprog_dn;
xhr.onloadstart = fprog_dn;
xhr.onloadend = fprog_dn;
xhr.upload.onprogress = fprog_up;
xhr.upload.onloadstart = fprog_up;
xhr.upload.onloadend = fprog_up;
}
// must be after event handlers
xhr.open(method, url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(), true);
if(data instanceof FormData)
{
// do not add any header - browser will do it automatically and handle the case of multiple files uploaded
}
else if(isObject(data) || isArray(data))
{
xhr.setRequestHeader("Content-Type", "application/json");
data = JSON.stringify(data);
}
else if(data instanceof File)
{
xhr.overrideMimeType(data.type);
}
else xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
try
{
if(data !== undefined) xhr.send(data);
else xhr.send();
}
catch(err)
{
console.log("XHR EXC :", method, url, "->", err);
err_cb(599, err);
}
return xhr;
}
function dispatchJson(resp, ok_cb, err_cb, req)
{
var j;
try
{
j = JSON.parse(resp);
}
catch(err)
{
if(resp.indexOf('"error":') != -1)
{
resp = resp.replace(/\/var\/.+\/api/g,'/api');
console.log(resp);
err_cb(597, '<pre style="white-space: pre-wrap;">'+resp.replace(/(?:\r\n|\r|\n)/g,'<br/>')+'</pre>');
}
else
{
console.log(err + " ==> IN: " + resp);
err_cb(597, "JSON parse error: " + err);
}
return;
}
ok_cb(j, req);
}
// DATA must be encoded - EncodeURIComponent
function ajaxSpin(method, url, ok_cb, err_cb, data, instance, prog_cb)
{
instance.$root.showSpin();
return ajaxReq(method, url,
function(resp, req)
{
instance.$root.hideSpin();
ok_cb(resp, req);
},
function(status, statusText)
{
instance.$root.hideSpin();
err_cb(status, statusText);
},
data, instance, prog_cb
);
}
// DATA must be encoded - EncodeURIComponent
function ajaxJsonSpin(method, url, ok_cb, err_cb, data, instance, prog_cb)
{
return ajaxSpin(method, url,
function(resp, req)
{
dispatchJson(resp, ok_cb, err_cb, req);
},
err_cb, data, instance, prog_cb
);
}
// ======================
// handle PHP run-time errors
function checkReply(js,err_cb)
{
if(js!=null && typeof js == "object" && typeof js.error == "object" && js.error.type != '')
{
var tabs = 1, delim = '#', spc = ' ', msg = '', err = js.error;
if(err.text != '') msg += err.text + "\n";
if(err.trace.length)
{
while(err.trace.length)
{
var tr = err.trace.shift();
msg += Array(tabs+1).join(delim) + ' ' + tr.line + ', ' + tr.file + "\n";
tabs++;
msg += Array(tabs+1).join(spc) + (typeof tr['class'] != 'undefined' ? tr['class'] + '::' : '') + tr['function'].name + '(' + tr['function'].args.join(', ')+')' + "\n";
}
}
msg += delim + ' ' + err.line + ', ' + err.file + "\n";
if(typeof err.sql == "object")
{
err = err.sql;
msg += "\n" + 'STATE = ' + err.state + "\n"
+ (err.text ? err.text + "\n" : '')
+ (err.detail ? err.detail + "\n" : '')
+ (err.context ? err.context : '');
}
console.log(msg);
err_cb.call(this,598,'<pre style="white-space: pre-wrap;">'+msg.replace(/(?:\r\n|\r|\n)/g,'<br/>')+'</pre>');
//err_cb.call(this,598,msg);
return false;
}
return true;
}
// expects THIS to point to Vue/Component instance
function cb_success(ok_cb,err_cb,resp,xhr)
{
if(checkReply.call(this,resp,err_cb))
{
ok_cb.call(this,resp);
}
}
function checkJSON(js)
{
var j;
try
{
j = JSON.parse(js);
}
catch(err)
{
return js;
}
if(typeof j == "object" && typeof j.msg != "undefined") return j.msg;
else return js;
}
// expects THIS to point to Vue/Component instance
function cb_fail(url,err_cb,skip_401,stat,resp)
{
var api = url.match(/^api(\/[^?]+)/);
if(api!=null) api = api[1]; else api = 'XYZ';
if(stat == 401)
{
if(skip_401) err_cb.call(this,stat,resp); // used only by PING in VueRouter.beforeEach
else
{
if(this.$route.path === '/login') err_cb.call(this,stat,resp);
else this.$router.push('/login');
}
}
else if(stat == 404)
{
err_cb.call(this,stat,'API endpoint for <b>'+api+'</b> was not found');
}
else
{
err_cb.call(this,stat,checkJSON(resp));
}
}
// ======================
export default
{
// expects THIS to point to Vue/Component instance
ajax_get(instance,url,ok_cb,err_cb,prog_cb,skip_401)
{
return ajaxJsonSpin("GET",url, cb_success.bind(instance,ok_cb,err_cb), cb_fail.bind(instance,url,err_cb,skip_401), null, instance, prog_cb);
},
ajax_post(instance,url,ok_cb,err_cb,data,prog_cb)
{
return ajaxJsonSpin("POST",url, cb_success.bind(instance,ok_cb,err_cb), cb_fail.bind(instance,url,err_cb,false), data, instance, prog_cb);
},
}