288 lines
11 KiB
JavaScript
288 lines
11 KiB
JavaScript
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
|
/*@
|
|
Ox.Request <o> Basic request handler object
|
|
FIXME: options is not a property, just documenting defaults
|
|
options <o> Options object
|
|
timeout <n|60000> request timeout
|
|
type <s|"POST"> request type, possible values POST, GET, PUT, DELETE
|
|
url <s> request url
|
|
@*/
|
|
|
|
Ox.Request = function(options) {
|
|
|
|
var cache = {},
|
|
//dfd = $.Deferred(),
|
|
pending = {},
|
|
requests = {},
|
|
self = {
|
|
options: Ox.extend({
|
|
timeout: 60000,
|
|
type: 'POST',
|
|
url: '/api/'
|
|
}, options)
|
|
};
|
|
|
|
return {
|
|
/*@
|
|
cancel <f> cancel pending requests
|
|
() -> <u> cancel all requests
|
|
(fn) -> <u> cancel all requests where function returns true
|
|
(id) -> <u> cancel request by id
|
|
@*/
|
|
cancel: function() {
|
|
if (arguments.length == 0) {
|
|
// cancel all requests
|
|
requests = {};
|
|
} else if (Ox.isFunction(arguments[0])) {
|
|
// cancel with function
|
|
Ox.forEach(requests, function(req, id) {
|
|
if (arguments[0](req)) {
|
|
delete requests[id];
|
|
}
|
|
});
|
|
} else {
|
|
// cancel by id
|
|
delete requests[arguments[0]];
|
|
}
|
|
},
|
|
/*@
|
|
clearCache <f> clear cached results
|
|
() -> <u> ...
|
|
@*/
|
|
clearCache: function(query) {
|
|
if (!query) {
|
|
cache = {};
|
|
} else {
|
|
cache = Ox.filter(cache, function(val, key) {
|
|
return key.indexOf(query) == -1;
|
|
});
|
|
}
|
|
},
|
|
|
|
_leakCache: function() {
|
|
return cache;
|
|
},
|
|
|
|
/*@
|
|
options <f> get/set options
|
|
() -> <o> get options
|
|
(options) -> <o> set options
|
|
options <o> Options Object
|
|
@*/
|
|
options: function(options) {
|
|
return Ox.getset(self.options, options, function() {}, this);
|
|
},
|
|
|
|
/*@
|
|
requests <f> pending requests
|
|
() -> <n> returns number of requests
|
|
@*/
|
|
requests: function() {
|
|
return Ox.len(requests);
|
|
},
|
|
|
|
/*@
|
|
send <f> send request
|
|
(options) -> <n> returns request id
|
|
options <o> Options Object
|
|
age <n|-1> cache age
|
|
id <n|Ox.uid()> request id
|
|
timeout <n|self.options.timeout> overwrite default timeout
|
|
type <n|self.options.timeout> overwrite default type
|
|
url <n|self.options.timeout> overwrite default url
|
|
@*/
|
|
send: function(options) {
|
|
|
|
var options = Ox.extend({
|
|
age: -1,
|
|
callback: null,
|
|
id: Ox.uid(),
|
|
timeout: self.options.timeout,
|
|
type: self.options.type,
|
|
url: self.options.url
|
|
}, options),
|
|
req = JSON.stringify({
|
|
url: options.url,
|
|
data: options.data
|
|
});
|
|
|
|
if (pending[options.id]) {
|
|
setTimeout(function() {
|
|
Ox.Request.send(options);
|
|
}, 0);
|
|
} else {
|
|
requests[options.id] = {
|
|
url: options.url,
|
|
data: options.data
|
|
};
|
|
if (cache[req] && (
|
|
options.age == -1
|
|
|| options.age > +new Date() - cache[req].time
|
|
)) {
|
|
setTimeout(function() {
|
|
callback && callback(cache[req].data);
|
|
}, 0);
|
|
} else {
|
|
pending[options.id] = true;
|
|
$.ajax({
|
|
complete: complete,
|
|
data: options.data,
|
|
//dataType: 'json',
|
|
timeout: options.timeout,
|
|
type: options.type,
|
|
url: options.url
|
|
});
|
|
}
|
|
}
|
|
|
|
function callback(data) {
|
|
if (requests[options.id]) {
|
|
delete requests[options.id];
|
|
options.callback && options.callback(data);
|
|
}
|
|
}
|
|
|
|
function debug(request) {
|
|
var $iframe = $('<iframe>')
|
|
.css({ // fixme: should go into a class
|
|
width: 768,
|
|
height: 384
|
|
}),
|
|
$dialog = Ox.Dialog({
|
|
title: 'Application Error',
|
|
buttons: [
|
|
Ox.Button({
|
|
title: 'Close'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
$dialog.close();
|
|
}
|
|
})
|
|
],
|
|
content: $iframe,
|
|
width: 768,
|
|
height: 384
|
|
})
|
|
.open(),
|
|
iframe = $iframe[0].contentDocument || $iframe[0].contentWindow.document;
|
|
iframe.open();
|
|
iframe.write(request.responseText);
|
|
iframe.close();
|
|
}
|
|
|
|
function complete(request) {
|
|
var data;
|
|
try {
|
|
data = JSON.parse(request.responseText);
|
|
} catch (err) {
|
|
try {
|
|
data = {
|
|
status: {
|
|
code: request.status,
|
|
text: request.statusText
|
|
}
|
|
};
|
|
} catch (err) {
|
|
data = {
|
|
status: {
|
|
code: '500',
|
|
text: 'Unknown Error'
|
|
}
|
|
};
|
|
}
|
|
}
|
|
if (data.status.code == 200 || data.status.code == 404) {
|
|
// we have to include 404 so we can
|
|
// test for the existence of things
|
|
cache[req] = {
|
|
data: data,
|
|
time: Ox.getTime()
|
|
};
|
|
callback(data);
|
|
} else if (data.status.code >= 400 && data.status.code < 500) {
|
|
var $dialog = Ox.Dialog({
|
|
buttons: [
|
|
Ox.Button({
|
|
id: 'close',
|
|
title: 'Close'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
$dialog.close();
|
|
}
|
|
})
|
|
],
|
|
content: Ox.Element()
|
|
.append(
|
|
$('<img>')
|
|
.attr({src: Ox.UI.PATH + 'png/icon128.png'})
|
|
.css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'})
|
|
)
|
|
.append(
|
|
Ox.Element()
|
|
.css({position: 'absolute', left: '96px', top: '16px', width: '256px'})
|
|
.html('Sorry, you have made an unauthorized request.')
|
|
),
|
|
height: 192,
|
|
keys: {enter: 'close', escape: 'close'},
|
|
title: Ox.toTitleCase(data.status.text),
|
|
width: 368
|
|
})
|
|
.open();
|
|
} else {
|
|
// 0 (timeout) or 500 (error)
|
|
var $dialog = Ox.Dialog({
|
|
buttons: [
|
|
Ox.Button({
|
|
id: 'details',
|
|
title: 'Details'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
$dialog.close(function() {
|
|
debug(request);
|
|
});
|
|
}
|
|
}),
|
|
Ox.Button({
|
|
id: 'close',
|
|
title: 'Close'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
$dialog.close();
|
|
}
|
|
})
|
|
],
|
|
content: Ox.Element()
|
|
.append(
|
|
$('<img>')
|
|
.attr({src: Ox.UI.PATH + 'png/icon128.png'})
|
|
.css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'})
|
|
)
|
|
.append(
|
|
Ox.Element()
|
|
.css({position: 'absolute', left: '96px', top: '16px', width: '256px'})
|
|
.html('Sorry, we have encountered an application error while handling your request. To help us find out what went wrong, you may want to report this error to an administrator. Otherwise, please try again later.')
|
|
),
|
|
height: 192,
|
|
keys: {enter: 'close', escape: 'close'},
|
|
title: 'Application Error',
|
|
width: 368
|
|
})
|
|
.open();
|
|
// fixme: change this to Send / Don't Send
|
|
}
|
|
pending[options.id] = false;
|
|
}
|
|
|
|
// return dfd.promise();
|
|
|
|
return options.id;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}();
|