Skip to content

Instantly share code, notes, and snippets.

@hasenj
Created April 13, 2013 14:37
Show Gist options
  • Select an option

  • Save hasenj/5378660 to your computer and use it in GitHub Desktop.

Select an option

Save hasenj/5378660 to your computer and use it in GitHub Desktop.

Revisions

  1. hasenj created this gist Apr 13, 2013.
    189 changes: 189 additions & 0 deletions requests.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,189 @@
    echo = function(areq) { console.log(areq.response) };
    requests = (function() {
    var module = {};

    var AsyncRequest = XMLHttpRequest;

    // takes a dict and returns a string "a=b&c=d"
    var toUrlParams = function(dict) {
    var res = [];
    for(var key in dict) {
    var value = encodeURIComponent(dict[key])
    res.push(key + "=" + value);
    }
    return res.join("&");
    }

    // takes a path and a params dict and returns a string "/path/?param1=val1&param2=val2"
    var makeUrl = function(path, params) {
    var params = toUrlParams(params);
    if(params) {
    return path + "?" + params;
    } else {
    return path;
    }
    }

    // get the hostname from a url/path the same way that the hostname is extracted from the location in window.href.hostname
    // from: http://stackoverflow.com/a/8498668/35364
    var pathHost = function(url) {
    var a = document.createElement('a');
    a.href = url;
    return a.hostname;
    }

    // call all the callbacks/hooks without failing
    var call = function(callback_array, this_arg, args) {
    for(var index = 0; index < callback_array.length; index++) {
    try {
    // call the callback using fn.apply
    callback_array[index].apply(this_arg, args);
    } catch(e) {
    // pass
    console.log("callback error");
    }
    }
    }

    var before_hooks = [];
    module.before_send = function(fn) {
    before_hooks.push(fn);
    return module; // allow chaining;
    }

    // constructor
    var JsonRequest = function(pmethod, ppath) {
    var self = this;
    var method = pmethod.toUpperCase();
    var path = ppath;
    var data = {};
    var params = {};
    var headers = [];
    var success_hooks = [];
    var error_hooks = [];
    var complete_hooks = [];

    // this one is a getter only!
    self.path = function() {
    return path;
    }
    self.method = function() {
    return method;
    }

    // the rest are only setters
    self.header = function(key, value) {
    var h = {key: key, value: value};
    headers.push(h);
    return self;
    }

    self.data = function(pdata) {
    data = pdata;
    return self;
    }

    self.params = function(pparams) {
    params = pparams;
    return self;
    }

    self.success = function(fn) {
    success_hooks.push(fn);
    return self;
    }

    self.error = function(fn) {
    error_hooks.push(fn);
    return self;
    }

    self.complete = function(fn) {
    complete_hooks.push(fn);
    return self;
    }

    self.send = function() {
    // call before_send hooks
    // we must call it here before we start building the request and setting headers, etc
    // because these hooks are meant for addings headers, etc
    call(before_hooks, self, [self]);

    var areq = new AsyncRequest();
    var url = makeUrl(path, params);
    areq.open(method, url);

    // force json
    areq.setRequestHeader("Content-Type", "application/json");
    // set the ajax header
    areq.setRequestHeader("X-Requested-With", "XMLHttpRequest");

    // set headers
    // Traverse the headers list by the order of insertion
    // Note: Do this after forcing the json header so that users can override it
    for(var i = 0; i < headers.length; i++) {
    var header = headers[i];
    areq.setRequestHeader(header.key, header.value)
    }

    // TODO: have option to send datatypes other than json?
    var json_data = JSON.stringify(data);
    areq.send(json_data);

    areq.onreadystatechange = function() {
    if(areq.readyState == areq.DONE) {
    call(complete_hooks, areq, [areq]);
    if(areq.status == 200) {
    call(success_hooks, areq, [areq]);
    } else {
    call(error_hooks, areq, [areq]);
    }
    }
    }

    return self;
    }

    // utilities/helpers

    /// Check if the path is going to a different domain
    self.isCrossOrigin = function() {
    if(path.charAt(0) === "/" && path.charAt(1) !== "/") {
    return false;
    }
    // path has a domain; check if it's different from current domain
    if(pathHost(path) == window.location.hostname) {
    return false;
    }

    // has a host but not the same as this one; so it's cross-domain
    return true;
    }

    self.isSameOrigin = function() {
    return !self.isCrossOrigin();
    }
    }

    module.request = function(method, path) {
    return new JsonRequest(method, path);
    }

    module.get = function(path) {
    return module.request("get", path);
    }

    module.post = function(path) {
    return module.request("post", path);
    }

    module.put = function(path) {
    return module.request("put", path);
    }

    module.del = function(path) {
    return module.request("delete", path);
    }

    return module;
    }());