/* classe API */ export class APIClient{ /* (1) Constructs an API client manager * * @_hostname Server hostname (without http, port number, etc) * @_baseuri Server base URI * @_ssl [OPT] Whether SSL is activated (http vs https) (default: https) * @_port [OPT] optional HTTP port (default: none) * * @return http_url Built http_url * ---------------------------------------------------------*/ constructor(_hostname, _baseuri, _ssl=true, _port=null){ /* (1) Build URL parts */ this.http_parts = { ssl: _ssl === true, hostname: _hostname, baseuri: this.fix_uri(_baseuri), port: !isNaN(_port) ? _port: null }; this.xhr_stack = []; // Ajax request stack this.buffer = null; // Last request buffer } fix_uri(_uri){ if( typeof _uri !== 'string' ) return ''; return _uri.split('/').filter((r) => r.trim().length).join('/') } /* (2) HTTP url dynamic getter * * @_uri [OPT] optional URI string * @_token [OPT] optional HTTP token * * @return http_url Built http_url * ---------------------------------------------------------*/ build_url(_uri, _token=null){ /* (1) Initialize URL buffer */ let bufurl = 'http'; /* (2) Manage @ssl */ this.http_parts.ssl && ( bufurl = bufurl.concat('s') ); bufurl = bufurl.concat('://'); /* (3) Manage token */ ( typeof _token === 'string' ) && ( bufurl = bufurl.concat(`${_token}@`) ); /* (4) Manage hostname */ bufurl = bufurl.concat(this.http_parts.hostname); /* (5) Manage port */ ( this.http_parts.port !== null ) && ( bufurl = bufurl.concat(`:${this.http_parts.port}`) ); /* (6) Base uri */ bufurl = bufurl.concat(`/${this.http_parts.baseuri}/`); /* (7) Manage URI */ bufurl = bufurl.concat( this.fix_uri(_uri) ); return bufurl; } /* Server Transaction * * @param _path target path (format "HTTP_METHOD uri/uri/uri") * @param _args formdata object (as raw object) * @param _callback Response callback * @param _token [OPT] http token * *************************************************************************************************** * * @usecase * 1. api.call( * 2. 'PUT newspaper/article/4' * 3. { content: "new content" }, * 4. (r) => alert(r.error), * 5. 'sometoken' * 6. ); * */ call(_path, _args, _callback, _token=null){ /* (1) Argument management ---------------------------------------------------------*/ /* (1) Set default callback if @callback not callable */ if( !(_callback instanceof Function) ) _callback = function(r){ console.warn('The API callback function is missing, default callback set.', 'Response', r); }; /* (2) Check @path format */ if( !/^([A-Z]+) (.+)/i.test(_path) ){ _callback({ error: -1 }); return false; } var http_method = RegExp.$1; var http_uri = RegExp.$2; /* (3) Default @_token */ if( typeof _token !== 'string' ) _token = null; /* (3) Create form data ---------------------------------------------------------*/ /* (1) Create virtual form */ var form_data = new FormData(); /* (2) Add attributes */ for( var key in _args ){ // {2.1} If a file -> send as it // if( _args[key] instanceof File ) form_data.append(key, _args[key]); // {2.2} Else -> JSON stringify // else form_data.append(key, JSON.stringify(_args[key])); } /* (4) Create XHR request ---------------------------------------------------------*/ /* (1) Clean ended requests */ for( var i = this.xhr_stack.length-1 ; i >= 0 ; i-- ){ if( this.xhr_stack[i] != null ) break; this.xhr_stack.pop(); } /* (2) Push a new entry -> fetch its index */ i = this.xhr_stack.push(null) - 1; /* (3) Create XHR object */ this.xhr_stack[i] = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttpRequest'); /* (5) Bind response event ---------------------------------------------------------*/ this.xhr_stack[i].onreadystatechange = function(i, parent){ /* (1) If request over */ if( this[i].readyState === 4 ){ /* (2) Update buffer (for debug) */ parent.buffer = this[i].responseText; /* (3) If request success */ if( [0, 200].indexOf(this[i].status) > -1 ){ /* (3.1) Create default response (if JSON error) */ var response = { error: -2 }; /* (3.2) Try to parse JSON */ try{ response = JSON.parse(this[i].responseText); }catch(e){} /* (3.3) Launch @_callback with response */ _callback(response); /* (4) If request error */ }else _callback({ error: -3 }); /* (5) Notify current xhr instance is done */ this[i] = null; } }.bind(this.xhr_stack, i, this); /* (6) Finish & send request ---------------------------------------------------------*/ /* (1) Open the XHR */ console.log(http_method, this.build_url(http_uri, _token)); this.xhr_stack[i].open(http_method, this.build_url(http_uri, _token), true); /* (2) Custom header to notify we're using Ajax */ this.xhr_stack[i].setRequestHeader('X-Requested-With', 'XMLHttpRequest'); /* (3) Make the call */ this.xhr_stack[i].send(form_data); return true; } }