'use strict'; /*@ Ox.Request Basic request controller # FIXME: options is not a property, just documenting defaults # options Options object # timeout request timeout # type request type, possible values POST, GET, PUT, DELETE # url request url @*/ Ox.Request = (function() { var cache = {}, $element, pending = {}, requests = {}, self = { options: { timeout: 60000, type: 'POST', url: '/api/' } }; return { /*@ cancel cancel pending requests () -> cancel all requests (fn) -> cancel all requests where function returns true (id) -> 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]]; } $element && $element.triggerEvent('request', { requests: Ox.len(requests) }); }, /*@ clearCache clear cached results () -> ... @*/ clearCache: function(query) { if (!query) { cache = {}; } else { cache = Ox.filter(cache, function(val, key) { return key.indexOf(query) == -1; }); } }, /*@ bindEvent Unbind event @*/ bindEvent: function() { if (!$element) { $element = Ox.Element(); } $element.bindEvent.apply(this, arguments); }, /*@ options get/set options () -> get options (options) -> set options options Options Object @*/ options: function() { return Ox.getset(self.options, arguments, function() {}, this); }, /*@ requests pending requests () -> returns number of requests @*/ requests: function() { return Ox.len(requests); }, /*@ send send request (options) -> returns request id options Options Object age cache age id request id timeout overwrite default timeout type overwrite default type url 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 )) { var data = cache[req].data; setTimeout(function() { callback && callback(data); }, 0); } else { pending[options.id] = true; $.ajax({ complete: complete, data: options.data, //dataType: 'json', timeout: options.timeout, type: options.type, url: options.url }); } $element && $element.triggerEvent('request', { requests: Ox.len(requests) }); } function callback(data) { if (requests[options.id]) { delete requests[options.id]; options.callback && options.callback(data); $element && $element.triggerEvent('request', { requests: Ox.len(requests) }); } } function complete(request) { var $dialog, data; try { data = JSON.parse(request.responseText); } catch (error) { try { data = { status: { code: request.status, text: request.statusText } }; } catch (error) { data = { status: { code: '500', text: 'Unknown Error' } }; } } if (Ox.contains([200, 404, 409], data.status.code)) { // we have to include not found and conflict // so that handlers can handle these cases cache[req] = { data: data, time: Ox.getTime() }; callback(data); } else { $element && $element.triggerEvent('error', data); } pending[options.id] = false; } return options.id; }, /*@ unbindEvent Unbind event @*/ // FIXME: Can this be removed? unbindEvent: function() { $element && $element.unbindEvent.apply(this, arguments); } }; }());