var VnObject = require('./object'); var JsonException = require('./json-exception'); /** * Handler for JSON rest connections. */ module.exports = new Class({ Extends: VnObject ,_connected: false ,_requestsCount: 0 ,token: null /** * Initilizes the connection object. */ ,initialize: function() { VnObject.prototype.initialize.call(this); this.fetchToken(); } ,fetchToken: function() { var token = null; if (sessionStorage.getItem('vnToken')) token = sessionStorage.getItem('vnToken'); if (localStorage.getItem('vnToken')) token = localStorage.getItem('vnToken'); this.token = token; } ,clearToken: function() { this.token = null; localStorage.removeItem('vnToken'); sessionStorage.removeItem('vnToken'); } /** * Opens the connection to the REST service. * * @param {String} user The user name * @param {String} password The user password * @param {Boolean} remember Specifies if the user should be remembered * @param {Function} openCallback The function to call when operation is done */ ,open: function(user, pass, remember, callback) { if (user !== null && user !== undefined) { var params = { user: user ,password: pass ,remember: remember }; } else var params = null; this.lbSend('POST', 'Accounts/login', params, this._onOpen.bind(this, callback, remember)); } /* * Called when open operation is done. */ ,_onOpen: function(callback, remember, json, error) { if (json) { this._connected = true; this.token = json.token; var storage = remember ? localStorage : sessionStorage; storage.setItem('vnToken', this.token); this.emit('openned'); } else this._closeClient(); if (callback) callback(this, this._connected, error); } /** * Closes the connection to the REST service. * * @param {Function} callback The function to call when operation is done */ ,close: function(callback) { this.lbSend('POST', 'Accounts/logout', null, this._onClose.bind(this, callback)); this._closeClient(); } /* * Called when close operation is done. */ ,_onClose: function(callback, json, error) { this.emit('closed'); if (callback) callback(this, null, error); } ,_closeClient: function() { this._connected = false; this.clearToken(); } /** * Supplants another user. * * @param {String} user The user name * @param {Function} callback The callback function */ ,supplantUser: function(user, callback) { var params = {supplantUser: user}; this.send('client/supplant', params, this._onUserSupplant.bind(this, callback)); } ,_onUserSupplant: function(callback, json, err) { if (json) this.token = json; if (callback) callback(err == null, err); } /** * Ends the user supplanting and restores the last login. */ ,supplantEnd: function() { this.lbSend('POST', 'Accounts/logout'); this.fetchToken(); } /** * Executes the specified REST service with the given params and calls * the callback when response is received. * * @param {String} restService The service path * @param {Map} params The params to pass to the service * @param {Function} callback The response callback */ ,send: function(restService, params, callback) { if (!params) params = {}; params.srv = 'json:'+ restService; this.sendWithUrl(params, callback, 'POST', '.'); } ,sendForm: function(form, callback) { var params = {}; var elements = form.elements; for (var i = 0; i < elements.length; i++) if (elements[i].name) params[elements[i].name] = elements[i].value; this.sendWithUrl(params, callback, 'POST', form.action); } ,sendFormMultipart: function(form, callback) { var formData = new FormData(form); var request = new XMLHttpRequest(); request.open('POST', form.action, true); if (this.token) request.setRequestHeader('Authorization', this.token); request.onreadystatechange = this._onStateChange.bind(this, request, callback); request.send(formData); this._addRequest(); } ,sendFormData: function(formData, callback) { var request = new XMLHttpRequest(); request.open('POST', '', true); if (this.token) request.setRequestHeader('Authorization', this.token); request.onreadystatechange = this._onStateChange.bind(this, request, callback); request.send(formData); this._addRequest(); } /* * Called when REST response is received. */ ,sendWithUrl: function(params, callback, method, url) { var request = new XMLHttpRequest(); request.open(method, url, true); request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); if (this.token) request.setRequestHeader('Authorization', this.token); request.onreadystatechange = this._onStateChange.bind(this, request, callback); request.send(Vn.Url.makeUri(params)); this._addRequest(); } ,lbSend: function(method, url, params, callback) { var request = new XMLHttpRequest(); request.open(method, `api/${url}`, true); request.setRequestHeader('Content-Type', 'application/json;charset=utf-8'); if (this.token) request.setRequestHeader('Authorization', this.token); request.onreadystatechange = this._onStateChange.bind(this, request, callback); request.send(params && JSON.stringify(params)); this._addRequest(); } ,_addRequest: function() { this._requestsCount++; if (this._requestsCount === 1) this.emit('loading-changed', true); } ,_onStateChange: function(request, callback) { if (request.readyState !== 4) return; this._requestsCount--; if (this._requestsCount === 0) this.emit('loading-changed', false); var data = null; var error = null; try { if (request.status == 0) { var ex = new JsonException(); ex.message = _('The server does not respond, please check your Internet connection'); throw ex; } var contentType = null; try { contentType = request .getResponseHeader('Content-Type') .split(';')[0] .trim(); } catch (err) { console.warn(err); } if (contentType != 'application/json') { var ex = new JsonException(); ex.message = request.statusText; ex.code = request.status; throw ex; } var json; var jsData; if (request.responseText) json = JSON.parse(request.responseText); if (json) jsData = json.data || json; if (request.status >= 200 && request.status < 300) { data = jsData; } else { var exception = jsData.exception; var error = jsData.error; if (exception) { exception = exception .replace(/\\/g, '.') .replace(/Exception$/, '') .replace(/^Vn\.Web\./, ''); var ex = new JsonException(); ex.exception = exception; ex.message = jsData.message; ex.code = jsData.code; ex.file = jsData.file; ex.line = jsData.line; ex.trace = jsData.trace; } else if (error) { var ex = new Error(); ex.name = error.name; ex.message = error.message; ex.code = error.code; ex.statusCode = request.status; } throw ex; } } catch (e) { data = null; error = e; } if (callback) try { callback(data, error); error = null; } catch (e) { error = e; } if (error) { if (error.exception == 'SessionExpired') this.clearToken(); this.emit('error', error); } } });