UNPKG

@pi0/framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

290 lines (255 loc) 8.76 kB
import Utils from './utils'; const globals = {}; let jsonpRequests = 0; function Request(requestOptions) { const globalsNoCallbacks = Utils.extend({}, globals); ('beforeCreate beforeOpen beforeSend error complete success statusCode').split(' ').forEach((callbackName) => { delete globalsNoCallbacks[callbackName]; }); const defaults = Utils.extend({ url: window.location.toString(), method: 'GET', data: false, async: true, cache: true, user: '', password: '', headers: {}, xhrFields: {}, statusCode: {}, processData: true, dataType: 'text', contentType: 'application/x-www-form-urlencoded', timeout: 0, }, globalsNoCallbacks); const options = Utils.extend({}, defaults, requestOptions); // Function to run XHR callbacks and events function fireCallback(callbackName, ...data) { /* Callbacks: beforeCreate (xhr, options), beforeOpen (xhr, options), beforeSend (xhr, options), error (xhr, status), complete (xhr, stautus), success (response, status, xhr), statusCode () */ if (globals[callbackName]) globals[callbackName](...data); if (options[callbackName]) options[callbackName](...data); } // Before create callback fireCallback('beforeCreate', options); // For jQuery guys if (options.type) options.method = options.type; // Parameters Prefix let paramsPrefix = options.url.indexOf('?') >= 0 ? '&' : '?'; // UC method const method = options.method.toUpperCase(); // Data to modify GET URL if ((method === 'GET' || method === 'HEAD' || method === 'OPTIONS' || method === 'DELETE') && options.data) { let stringData; if (typeof options.data === 'string') { // Should be key=value string if (options.data.indexOf('?') >= 0) stringData = options.data.split('?')[1]; else stringData = options.data; } else { // Should be key=value object stringData = Utils.serializeObject(options.data); } if (stringData.length) { options.url += paramsPrefix + stringData; if (paramsPrefix === '?') paramsPrefix = '&'; } } // JSONP if (options.dataType === 'json' && options.url.indexOf('callback=') >= 0) { const callbackName = `f7jsonp_${Date.now() + ((jsonpRequests += 1))}`; let abortTimeout; const callbackSplit = options.url.split('callback='); let requestUrl = `${callbackSplit[0]}callback=${callbackName}`; if (callbackSplit[1].indexOf('&') >= 0) { const addVars = callbackSplit[1].split('&').filter(el => el.indexOf('=') > 0).join('&'); if (addVars.length > 0) requestUrl += `&${addVars}`; } // Create script let script = document.createElement('script'); script.type = 'text/javascript'; script.onerror = function onerror() { clearTimeout(abortTimeout); fireCallback('error', null, 'scripterror'); fireCallback('complete', null, 'scripterror'); }; script.src = requestUrl; // Handler window[callbackName] = function jsonpCallback(data) { clearTimeout(abortTimeout); fireCallback('success', data); script.parentNode.removeChild(script); script = null; delete window[callbackName]; }; document.querySelector('head').appendChild(script); if (options.timeout > 0) { abortTimeout = setTimeout(() => { script.parentNode.removeChild(script); script = null; fireCallback('error', null, 'timeout'); }, options.timeout); } return undefined; } // Cache for GET/HEAD requests if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS' || method === 'DELETE') { if (options.cache === false) { options.url += `${paramsPrefix}_nocache${Date.now()}`; } } // Create XHR const xhr = new XMLHttpRequest(); // Save Request URL xhr.requestUrl = options.url; xhr.requestParameters = options; // Before open callback fireCallback('beforeOpen', xhr, options); // Open XHR xhr.open(method, options.url, options.async, options.user, options.password); // Create POST Data let postData = null; if ((method === 'POST' || method === 'PUT' || method === 'PATCH') && options.data) { if (options.processData) { const postDataInstances = [ArrayBuffer, Blob, Document, FormData]; // Post Data if (postDataInstances.indexOf(options.data.constructor) >= 0) { postData = options.data; } else { // POST Headers const boundary = `---------------------------${Date.now().toString(16)}`; if (options.contentType === 'multipart/form-data') { xhr.setRequestHeader('Content-Type', `multipart/form-data; boundary=${boundary}`); } else { xhr.setRequestHeader('Content-Type', options.contentType); } postData = ''; let data = Utils.serializeObject(options.data); if (options.contentType === 'multipart/form-data') { data = data.split('&'); const newData = []; for (let i = 0; i < data.length; i += 1) { newData.push(`Content-Disposition: form-data; name="${data[i].split('=')[0]}"\r\n\r\n${data[i].split('=')[1]}\r\n`); } postData = `--${boundary}\r\n${newData.join(`--${boundary}\r\n`)}--${boundary}--\r\n`; } else { postData = data; } } } else { postData = options.data; } } // Additional headers if (options.headers) { Object.keys(options.headers).forEach((headerName) => { xhr.setRequestHeader(headerName, options[headerName]); }); } // Check for crossDomain if (typeof options.crossDomain === 'undefined') { // eslint-disable-next-line options.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(options.url) && RegExp.$2 !== window.location.host; } if (!options.crossDomain) { xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); } if (options.xhrFields) { Utils.extend(xhr, options.xhrFields); } let xhrTimeout; // Handle XHR xhr.onload = function onload() { if (xhrTimeout) clearTimeout(xhrTimeout); if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { let responseData; if (options.dataType === 'json') { try { responseData = JSON.parse(xhr.responseText); fireCallback('success', responseData, xhr.status, xhr); } catch (err) { fireCallback('error', xhr, 'parseerror'); } } else { responseData = xhr.responseType === 'text' || xhr.responseType === '' ? xhr.responseText : xhr.response; fireCallback('success', responseData, xhr.status, xhr); } } else { fireCallback('error', xhr, xhr.status); } if (options.statusCode) { if (globals.statusCode && globals.statusCode[xhr.status]) globals.statusCode[xhr.status](xhr); if (options.statusCode[xhr.status]) options.statusCode[xhr.status](xhr); } fireCallback('complete', xhr, xhr.status); }; xhr.onerror = function onerror() { if (xhrTimeout) clearTimeout(xhrTimeout); fireCallback('error', xhr, xhr.status); fireCallback('complete', xhr, 'error'); }; // Timeout if (options.timeout > 0) { xhr.onabort = function onabort() { if (xhrTimeout) clearTimeout(xhrTimeout); }; xhrTimeout = setTimeout(() => { xhr.abort(); fireCallback('error', xhr, 'timeout'); fireCallback('complete', xhr, 'timeout'); }, options.timeout); } // Ajax start callback fireCallback('beforeSend', xhr, options); // Send XHR xhr.send(postData); // Return XHR object return xhr; } function RequestShortcut(method, ...args) { let [url, data, success, error, dataType] = []; if (typeof args[1] === 'function') { [url, success, error, dataType] = args; } else { [url, data, success, error, dataType] = args; } [success, error].forEach((callback) => { if (typeof callback === 'string') { dataType = callback; if (callback === success) success = undefined; else error = undefined; } }); dataType = dataType || (method === 'json' ? 'json' : undefined); return Request({ url, method: method === 'post' ? 'POST' : 'GET', data, success, error, dataType, }); } Request.get = function get(...args) { return RequestShortcut('get', ...args); }; Request.post = function post(...args) { return RequestShortcut('post', ...args); }; Request.json = function json(...args) { return RequestShortcut('json', ...args); }; Request.setup = function setup(options) { if (options.type && !options.method) { Utils.extend(options, { method: options.type }); } Utils.extend(globals, options); }; export default Request;