summaryrefslogtreecommitdiffhomepage
path: root/modules
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2018-11-16 19:31:07 +0100
committerJo-Philipp Wich <jo@mein.io>2018-11-16 21:11:34 +0100
commita1fff6a9eebbfe3e9b3e5401645b1db4bdf37735 (patch)
tree50a4c8e79908823b8a62dc5b819c673c107a9b18 /modules
parent425a02734e28ec41403511aa1c46cbe489930ff1 (diff)
luci-base: xhr.js: rework class, handle expired session
Drop very old IE compat code, restructure class, align code style with other files and properly handle JSON mimetypes with charset trailer. Also detect session related 403 errors and show a modal prompting to re-login. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/xhr.js208
1 files changed, 95 insertions, 113 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/xhr.js b/modules/luci-base/htdocs/luci-static/resources/xhr.js
index 25a90e725..8292fcdb6 100644
--- a/modules/luci-base/htdocs/luci-static/resources/xhr.js
+++ b/modules/luci-base/htdocs/luci-static/resources/xhr.js
@@ -1,24 +1,65 @@
/*
* xhr.js - XMLHttpRequest helper class
- * (c) 2008-2010 Jo-Philipp Wich
+ * (c) 2008-2018 Jo-Philipp Wich <jo@mein.io>
*/
-XHR = function()
-{
- this.reinit = function()
- {
- if (window.XMLHttpRequest) {
- this._xmlHttp = new XMLHttpRequest();
+XHR.prototype = {
+ _encode: function(obj) {
+ obj = obj ? obj : { };
+ obj['_'] = Math.random();
+
+ if (typeof obj == 'object') {
+ var code = '';
+ var self = this;
+
+ for (var k in obj)
+ code += (code ? '&' : '') +
+ k + '=' + encodeURIComponent(obj[k]);
+
+ return code;
}
- else if (window.ActiveXObject) {
- this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+
+ return obj;
+ },
+
+ _response: function(callback, ts) {
+ if (this._xmlHttp.readyState !== 4)
+ return;
+
+ var status = this._xmlHttp.status,
+ login = this._xmlHttp.getResponseHeader("X-LuCI-Login-Required"),
+ type = this._xmlHttp.getResponseHeader("Content-Type"),
+ json = null;
+
+ if (status === 403 && login === 'yes') {
+ XHR.halt();
+
+ showModal(_('Session expired'), [
+ E('div', { class: 'alert-message warning' },
+ _('A new login is required since the authentication session expired.')),
+ E('div', { class: 'right' },
+ E('div', {
+ class: 'btn primary',
+ click: function() {
+ var loc = window.location;
+ window.location = loc.protocol + '//' + loc.host + loc.pathname + loc.search;
+ }
+ }, _('To login…')))
+ ]);
}
- else {
- alert("xhr.js: XMLHttpRequest is not supported by this browser!");
+ else if (type && type.toLowerCase().match(/^application\/json\b/)) {
+ try {
+ json = JSON.parse(this._xmlHttp.responseText);
+ }
+ catch(e) {
+ json = null;
+ }
}
- }
- this.busy = function() {
+ callback(this._xmlHttp, json, Date.now() - ts);
+ },
+
+ busy: function() {
if (!this._xmlHttp)
return false;
@@ -32,20 +73,18 @@ XHR = function()
default:
return false;
}
- }
+ },
- this.abort = function() {
+ abort: function() {
if (this.busy())
this._xmlHttp.abort();
- }
+ },
- this.get = function(url,data,callback,timeout)
- {
- this.reinit();
+ get: function(url, data, callback, timeout) {
+ this._xmlHttp = new XMLHttpRequest();
- var ts = Date.now();
- var xhr = this._xmlHttp;
- var code = this._encode(data);
+ var xhr = this._xmlHttp,
+ code = this._encode(data);
url = location.protocol + '//' + location.host + url;
@@ -60,83 +99,51 @@ XHR = function()
if (!isNaN(timeout))
xhr.timeout = timeout;
- xhr.onreadystatechange = function()
- {
- if (xhr.readyState == 4) {
- var json = null;
- if (xhr.getResponseHeader("Content-Type") == "application/json") {
- try { json = JSON.parse(xhr.responseText); }
- catch(e) { json = null; }
- }
-
- callback(xhr, json, Date.now() - ts);
- }
- }
-
+ xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.send(null);
- }
-
- this.post = function(url,data,callback,timeout)
- {
- this.reinit();
+ },
- var ts = Date.now();
- var xhr = this._xmlHttp;
- var code = this._encode(data);
+ post: function(url, data, callback, timeout) {
+ this._xmlHttp = new XMLHttpRequest();
- xhr.onreadystatechange = function()
- {
- if (xhr.readyState == 4) {
- var json = null;
- if (xhr.getResponseHeader("Content-Type") == "application/json") {
- try { json = JSON.parse(xhr.responseText); }
- catch(e) { json = null; }
- }
-
- callback(xhr, json, Date.now() - ts);
- }
- }
+ var xhr = this._xmlHttp,
+ code = this._encode(data);
xhr.open('POST', url, true);
if (!isNaN(timeout))
xhr.timeout = timeout;
+ xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(code);
- }
+ },
- this.cancel = function()
- {
- this._xmlHttp.onreadystatechange = function(){};
+ cancel: function() {
+ this._xmlHttp.onreadystatechange = function() {};
this._xmlHttp.abort();
- }
+ },
- this.send_form = function(form,callback,extra_values)
- {
+ send_form: function(form, callback, extra_values) {
var code = '';
- for (var i = 0; i < form.elements.length; i++)
- {
+ for (var i = 0; i < form.elements.length; i++) {
var e = form.elements[i];
- if (e.options)
- {
+ if (e.options) {
code += (code ? '&' : '') +
form.elements[i].name + '=' + encodeURIComponent(
e.options[e.selectedIndex].value
);
}
- else if (e.length)
- {
+ else if (e.length) {
for (var j = 0; j < e.length; j++)
if (e[j].name) {
code += (code ? '&' : '') +
e[j].name + '=' + encodeURIComponent(e[j].value);
}
}
- else
- {
+ else {
code += (code ? '&' : '') +
e.name + '=' + encodeURIComponent(e.value);
}
@@ -147,46 +154,25 @@ XHR = function()
code += (code ? '&' : '') +
key + '=' + encodeURIComponent(extra_values[key]);
- return(
- (form.method == 'get')
- ? this.get(form.getAttribute('action'), code, callback)
- : this.post(form.getAttribute('action'), code, callback)
- );
- }
-
- this._encode = function(obj)
- {
- obj = obj ? obj : { };
- obj['_'] = Math.random();
-
- if (typeof obj == 'object')
- {
- var code = '';
- var self = this;
-
- for (var k in obj)
- code += (code ? '&' : '') +
- k + '=' + encodeURIComponent(obj[k]);
-
- return code;
- }
-
- return obj;
+ return (form.method == 'get'
+ ? this.get(form.getAttribute('action'), code, callback)
+ : this.post(form.getAttribute('action'), code, callback));
}
}
-XHR.get = function(url, data, callback)
-{
+XHR.get = function(url, data, callback) {
(new XHR()).get(url, data, callback);
}
-XHR.poll = function(interval, url, data, callback, post)
-{
+XHR.post = function(url, data, callback) {
+ (new XHR()).post(url, data, callback);
+}
+
+XHR.poll = function(interval, url, data, callback, post) {
if (isNaN(interval) || interval < 1)
interval = 5;
- if (!XHR._q)
- {
+ if (!XHR._q) {
XHR._t = 0;
XHR._q = [ ];
XHR._r = function() {
@@ -213,8 +199,7 @@ XHR.poll = function(interval, url, data, callback, post)
return e;
}
-XHR.stop = function(e)
-{
+XHR.stop = function(e) {
for (var i = 0; XHR._q && XHR._q[i]; i++) {
if (XHR._q[i] === e) {
e.xhr.cancel();
@@ -226,10 +211,8 @@ XHR.stop = function(e)
return false;
}
-XHR.halt = function()
-{
- if (XHR._i)
- {
+XHR.halt = function() {
+ if (XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@@ -242,10 +225,8 @@ XHR.halt = function()
}
}
-XHR.run = function()
-{
- if (XHR._r && !XHR._i)
- {
+XHR.run = function() {
+ if (XHR._r && !XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@@ -260,9 +241,10 @@ XHR.run = function()
}
}
-XHR.running = function()
-{
+XHR.running = function() {
return !!(XHR._r && XHR._i);
}
+function XHR() {}
+
document.addEventListener('DOMContentLoaded', XHR.run);