UNPKG

mojax

Version:

extendable ajax request module

337 lines (288 loc) 11.1 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ajax = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ "use strict"; var xhrFactory = require("./lib/xhrFactory"), chainAddons = require("./lib/chainAddons"), finalizeParams = require("./lib/finalizeParams"); /** * @summary creates a mojax instance, which can be extended with middleware/addons, and from which HTTP requests can be dispatched * @returns {requester} */ function createRequester() { var addons = []; /** * @typedef {object} requester * @summary environment/interface used to add middleware and make HTTP requests */ return { /** * @function * @name requester#use * * @summary adds a middleware function to the requester pipeline * @param {function} fn - middleware function to add to the requester's request pipeline * @returns {object} this */ use: function(fn) { if (typeof fn === "function") { addons.push(fn); } else { throw new TypeError("ajax.use() expects a function argument. Provided:\n" + fn + " (" + typeof fn + ")"); } return this; }, /** * @function * @name requester#req * * @summary starts an async HTTP request, passing the configuration object through the middleware pipeline, and finally sending the request * @param {object} params - configuration object used for the request * @returns {object} this */ req: function(params) { xhrFactory( chainAddons(addons.concat(finalizeParams), params) ); return this; } }; } module.exports = { createRequester: createRequester }; },{"./lib/chainAddons":2,"./lib/finalizeParams":3,"./lib/xhrFactory":7}],2:[function(require,module,exports){ module.exports = function chainAddons(addons, params) { "use strict"; return addons.reduce(function(config, nextAddon) { if (Object.prototype.toString.call(config) === "[object Object]") { return nextAddon(config); } }, params); }; },{}],3:[function(require,module,exports){ var mapCallbacks = require("./mapCallbacks"); /** * @summary enforces required properties of the request configuration object. * @param {object} c - request configuration object */ function finalizeParams(c) { "use strict"; if (Object.prototype.toString.call(c) !== "[object Object]") { throw new TypeError("ajax request expects an object argument. Provided\n" + c + " (" + typeof c + ")"); } // url must be string if (typeof c.url !== "string") { throw new TypeError("ajax request object expects prop 'url' to be a string. Provided\n" + c.url + " (" + typeof c.url + ")"); } // timeout can be set if (c.timeout && typeof c.timeout !== "number") { throw new TypeError("ajax request object timeout property must be an integer. Provided\n" + c.timeout + " (" + typeof c.timeout + ")"); } // request method must be uppercase string === GET | POST | PUT | DELETE if (typeof c.method === "string") { // enforce proper case c.method = c.method.toUpperCase(); if (["GET","POST","PUT","DELETE"].indexOf(c.method) === -1) { throw new RangeError("ajax: 'method' must have one of the following values:\nGET\nPOST\nPUT\nDELETE\nProvided: " + c.method); } } else { throw new RangeError("ajax request object expects property 'method' to be one of:\n GET, POST, PUT, DELETE\n Provided:\n" + c.method); } // optional if (c.responseType) { // enforce proper case c.responseType = c.responseType.toLowerCase(); if (["json","text","xml","text/xml","text/html","arraybuffer","blob"].indexOf(c.responseType) === -1) { throw new RangeError("ajax: responseType must have one of the following values:\narraybuffer\nblob\njson\ntext\nxml\ntext/xml\ntext/html\n\nProvided: " + c.responseType); } } else { c.responseType = "text"; } // force mandatory X-Requested-With header if (c.headers) { if (Object.prototype.toString.call(c.headers) !== "[object Object]") { throw new TypeError("ajax request object expects prop 'headers' to be an object of key:value pairing for header:value\nProvided:\n" + c.headers); } else { Object.keys(c.headers).forEach(function(key) { if (typeof c.headers[key] !== "string") { throw new TypeError("ajax request object 'headers' properties must all be strings.\nProvided " + typeof c.headers[key] + " for " + key); } }); if (!c.headers["X-Requested-With"]) { c.headers["X-Requested-With"] = "XMLHttpRequest"; } } } else { c.headers = { "X-Requested-With": "XMLHttpRequest" }; } if (c.method === "POST" || c.method === "PUT") { if (typeof c.data !== "string") { throw new TypeError("ajax request object prop 'data' must be present on POST or PUT requests and must be a string. Provided\n" + c.data + " (" + typeof c.data + ")"); } } ["onOpen", "onHeaders", "onSuccess", "onFailure"].forEach(function(key) { c[key] = mapCallbacks(c[key]); }); return c; } module.exports = finalizeParams; },{"./mapCallbacks":4}],4:[function(require,module,exports){ /** * Maps a function to an array of functions, otherwise returns an empty array. * @param {function|function[]|undefined|null} v - initial value to map to an array * @returns {function[]|array} - array of functions or empty array */ module.exports = function mapCallbacks(v) { "use strict"; if (typeof v === "function") { return [v]; } if (Array.isArray(v)) { v.forEach(function(fn) { if (typeof fn !== "function") { throw new TypeError("expected function or array of functions for callbacks. Provided\n" + fn + " (" + typeof fn + ")"); } }); return v; } if (v === null || v === undefined) { return []; } else { throw new TypeError("expected function or array of functions for callbacks. Provided\n" + v + " (" + typeof v + ")"); } }; },{}],5:[function(require,module,exports){ /** * @author jQuery * @copyright jQuery foundation * @license https://github.com/jquery/jquery/blob/master/LICENSE.txt * * Parses XML string content * @param {string} data - content * @returns {xml} */ function parseXML(data) { "use strict"; var xml; // Support: IE9 try { xml = (new window.DOMParser()).parseFromString(data, "text/xml"); } catch (e) { xml = undefined; } if (!xml || xml.getElementsByTagName("parsererror").length) { throw new SyntaxError(""); } return xml; } /** * @memberof ajax * @summary parses the response data based on provided responseType * @private * @param xhr {object} XMLHttpRequest object * @returns {object|string} */ module.exports = function parseResponse(xhr) { "use strict"; // only try to parse if not already parsed by xhr if (typeof xhr.response === "string" && xhr.response !== "") { if (xhr.responseType === "json") { return JSON.parse(xhr.response); } else if (xhr.type === "text/xml" || xhr.type === "xml") { return parseXML(xhr.response); } } return xhr.response; }; },{}],6:[function(require,module,exports){ var parseResponse = require("./parseResponse"); /** * @summary triggers existing listeners based on state of XMLHttpRequest. * @param xhr * @param {object} c - configuration object */ module.exports = function(xhr, c) { "use strict"; var resp; switch (xhr.readyState) { case 1: { if (c.onOpen.length) { c.onOpen.forEach(function(cb) { cb(xhr); }); } break; } case 2: { if (c.onHeaders.length) { c.onHeaders.forEach(function(cb) { cb(xhr); }); } break; } case 4: { // dispatch to callbacks if (c.onSuccess.length && /2|3/.test(xhr.status.toString().charAt(0))) { try { resp = parseResponse(xhr.response, c.responseType); } catch (e) { if (e instanceof SyntaxError) { throw new SyntaxError("unable to parse response of type: " + c.responseType + " for\n" + xhr.response); } else { throw Error(e); } } // SUCCESS c.onSuccess.forEach(function(cb) { cb(xhr, resp); }); } else if (c.onFailure.length && /4|5/.test(xhr.status.toString().charAt(0))) { // FAILURE c.onFailure.forEach(function(cb) { cb(xhr); }); } break; } default: { break; } } }; },{"./parseResponse":5}],7:[function(require,module,exports){ var responseListener = require("./responseListener"); /** * @memberof ajax * @summary executes the XMLHttpRequest * @private * @param c {object} - request configuration hash */ module.exports = function xhrFactory(c) { "use strict"; var xhr = new XMLHttpRequest(); // early abort, if config invalid if (Object.prototype.toString.call(c) !== "[object Object]") { return; } // bind listeners xhr.onreadystatechange = function() { responseListener(xhr, c); }; // start XMLHttpRequest xhr.open(c.method, c.url, true); // set request headers // must be done after open() Object.keys(c.headers || {}).forEach(function(h) { xhr.setRequestHeader(h, c.headers[h]); }); // handle timeout if (c.timeout) { xhr.timeout = c.timeout; } xhr.send(c.data); return xhr; }; },{"./responseListener":6}]},{},[1])(1) });